在MySQL中经常出现未按照理想情况使用索引的情况,今天记录一种Order by语句的使用导致未按预期使用索引的情况。
1. 问题现象
1.1 SQL语句:
SELECT DISTINCT p.*FROM tb_name p WHERE1=1AND p.createDate>='2019-10-23'AND p.createDate<='2019-11-20 24:00:00'AND p.status='1'AND p.areaName LIKE'%上海%'ORDER BY p.payDate DESC LIMIT0,15
1.2 执行计划如下:

1.3 表中索引信息如下:

运行此SQL耗时约5.7s。从SQL及索引情况来看,使用createDate字段的索引应该会更好才对,为验证此情况,使用force index来强制使用createDate索引运行一次查看结果。
SQL改为如下:

修改后执行计划如下:

实际运行该SQL耗时约为0.15s,相差约50倍的差距。
1.5 简单分析
从执行计划情况对比来看,使用createDate会进行额外的排序(Using filesort),这个不难理解。
2 各种不太合理尝试
2.1 强制使用索引
使用force index (createDate)是可以解决的,此方式上面已经测试过了
2.2 忽略不理想的索引
类似于force index,可以使用IGNORE INDEX ,其实目的也在于使用上createDate 索引,例如:

其效果和force index 一致,运行耗时也在0.15s左右。
2.3 添加组合索引
将payDate 及createDate 添加为组合索引,但是此举不是一个好办法,执行计划也未按理想情况运行。
3. 相对合理的方式
无论使用force index 还是 ignore index都会影响MySQL优化器自身的执行情况。例如createDate 如果范围很大,那么其实走payDate 的索引取前15条记录会更快,为了让应用改动最少且不会因为其他条件的变化而导致未能走合理的索引,选择另一种优化方案,将SQL改为如下情况:

此时执行执行计划如下:

调整createDate 之后,执行执行计划:

也按预期的情况正常。由此看来此方式相对之前的方案更佳理想的。
网友评论