在MySQL中经常出现未按照理想情况使用索引的情况,今天记录一种Order by语句的使用导致未按预期使用索引的情况。

1.  问题现象

1.1 SQL语句:

SELECT DISTINCT p.*  FROM tb_name p
WHERE 1=1 AND p.createDate >= '2019-10-23' AND p.createDate <= '2019-11-20 24:00:00' AND p.status = '' AND p.areaName LIKE '%上海%'
ORDER BY p.payDate DESC LIMIT 0 , 15

1.2 执行计划如下:

+----+-------------+-------+------------+-------+-------------------------------------------------------------+--------------------+---------+------+--------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+-------------------------------------------------------------+--------------------+---------+------+--------+----------+------------------------------------+
| 1 | SIMPLE | p | NULL | range | createDate,idx_status_payDate                  | idx_status_payDate | 108 | NULL | 880063 | 0.74 | Using index condition; Using where |
+----+-------------+-------+------------+-------+-------------------------------------------------------------+--------------------+---------+------+--------+----------+------------------------------------+

1.3 表中索引信息如下:

+------------------+------------+-------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+-------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tb_name | 0 | PRIMARY | 1 | id | A | 1760103 | NULL | NULL | | BTREE | | |
| tb_name | 1 | idx_payDate | 1 | payDate | A | 1734626 | NULL | NULL | YES | BTREE | | |
| tb_name | 1 | createDate | 1 | createDate | A | 1736316 | NULL | NULL | YES | BTREE | | |
| tb_name | 1 | idx_status_payDate | 1 | status | A | 2 | NULL | NULL | YES | BTREE | | |
| tb_name | 1 | idx_status_payDate | 2 | payDate | A | 1741214 | NULL | NULL | YES | BTREE | | |
+------------------+------------+-------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
16 rows in set (0.00 sec)

1.4   理想情况

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

SQL改为如下:

SELECT DISTINCT p.*  FROM tb_name p  FORCE INDEX (createDate)
WHERE 1=1 AND p.createDate >= '2019-10-23' AND p.createDate <= '2019-11-20 24:00:00' AND p.status = '' AND p.areaName LIKE '%上海%'
ORDER BY p.payDate DESC LIMIT 0 , 15

修改后执行计划如下:

root@db09:03:13>explain SELECT DISTINCT p.*  FROM tb_namep  FORCE INDEX (createDate)
-> WHERE 1=1 AND p.createDate >= '2019-10-23' AND p.createDate <= '2019-11-20 24:00:00' AND p.status = '' AND p.areaName LIKE '%上海%'
-> ORDER BY p.payDate DESC LIMIT 0 , 15;
+----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+----------------------------------------------------+
| 1 | SIMPLE | p | NULL | range | createDate | createDate | 6 | NULL | 117858 | 1.11 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+----------------------------------------------------+
1 row in set, 3 warnings (0.00 sec)

实际运行该SQL耗时约为0.15s,相差约50倍的差距。

1.5 简单分析

从执行计划情况对比来看,使用createDate会进行额外的排序(Using filesort),这个不难理解。

2   各种不太合理尝试

2.1 强制使用索引

使用force  index (createDate)是可以解决的,此方式上面已经测试过了

2.2  忽略不理想的索引

类似于force index,可以使用IGNORE INDEX ,其实目的也在于使用上createDate 索引,例如:

SELECT DISTINCT p.*  FROM tb_name p  IGNORE INDEX (idx_status_payDate,idx_payDate)
WHERE 1=1 AND p.createDate >= '2019-10-23' AND p.createDate <= '2019-11-20 24:00:00' AND p.status = '' AND p.areaName LIKE '%上海%'
ORDER BY p.payDate DESC LIMIT 0 , 15

其效果和force index 一致,运行耗时也在0.15s左右。

2.3 添加组合索引

将payDate 及createDate 添加为组合索引,但是此举不是一个好办法,执行计划也未按理想情况运行。

3.  相对合理的方式

无论使用force  index  还是 ignore index都会影响MySQL优化器自身的执行情况。例如createDate 如果范围很大,那么其实走payDate 的索引取前15条记录会更快,为了让应用改动最少且不会因为其他条件的变化而导致未能走合理的索引,选择另一种优化方案,将SQL改为如下情况:

SELECT DISTINCT p.*  FROM tb_name p
WHERE 1=1 AND p.createDate >= '2019-10-23' AND p.createDate <= '2019-11-20 24:00:00' AND p.status = '' AND p.areaName LIKE '%上海%'
ORDER BY p.payDate DESC, createDate LIMIT 0 , 15

此时执行执行计划如下:

+----+-------------+-------+------------+-------+-------------------------------+------------+---------+------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+-------------------------------+------------+---------+------+--------+----------+----------------------------------------------------+
| 1 | SIMPLE | p | NULL | range | createDate,idx_status_payDate | createDate | 6 | NULL | 123024 | 5.55 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------------+-------+-------------------------------+------------+---------+------+--------+----------+----------------------------------------------------+
1 row in set, 3 warnings (0.00 sec)

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

