ORDER BY导致索引使用不理想
在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导致索引使用不理想的更多相关文章
- mysql 理解索引,添加索引,使用索引(哪些情况会导致索引失效)
索引用于快速找出在某个列中有一特定值的行.不使用索引,MySQL必须从第1条记录开始然后读完整个表直到找出相关的行,还需要考虑每次读入数据页的IO开销.而如果采取索引,则可以根据索引指向的页以及记录在 ...
- SQL SERVER 中is null 和 is not null 将会导致索引失效吗?
其实本来这个问题没有什么好说的,今天优化的时候遇到一个SQL语句,因为比较有意思,所以我截取.简化了SQL语句,演示给大家看,如下所示 declare @bamboo_Code varchar(3); ...
- 索引法则--LIKE以%开头会导致索引失效进而转向全表扫描(使用覆盖索引解决)
Mysql 系列文章主页 =============== 1 准备数据 1.1 建表 DROP TABLE IF EXISTS staff; CREATE TABLE IF NOT EXISTS st ...
- order by与索引(转载)
order by与索引 ORDER BY 通常会有两种实现方法,一个是利用有序索引自动实现,也就是说利用有序索引的有序性就不再另做排序操作了.另一个是把结果选好之后再排序. 用有序索引这种,当然是 ...
- 有些 where 条件会导致索引无效
在查询中,WHERE 条件也是一个比较重要的因素,尽量少并且是合理的 where条件是徆重要的,尽量在多个条件的时候,把会提取尽量少数据量的条件放在前面,减少后一个 where 条件的查询时间.有些 ...
- mybatis的sql语句导致索引失效,使得查询超时
mybaitis书写sql需要特别注意where条件中的语句,否则将会导致索引失效,使得查询总是超时.如下语句会出现导致索引失效的情况: with test1 as (select count(C_F ...
- 【MySQL 原理分析】之 Trace 分析 order by 的索引原理
一.背景 昨天早上,交流群有一位同学提出了一个问题.看下图: 我不是大佬,而且当时我自己的想法也只是猜测,所以并没有回复那位同学,只是接下来自己做了一个测试验证一下. 他只简单了说了一句话,就是同样的 ...
- mysql中group by和order by混用 结果不是理想结果(转)
文章转自 https://www.cnblogs.com/myphper/p/3767572.html 在使用mysql排序的时候会想到按照降序分组来获得一组数据,而使用order by往往得到的不是 ...
- mysql中group by和order by混用 结果不是理想结果
在使用mysql排序的时候会想到按照降序分组来获得一组数据,而使用order by往往得到的不是理想中的结果,那么怎么才能使用group by 和order by得到理想中的数据结果呢? 例如 有一个 ...
随机推荐
- 数据库Oracle日期函数
SYSDATE 函数:是一个日期函数,它返回当前数据库服务器的日期和时间. 用日期计算: • 从日期加或者减一个数,结果是一个日期值 • 两个日期相减,得到两个日期之间的天数 ,可以加小时到日期上 S ...
- 度及拓扑图的使用-UESTC1958学霸周选课
学霸周选课 Time Limit: 1000 MS Memory Limit: 128 MB Submit Status 众所周知周大爷不仅编程了得,专业课成绩更是名列前茅,恰巧又到了选课的季 ...
- 2019企业linux运维最需要的了解的一些硬件基础知识
第3章 服务器 245 3.1 电脑的种类 245 3.2 服务器的介绍 246 3.2.1 服务器的类别 246 3.2.2 服务器的性能 247 3.2.3 服务器的 ...
- 怎么定义一个自己的vue组件
1.在src文件夹中创建一个hello文件夹,然后创建hello.js和hello.vue 2.hello.vue代码如下 <template> <button>这是hello ...
- Python文件头注释的含义,你肯定不懂
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:冰火梦幻 本文档描述了Python语言文件头里典型注释的含义. 1. ...
- Django 04
目录 视图层 三个常用方法 JsonResponse FBV 和 CBV 模板层 模板语法 模板传值 过滤器 标签 自定义过滤器和标签 模板的继承 模板的导入 视图层 三个常用方法 HttpRespo ...
- postman高级应用
目录 提取接口返回值 设置动态参数 流程控制 调试 定义公共函数 外部数据文件 提取接口返回值 1.获取响应的header值 var Content-Type=postman.getResponseH ...
- RT-Thread的位图调度算法分析(最新版)
RT-Thread的内核调度算法 rt-thread的调度算法为基于优先级调度和基于时间片轮转调度共存的策略.rt-thread内核中存在多个线程优先级,并且支持多个线程具有同样的线程优先级.线程级别 ...
- 函数式编程 -> Lambda
一.函数式编程 函数式编程,同面向对象编程.指令式编程一样,是一种软件编程范式,在多种编程语言中都有应用.百科词条中有很学术化的解释,但理解起来并不容易.不过,我们可以借助于数学中函数的概念,来理解函 ...
- 《MySQL数据库》常用语法(二)
表关联关系: -- 内联接 SELECT * FROM m INNER JOIN n ON m.id = n.id; -- 左外联接 SELECT * FROM m LEFT JOIN n ON m. ...