Mysql实例分析一个MySQL的异常查询的案例

Mysql实例分析一个MySQL的异常查询的案例,第1张

概述介绍《Mysql实例分析一个MySQL的异常查询的案例》开发教程,希望对您有用。

《MysqL实例分析一个MysqL的异常查询的案例》要点:@H_502_3@本文介绍了MysqL实例分析一个MysqL的异常查询的案例,希望对您有用。如果有疑问,可以联系我们。

问题MysqL教程

用户工单疑问:相同的语句,只是最后的limit行数不同.奇怪的是,limit 10 的性能比limit 100的语句还慢约10倍.MysqL教程

隐藏用户表信息,语句及结果如下
MysqL教程

SELECT f1,SUM(`f2`) `CNT` FROM T WHERE f1 IS NOT NulL AND f3 = '2014-05-12' GROUP BY f1 ORDER BY `CNT` DESC liMIT 10;

执行时间3 min 3.65 secMysqL教程

SELECT f1,SUM(`f2`) `CNT` FROM T WHERE f1 IS NOT NulL AND f3 = '2014-05-12' GROUP BY f1 ORDER BY `CNT` DESC liMIT 100;

执行时间1.24Sec.MysqL教程

性能差距非常大!MysqL教程

分析
MysqL Tips:追查语句执行时最常用的方法,是通过explain来看语句的执行计划. ?MysqL教程

更有冲击性的效果是通过缩小范围后,在这个数据下,limit 67和limit 68的执行计划相差很大.MysqL教程

两个执行计划:
MysqL教程

liMIT 67ID: 1select_type: SIMPLE@R_502_5991@: atype: rangepossible_keys: A,B,Ckey: Bkey_len: 387ref: NulLrows: 2555192Extra: Using where; Using temporary; Using filesort1 row in set (0.00 sec)liMIT 68ID: 1select_type: SIMPLE@R_502_5991@: atype: refpossible_keys: A,Ckey: Akey_len: 3ref: constrows: 67586Extra: Using where; Using temporary; Using filesort1 row in set (0.00 sec)

可以看到,两个语句的执行计划不同:使用的索引不同.MysqL教程

MysqL Tips:explain的结果中,key表示最终使用的索引,rows表示使用这个索引需要扫描的行数,这是个估计值.MysqL教程

表中 索引A定义为 (f3,f4,f1,f2,f5); 索引B定义为(f1,f3);MysqL教程

一个确认MysqL教程

虽然rows是估计值,但是指导索引使用的依据.既然limit 68能达到rows 67586,说明在第一个语句优化器可选结果中,也应该有此值,为什么不会选择索引A?
先确认一下我们上面的这个结论.MysqL教程

MysqL Tips:MysqL语法中能够用force index 来强行要求优化器使用某一个索引.MysqL教程

Explain SELECT f1,SUM(f2) CNT FROM t force index(A) WHERE f1 IS NOT NulL AND f3 = ‘2014-05-12' GROUP BY P ORDER BY CNT DESC liMIT 67\GID: 1select_type: SIMPLE@R_502_5991@: atype: refpossible_keys:Akey: Akey_len: 3ref: constrows: 67586Extra: Using where; Using temporary; Using filesort1 row in set (0.00 sec)

顺便说明,由于我们指定了force index,因此优化器不会考虑其他索引,possible_keys里只会显示A.我们关注的是rows:67586.这说明在limit 67语句里,使用索引A也能够减少行扫描.MysqL教程

MysqL Tips:MysqL优化器会对possiable_key中的每个可能索引都计算查询代价,选择最小代价的查询计划.MysqL教程

至此我们大概可以猜测,这个应该是MysqL实现上的BUG:没有选择合适的索引,导致使用了明显错误的执行计划.MysqL教程

MysqL Tips:MysqL的优化器执行期间需要依赖于表的统计信息,而统计信息是估算值,因此有可能导致得到的执行计划非最优.MysqL教程

但要说明的是,上述Tip是客观情况造成(可接受),但本例却是例外,因此优化器实际上可以拿到能够作出选择正确结果的数据(rows值),但是最终选择错误.MysqL教程

原因分析MysqL教程

MysqL优化器是按照查询代价的估算值,来确定要使用的索引.计算这个估算值的过程,基本是按照“估计需要扫描的行数”来确定的.MysqL教程

MysqL Tips:MysqL在目前集团主流使用的5.1和5.5版本中只能使用前缀索引.MysqL教程

因此,使用索引A只能用上字段f3,使用索引B只能用上字段f1.Rows即为使用了索引查到上下界,之后需要扫描的数据行数(估算值).MysqL教程

上述的语句需要用到group和order by,因此执行计划中都有Using temporary; Using filesort.
流程上按顺序先计算使用索引A的查询代价.MysqL教程

之后依次计算其他possitabe_key的查询代价.由于过程中需要排序,在得到一个暂定结果后,需要判断是否有代价更低的排序方式(test_if_cheaper_ordering).
与之前的大同小异,也是依靠估计扫描行数来计算代价.MysqL教程

在这个逻辑的实现过程中,存在一个BUG:在估计当前索引的区分度的时候,没有考虑到前缀索引.MysqL教程

即:假设表中有50w行数据,索引B(f1,f3),则计算索引区分度时,需要根据能够用上的前缀部分来确定.比如f1有1000个不同的值,则平均每个key值上的记录数为500.如(f1,f2)有10000个同的值,则平均每个组合key上的记录数为50,若(f1,f3)有50w个不同的值,则平均每个组合key上的记录数为1.MysqL教程

MysqL Tips:每个key上的记录数越少,说明使用该索引查询时效率最高.对应于show index from tbl 输出结果中的Cardinality值越大.MysqL教程

在这个case下,索引B只能使用f1做前缀索引,但是在计算单key上的行平均值时用的是(f1,这就导致估算用索引B估算的时候,得到的代价偏小.导致误选.MysqL教程

回到问题本身MysqL教程

1、 为什么limit值大的时候反而选对了呢?
这是因为在计算B的查询代价时,查询需要返回的行数limit_rows也参与乘积,若limit值较大,则计算出来的B的代价就会更大,反而会由于代价.值超过A,而导致优化器最终选择A.MysqL教程

2、 这个表有50w行数就,为什么limit相差为就差别这么大?
这与语句本身有关.这个语句中有group by,这就意味着每多limit一个值,实际上需要扫描更多的行N. 这里N为“表的总行数”/“表中不同的f2值”.
也就是说这个语句使得这个BUG有放大作用.MysqL教程

解决方案MysqL教程

分析清楚后解决方法就比较简单了,修改代码逻辑,在执行test_if_cheaper_ordering过程中,改用字段f1的区分度来计算即可.
MysqL教程

总结

以上是内存溢出为你收集整理的Mysql实例分析一个MySQL的异常查询的案例全部内容,希望文章能够帮你解决Mysql实例分析一个MySQL的异常查询的案例所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/sjk/1163415.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-01
下一篇 2022-06-01

发表评论

登录后才能评论

评论列表(0条)

保存