问题
线上碰到的问题:相同的语句,只是最后的limit行数不同。奇怪的是,limit 10 的性能比limit 100的语句还慢约10倍。
隐藏用户表信息,语句及结果如下
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 sec
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 Tips:追查语句执行时最常用的方法,是通过explain来看语句的执行计划。
更有冲击性的效果是通过缩小范围后,在这个数据下,limit 67和limit 68的执行计划相差很大。
两个执行计划:
LIMIT 67
id: 1
select_type: SIMPLE
table: a
type: range
possible_keys: A,B,C
key: B
key_len: 387
ref: NULL
rows: 2555192
Extra: Using where; Using temporary; Using filesort
1 row in set (0.00 sec)
LIMIT 68
id: 1
select_type: SIMPLE
table: a
type: ref
possible_keys: A,B,C
key: A
key_len: 3
ref: const
rows: 67586
Extra: Using where; Using temporary; Using filesort
1 row in set (0.00 sec)
可以看到,两个语句的执行计划不同:使用的索引不同。
MySQL Tips:explain的结果中,key表示最终使用的索引,rows表示使用这个索引需要扫描的行数,这是个估计值。
表中 索引A定义为 (f3, f4, f1, f2, f5); 索引B定义为(f1, f2, f3);
一个确认
虽然rows是估计值,但是指导索引使用的依据。既然limit 68能达到rows 67586, 说明在第一个语句优化器可选结果中,也应该有此值,为什么不会选择索引A?
先确认一下我们上面的这个结论。
MySQL Tips:MySQL语法中能够用force index 来强行要求优化器使用某一个索引。
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\G
id: 1
select_type: SIMPLE
table: a
type: ref
possible_keys:A
key: A
key_len: 3
ref: const
rows: 67586
Extra: Using where; Using temporary; Using filesort
1 row in set (0.00 sec)
顺便说明,由于我们指定了force index,因此优化器不会考虑其他索引,possible_keys里只会显示A。
我们关注的是rows:67586。这说明在limit 67语句里,使用索引A也能够减少行扫描。
MySQL Tips:MySQL优化器会对possiable_key中的每个可能索引都计算查询代价,选择最小代价的查询计划。
至此我们大概可以猜测,这个应该是MySQL实现上的bug:没有选择合适的索引,导致使用了明显错误的执行计划。
MySQL Tips:MySQL的优化器执行期间需要依赖于表的统计信息,而统计信息是估算值,因此有可能导致得到的执行计划非最优。
但要说明的是,上述Tip是客观情况造成(可接受),但本例却是例外,因此优化器实际上可以拿到能够作出选择正确结果的数据(rows值),但是最终选择错误。
原因分析
MySQL优化器是按照查询代价的估算值,来确定要使用的索引。计算这个估算值的过程,基本是按照“估计需要扫描的行数”来确定的。
MySQL Tips:MySQL在目前集团主流使用的5.1和5.5版本中只能使用前缀索引。
因此,使用索引A只能用上字段f3,使用索引B只能用上字段f1。Rows即为使用了索引查到上下界,之后需要扫描的数据行数(估算值)。
上述的语句需要用到group和order by,因此执行计划中都有Using temporary; Using filesort。
流程上按顺序先计算使用索引A的查询代价。
之后依次计算其他possitabe_key的查询代价。由于过程中需要排序,在得到一个暂定结果后,需要判断是否有代价更低的排序方式(test_if_cheaper_ordering)。
与之前的大同小异,也是依靠估计扫描行数来计算代价。
在这个逻辑的实现过程中,存在一个bug:在估计当前索引的区分度的时候,没有考虑到前缀索引。
即:假设表中有50w行数据,索引B(f1,f2,f3),则计算索引区分度时,需要根据能够用上的前缀部分来确定。比如f1有1000个不同的值,则平均每个key值上的记录数为500.如(f1,f2)有10000个同的值,则平均每个组合key上的记录数为50,若(f1,f2,f3)有50w个不同的值,则平均每个组合key上的记录数为1。
MySQL Tips:每个key上的记录数越少,说明使用该索引查询时效率最高。对应于show index from tbl 输出结果中的Cardinality值越大。
在这个case下,索引A只能使用f1做前缀索引,但是在计算单key上的行平均值时用的是(f1,f2,f3),这就导致估算用索引B估算的时候,得到的代价偏小。导致误选。
回到问题本身
1、 为什么limit值大的时候反而选对了呢?
这是因为在计算B的查询代价时,查询需要返回的行数limit_rows也参与乘积,若limit值较大,则计算出来的B的代价就会更大,反而会由于代价。值超过A,而导致优化器最终选择A。
2、 这个表有50w行数就,为什么limit相差1为就差别这么大?
这与语句本身有关。这个语句中有group by,这就意味着每多limit一个值,实际上需要扫描更多的行N。 这里N为“表的总行数”/“表中不同的f2值”。
也就是说这个语句使得这个bug有放大作用。
解决方案
分析清楚后解决方法就比较简单了,修改代码逻辑,在执行test_if_cheaper_ordering过程中,改用字段f1的区分度来计算即可。
相关推荐
异常定义是创建一个特定的条件(condition),这个条件关联到可能发生的错误或警告。语法如下: ```sql DECLARE condition_name CONDITION FOR [condition_type]; ``` 其中,`condition_name`是你为异常起的...
mysql 树形结构查询 MySQL 树形结构查询是指使用存储过程来实现 MySQL 数据库中的树形结构查询。这种查询方式可以高效地查询树形结构的数据,并且可以根据需要设置递归深度。 MySQL 中的树形结构查询可以使用存储...
然后,声明了一个变量`l_manager_id`和一个游标`csr_mgr_id`。游标用于从`employee`表中查询与给定名字和姓氏匹配的经理ID。 接下来,我们定义了异常处理程序(Exception Handlers)。这些处理程序使用`DECLARE ...
mysql中有种可以通过join相关操作进行表与表之间的方式查询不同结果集,但是在一对多的情况下,关键查询的结果是多条的.例如:班级和学习的关系,我想很直观的看到班级和学生的情况,列表显示出班级的信息和班级的男生...
在Centos上部署项目发现一个奇怪的问题,数据库连接一直抛异常。于是花了两个小时搜了各种数据库连接异常导致的原因,最终问题得以解决。同时,把解决过程中搜集到的异常信息汇总一下,当大家遇到类似的问题时,给...
在某个新服务器上,新建了一个MySQL的实例,该服务器上面只有MySQL这一个进程,但是CPU的负载却居高不下,使用top命令查询的结果如下: [dba_mysql@dba-mysql ~]$ top top - 17:12:44 up 104 days, 20 min, 2 ...
本文主要讨论了如何解决MySQL单表2000万数据查询慢的问题,通过将表分区和使用时间触发器来实现数据的优化。 分区设计 在解决方案中,我们使用的是按照8周将单表分为8个区,每周一都会将最早一周的分区Drop掉,然后...
异常发生的原因在于MySQL服务器默认设置了一个超时时间(`wait_timeout`),如果一个连接长时间未被使用,则MySQL会自动断开这个连接。在这个案例中,C3P0连接池中的某些连接由于长时间空闲而被MySQL服务器断开,...
本文实例讲述了MySQL 多表关联一对多查询实现取最新一条数据的方法。分享给大家供大家参考,具体如下: MySQL 多表关联一对多查询取最新的一条数据 遇到的问题 多表关联一对多查询取最新的一条数据,数据出现重复 ...
MyBatis是当前最流行的持久层框架之一,它提供了一个简单的方式来访问数据库。然而,在使用MyBatis连接MySQL8时,可能会出现一些问题。本文将介绍MyBatis连接MySQL8出现的问题解决方法。 问题描述 在使用MyBatis...
MySql,多表联合查询加AS语句,多余的就不说了,自己看,绝对简单!
MySQL是世界上最流行的关系型数据库管理系统之一,然而在日常使用中,由于各种原因,我们可能会遇到各种异常和错误。这些异常通常以错误代码的形式出现,帮助我们识别问题所在并找到解决方案。以下是一些常见的MySQL...
在C#应用中,我们需要一个连接MySQL的驱动,如`MySql.Data.MySqlClient`库,这个库提供了用于与MySQL服务器交互的方法和类。如果在项目中缺失该库的dll(动态链接库),可以通过NuGet包管理器在线安装或手动将dll...
MySQL 数据库的主从复制是实现高可用性和数据冗余的一种常见策略,它允许数据从一个主服务器(Master)实时同步到一个或多个从服务器(Slave)。然而,在实际操作中,可能会遇到各种复制异常问题,这会影响数据的...
在使用IntelliJ IDEA进行Java开发并结合MySQL数据库进行调试时,可能会遇到一系列常见的异常问题。以下是一些解决这些问题的方法: 1. **错误提示:Error: java: 程序包 javax.servlet.http 不存在** 当IDEA导入...
MySQL异常查询案例分析 标题和描述中提到的知识点涉及MySQL查询优化问题,特别是与索引相关的问题。具体内容主要通过案例分析方式,展示了一个在相同查询语句中,仅限定了不同行数(limit值不同)的情况下,性能...
【基于PHP+MYSQL的成绩查询系统】是一个典型的Web应用程序,它结合了PHP编程语言和MySQL数据库管理系统,用于实现在线成绩查询的功能。这样的系统通常应用于教育机构,方便学生、教师以及管理员查看和管理学术成绩。...
1、支持所有数据库查询字符串,或者指定一个或者多个数据库查询字符串; 2、支持本地使用或者指定远程数据库地址; 3、支持命令行指定参数方便运行,支持省略参数; 4、支持显示匹配的数据来自数据库名称和表名称; ...
1.表中有id和name 两个字段,查询出name重复的所有数据 select * from xi a where (a.username) in (select username from xi group by username having count(*) > 1) 2、查询出所有数据进行分组之后,和重复数据...
2. 功能翻倍:增加模糊查询模式,并且可以修改一个参数控制。 Php Mysql电脑网页版通用考试成绩查分系统使用用途 适合修改不频繁、保密性不高的成绩、工资、物业水电费等各种精准查询。 1. 成绩查询系统,每个学校...