我的编程空间,编程开发者的网络收藏夹
学习永远不晚

ORDER BY导致索引使用不理想

短信预约 信息系统项目管理师 报名、考试、查分时间动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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 = "1"  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 = "1"  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 = "1"  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 = "1"  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 = "1"  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 = "1"  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)

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

ORDER BY导致索引使用不理想

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

ORDER BY导致索引使用不理想

在MySQL中经常出现未按照理想情况使用索引的情况,今天记录一种Order by语句的使用导致未按预期使用索引的情况。1.  问题现象1.1 SQL语句:SELECT DISTINCT p.* FROM tb_name p WHERE 1=1 AND p.c
2016-07-15

MySQL45讲之函数转换导致不使用索引 - flowers

本文介绍三种因为函数转换导致不使用索引的问题。 前言本文介绍三种因为函数转换导致不使用索引的问题。注意,不使用索引指的是不使用树搜索,而是全表扫描索引树。显式使用函数比如 select count(*) from tradelog where month
MySQL45讲之函数转换导致不使用索引 - flowers
2022-02-20

union和子查询中order by一起使用导致排序失效问题及解决

目录一、前言二、问题列举2.1 子查询中不能使用order by2.2 子查询order by无效2.3 排序条件不够严格导致分页数据重复总结一、前言分页查询的需求如同家常便饭,多数情况下主要利用order by和limit即可实现,有些
2022-12-27

使用select 语句进行查询时,变量名不加引号会导致的错误

假设一个数据库表中存有如下信息,其中login_uuid字段数据类型为varchar 当我们查找999这条数据时,使用这样的查询语句 查询出来的结果也是,我们所期望的 但是,如果没有在999处加引号,会出现错误 查询结果为 所有,999开头的都查出来
使用select 语句进行查询时,变量名不加引号会导致的错误
2017-05-03

使用imp导入表和索引至不同表空间的方法是什么

使用imp导入表和索引至不同表空间的方法是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。使用expdp/impdp方式将表和索引导入至不同表空间的方法导出:expdp te
2023-06-06

编程热搜

目录