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得到理想中的数据结果呢? 例如 有一个 ...
随机推荐
- POJ1704 Georgia and Bob(Nim博弈变形)
Georgia and Bob Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 14312 Accepted: 4840 ...
- AtCoder-3920
We have a 3×3 grid. A number ci,j is written in the square (i,j), where (i,j) denotes the square at ...
- HDU-3727 Jewel
Jimmy wants to make a special necklace for his girlfriend. He bought many beads with various sizes, ...
- Jomoo的模板
目录 1 杂类算法 1.1 快读模板 1.2 O(1) int64 乘法 2 图论算法 2.1 树类 - Trie 2.2 树类 - 并查集(NB version) 2.3 树类 - LCA 2.4 ...
- Python爬虫技术:爬虫时如何知道是否代理ip伪装成功?
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. python爬虫时如何知道是否代理ip伪装成功: 有时候我们的爬虫程序添加了 ...
- Django异步任务线程池
当数据库数据量很大时(百万级),许多批量数据修改请求的响应会非常慢,一些不需要即时响应的任务可以放到后台的异步线程中完成,发起异步任务的请求就可以立即响应 选择用线程池的原因是:线程比进程更为可控.不 ...
- UWP 中的全局异常处理
问题 在开发一款应用的过程中,我们开发者很难考虑到所有问题,往往会忘记处理一些可能发生的异常.随之而来的结果就是用户使用过程中接连不断的崩溃.所以,我们有必要处理所有未被我们处理的异常. 本文介绍了 ...
- python学习-excel读取
# 第三方库 openpyxl # install 安装# pip install openpyxl # 引入第三方库# excel操作的流程:# 打开excel,进入工作薄 workbook# 选择 ...
- 自建邮件服务器域名解析设置(A与MX记录)
自建邮件服务器域名解析设置(A与MX记录) 前言 如果域名没有做解析,只能用于内网收发邮件.要想实现与外网邮箱的收发,需要做域名解析.是在"域名解析后台"进行设置(域名提供商提供& ...
- 使用between操作符过滤数据
select prod_name,prod_price from products where prod_price between 5 and 10; 就可以查询处5-10之间的所有数据 betwe ...