root@db 09:51:00>EXPLAIN
-> SELECT DISTINCT p.* FROM tb_name p IGNORE INDEX (idx_status_synIs_deleteStatus)
-> WHERE 1=1 AND p.createDate >= '2009-10-23' AND p.createDate <= '2019-11-20 24:00:00' AND p.status = '' AND p.areaName LIKE '%上海%'
-> ORDER BY p.payDate DESC,createDate DESC LIMIT 0 , 15;
+----+-------------+-------+------------+------+-------------------------------+--------------------+---------+-------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-------------------------------+--------------------+---------+-------+--------+----------+----------------------------------------------------+
| 1 | SIMPLE | p | NULL | ref | createDate,idx_status_payDate | idx_status_payDate | 108 | const | 880205 | 5.56 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------------+------+-------------------------------+--------------------+---------+-------+--------+----------+----------------------------------------------------+
1 row in set, 3 warnings (0.00 sec)

也按预期的情况正常。由此看来此方式相对之前的方案是最佳的。

ORDER BY导致索引使用不理想的更多相关文章

  1. mysql 理解索引,添加索引,使用索引(哪些情况会导致索引失效)

    索引用于快速找出在某个列中有一特定值的行.不使用索引,MySQL必须从第1条记录开始然后读完整个表直到找出相关的行,还需要考虑每次读入数据页的IO开销.而如果采取索引,则可以根据索引指向的页以及记录在 ...

  2. SQL SERVER 中is null 和 is not null 将会导致索引失效吗?

    其实本来这个问题没有什么好说的,今天优化的时候遇到一个SQL语句,因为比较有意思,所以我截取.简化了SQL语句,演示给大家看,如下所示 declare @bamboo_Code varchar(3); ...

  3. 索引法则--LIKE以%开头会导致索引失效进而转向全表扫描(使用覆盖索引解决)

    Mysql 系列文章主页 =============== 1 准备数据 1.1 建表 DROP TABLE IF EXISTS staff; CREATE TABLE IF NOT EXISTS st ...

  4. order by与索引(转载)

    order by与索引   ORDER BY 通常会有两种实现方法,一个是利用有序索引自动实现,也就是说利用有序索引的有序性就不再另做排序操作了.另一个是把结果选好之后再排序. 用有序索引这种,当然是 ...

  5. 有些 where 条件会导致索引无效

    在查询中,WHERE 条件也是一个比较重要的因素,尽量少并且是合理的 where条件是徆重要的,尽量在多个条件的时候,把会提取尽量少数据量的条件放在前面,减少后一个 where 条件的查询时间.有些 ...

  6. mybatis的sql语句导致索引失效,使得查询超时

    mybaitis书写sql需要特别注意where条件中的语句,否则将会导致索引失效,使得查询总是超时.如下语句会出现导致索引失效的情况: with test1 as (select count(C_F ...

  7. 【MySQL 原理分析】之 Trace 分析 order by 的索引原理

    一.背景 昨天早上,交流群有一位同学提出了一个问题.看下图: 我不是大佬,而且当时我自己的想法也只是猜测,所以并没有回复那位同学,只是接下来自己做了一个测试验证一下. 他只简单了说了一句话,就是同样的 ...

  8. mysql中group by和order by混用 结果不是理想结果(转)

    文章转自 https://www.cnblogs.com/myphper/p/3767572.html 在使用mysql排序的时候会想到按照降序分组来获得一组数据,而使用order by往往得到的不是 ...

  9. mysql中group by和order by混用 结果不是理想结果

    在使用mysql排序的时候会想到按照降序分组来获得一组数据,而使用order by往往得到的不是理想中的结果,那么怎么才能使用group by 和order by得到理想中的数据结果呢? 例如 有一个 ...

随机推荐

  1. Coderfocers-616c

    You are given a rectangular field of n × m cells. Each cell is either empty or impassable (contains ...

  2. Selenium之xpath绝对路径表示法

    xpath写法: 绝对路径:以/开始,逐个增加节点用/分割 特点:不能跨级.类似css中的直接子元素选择器 相对路径:用两个斜杠  //      如  //div//p//a 通配符:xpath也有 ...

  3. 关于OV7670摄像头的分辨率设置

    关于OV7670摄像头的分辨率设置最近一直在纠结如何把OV7670输出的图片分辨率缩小,以减少数据量来适应数据的传输,在网上看了好多也没有关于寄存器的具体设置,最终又读了几遍数据手册,加上网友们写的帖 ...

  4. Webpack基础知识总结

    本文将从多个方面回顾下自己了解的Webpack知识,包括常见的配置项,前端搭建的一些方法和项目实际优化方法,有错误的地方还请指出并多多包涵. 一.关于Webpack 1.概念 本质上,webpack ...

  5. 【Git】远程分支

    [Git]远程分支 转载:https://www.cnblogs.com/yangchongxing/p/10239270.html 目录 ============================ 1 ...

  6. Python异常体系结构图

  7. GHOST CMS - Package.json

    Package.json The package.json file is a set of meta data about a theme. package.json 文件是一组关于主题的元数据. ...

  8. Kotlin 编程语言成为其 Android 应用程序开发人员的首选语言

    今年 5 月,谷歌在 I/O 大会上宣布,Kotlin 编程语言成为其 Android 应用程序开发人员的首选语言. Kotlin 是一种面向现代多平台应用程序的编程语言,成为谷歌开发 Android ...

  9. css3 @page

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  10. Sqlite—数据库备份与恢复

    数据库备份 例如:备份 /www/wwwroot 下面的 task.db 数据库 1.进入数据库 [root@localhost ~]# sqlite3 /www/wwwroot/task.db 2. ...