在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. Mybatis_多表关联查询_resultMap_集合对象_N+1方式实现

    mapper 层 提供 ClazzMapper 和 StudentMapper, ClazzMapper 查询所有班级信息, StudentMapper 根据班级编号查询学生信息. 在 ClazzMa ...

  2. 无向图的桥+搜索优化--UESTC1956-北极的猴子

    北极的猴子 Time Limit: 1000 MS     Memory Limit: 256 MB Submit Status 也许你不知道,在北极也有猴子,我们叫它们北极猴.北极猴们在北极一共有n ...

  3. 学好linux必须精通用户管理的章节知识

    第12章 Linux中用户知识管理 12.1 系统开机启动流程 12.1.1 centos6系统开机启动流程 12.1.1.1 开机系统流程语言描述 服务器电源开关打开 bios自检 目的:检查硬件是 ...

  4. Mysql双活方案

    #### 说明 Mysql主主互备即为两个mysql的互为备份机   ##### Windows下安装步骤(Linux下步骤类似,基本就是装上mysql,然后修改配置来完成主从的设置) - step1 ...

  5. python 2.7导入模块问题

    有如下结构的python文件 base |----pkg1 |----__init__.py |----add.py |----pkg2 |----__init__.py |----call_func ...

  6. DENEBOLA (See3CAM_CX3RDK) - CX3 Reference Design

    Denebola (See3CAM_CX3RDK) is a USB3.0 USB video class (UVC) reference design kit (RDK) developed by ...

  7. 为什么JDK动态代理中要求目标类实现的接口数量不能超过65535个

    先明确几个概念: Class文件是一组以8字节为基础单位的二进制流 各个数据项目严格按照顺序紧凑排列在class文件中 中间没有任何分隔符,这使得class文件中存储的内容几乎是全部程序运行的程序 J ...

  8. 机会来了!5G时代带来新闻传播行业的变革!

    5G时代到来!新闻传播行业大变革! 1.作为一名体育生进入的新闻传播学院,传统的新闻媒体能力已不再具有优势,意味着我有翻身的机会了! 从一开始进入大学,由于高中的知识储备不如其他人,尤其是英语能力方面 ...

  9. python之MiniWeb框架

    以往,我们多完成的是静态web服务器,主要处理的都是一些已经‘写死’的数据,那么今天,我们来学习一下动态数据的处理. 说到动态数据,我们就需要了解一个东西,那就是web框架. 所谓web框架简单地说就 ...

  10. 《Java基础知识》Java类的定义及其实例化

    类必须先定义才能使用.类是创建对象的模板,创建对象也叫类的实例化. 下面通过一个简单的例子来理解Java中类的定义: public class Dog { String name; int age; ...