优化设计案例(优化问题的案例及分析)

lxf2023-12-01 23:20:01

bitscn.com 一个MySQL文件排序的优化案例

MySQL中的文件排序是什么意思?官方手册定义:

MySQL必须进行额外的传递,以找出如何按排序顺序检索行。排序是通过根据连接类型遍历所有行,并为匹配WHERE子句的所有行存储排序键和指向该行的指针来完成的。然后对键进行排序,并按排序顺序检索行。

一般来说,如果您的SQL查询语句中有order by,并且没有合适的索引,那么您可以通过EXPLAIN query在额外的列中看到单词Using filesort。当然,一般意味着这时候你需要对其进行优化,无论是通过优化索引,还是改变SQL查询的实现方式

先看表结构信息:

[sql]创建表` TB xxxx `( ` id ` int(10)unsigned NOT NULL auto _ increment COMMENT & # 39;??id & # 39,` a ' varchar(20)默认空注释& # 39;??id & # 39,` base62_id` varchar(10)默认空注释& # 39;??base62 _ id & # 39,` userid ' varchar(20)默认空注释& # 39;??id & # 39,` category` int(5)无符号缺省空注释& # 39;????id & # 39,` rate ' decimal(10,2) NOT NULL默认值& # 39;0.00'评论& # 39;??',` status '枚举(& # 39;Y & # 39,'N & # 39)NOT NULL默认& # 39;Y & # 39评论& # 39;??',` releaseTime` datetime默认空注释& # 39;??????',` createTime` datetime默认空注释& # 39;??????',` content` text,PRIMARY KEY (`id `),UNIQUE KEY `a` (`a `),KEY `releaseTime` (`releaseTime `),KEY ` rate `( ` category `, ` rate `, ` release time `),KEY ` idx _ c _ r _ rate `( ` category `, ` release time `, ` rate `),KEY ` idx _ status _ rt _ rate `( ` status `,` release `),` rate `),KEY ` idx _ status _ rate _ release `( ` status?????'集合中的1行(0.15秒)

Ps:上表结构中有些指标实际上网上并不存在,是为了测试而临时添加的。

那么业务有如下查询:

[sql] SELECT a,tbxxxxx中的内容,其中` status` = & # 39Y & # 39而releaseTime > & # 392013-07-08 11:00:00'按费率排序DESC限制0,10

对于这种类型的查询,第一反应可能是构建一个(status,releaseTime,rate)复合索引,然后通过EXPLAIN发现优化器选择了同样的方式:

[html]解释选择a,内容来自tbxxxxx其中` status` = & # 39Y & # 39而releaseTime > & # 392013-07-08 11:00:00'按利率排序DESC限制0,10;+--+-+-+-+-+-+-+-+--+| id | select _ type | table | type | possible _ keys | key | key _ len | ref | rows | Extra |+-+-+-+-+-+-+-+--+| 1 | SIMPLE | TB xxxxx | ref | release time,idx _ status _ rt _ rate _ release | idx _ status _ rt _ rate | 1 | const | 531837 | Using where;使用文件排序| + - + - + - + - + - + - + - + - +集合中的1行(0.15秒)

上面key_len=1可以理解,因为MySQL 5.6之前没有ICP,所以releaseTime的范围查询不能使用索引。状态占用一个字节。但是从Extra中,我们可以发现按速率排序导致文件排序。那么如何调整这个索引来避免文件排序呢?其实一开始我也没指望优化这个文件排序的开销(结果发现这种情况下的文件排序占了99%的开销),但是我想了想怎么优化扫描的行数,让过滤的行数更少,可以减少返回表带来的开销。但打开侧写后,我发现了问题最关键的部分:

上面的图标显示,对结果集进行排序占用了大部分时间。那么这个时候问题就变成了如何优化这个可恶的文件排序,于是就增加了一个(status,rate,releaseTime)字段的索引。测试表明,优化器在默认情况下仍然采用复合索引(status、releaseTime、rate ),因此只能通过使用use index()的语法来强制使用目标索引:

[sql]解释SELECT a,内容来自tbxxxxx使用索引(idx_status_rate_release)其中` status` = & # 39Y & # 39而releaseTime > & # 392013-07-08 11:00:00'按利率排序DESC限制0,10;+-+-+-+-+-+-+-+-+-+| id | select _ type | table | type | possible _ keys | key | key _ len | ref | rows | Extra |+-+-+-+-+-+-+-+-+-+| 1 | SIMPLE | TB XXXXXX | ref | idx _ status _ rate _ release | idx _ status _ rate _ release | 1 | const | 1057094 |使用where | + - + - + - + - + - + - + - +集合中的1行(0.00秒)

从执行计划中可以看出,此时没有filesort阶段,因为默认的索引前缀状态和rate是按照rate排序的,所以最终的order by rate效果可以通过使用索引的有序数据来实现。细心的朋友可能会发现,这次执行计划扫描的行数估计是上次执行计划的两倍,所以扫描这么多行并不贵(这也是为什么优化这个SQL的第一反应是考虑优化索引来减少扫描的行数)。事实上,证明这个开销与filesort相比是很小的,有两点可以证明:profile分析显示99%的时间都花在了排序结果上,其次,从库在线测试第二个执行计划的查询时间约为0.1s,而第一个执行计划的查询时间约为3s。虽然总的来说不推荐使用索引语法,后期也有很大的不确定性,但是既然它有自己的理由提供这个语法,用好它也有自己的优势,就这么做吧。另外这里需要注意的是:(status,rate,releaseTime)这个索引中只需要rate,status只有两个值,所以过滤效果不明显,不需要releaseTime(可能在ICP之后使用)。

最后有两个小感悟:1。MySQL的优化器还是有点欺骗性,有时候不靠谱。2.有时候SQL的优化不能完全直观。用普通的经验,命令概要可以完整的统计出一条SQL查询执行过程中各个阶段的开销,这样我们就可以对一条SQL有优化的关键点,这样就可以做到不盲目,从而高效快速的优化。

bitsCN.com adminjs.cn是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 Adminjs.cn还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 在Adminjs.cn中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!