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得到理想中的数据结果呢? 例如 有一个 ...
随机推荐
- 🔥🔥🔥Spring Cloud进阶篇之Eureka原理分析
前言 之前写了几篇Spring Cloud的小白教程,相信看过的朋友对Spring Cloud中的一些应用有了简单的了解,写小白篇的目的就是为初学者建立一个基本概念,让初学者在学习的道路上建立一定的基 ...
- [TimLinux] JavaScript 模态框可拖动功能实现——原始版
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- HDU1517 Multiply Game
Stan and Ollie play the game of multiplication by multiplying an integer p by one of the numbers 2 t ...
- scikit-learn与数据预处理
.caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...
- 备战“金九银十”10道String高频面试题解析
前言 String 是我们实际开发中使用频率非常高的类,Java 可以通过 String 类来创建和操作字符串,使用频率越高的类,我们就越容易忽视它,因为见的多所以熟悉,因为熟悉所以认为它很简单,其实 ...
- 《Java算法》排序算法-快速排序
排序算法-快速排序: /** * 给定一个数组:按照从小到大排序. * 思路: * 1. 获取第一个数放入临时变量data,将大于data的数放右边,小于data的数放在左边. * 2. data左边 ...
- JS中原始值和引用值分析
JS中变量中两种类型的值:原始值,引用值 原始值是存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置. var x = 1; //1就是一个原始值,变量x中存放的就是原始 ...
- JavaScript动画实例:旋转的圆球
1.绕椭圆轨道旋转的圆球 在Canvas画布中绘制一个椭圆,然后在椭圆上绘制一个用绿色填充的实心圆.之后每隔0.1秒刷新,重新绘制椭圆和实心圆,重新绘制时,实心圆的圆心坐标发生变化,但圆心坐标仍然位于 ...
- python子类如何继承父类的实例变量?
类型1:父类和子类的实例变量均不需要传递 class A(object): def __init__(self): self.name = "cui" def get_name(s ...
- Windows 10 - 查看系统剪切板历史
在之前的Windows版本(1809之前),Windows的剪切板历史一直都是对用户不可见的. 也就是它只保留最后一次的剪切记录,但是这个记录是什么,不知道!只能粘贴出来才知道. 但是现在变了,Win ...