前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ORDER BY导致未按预期使用索引

ORDER BY导致未按预期使用索引

作者头像
July
发布2019-11-28 23:31:10
2.6K0
发布2019-11-28 23:31:10
举报
文章被收录于专栏:数据库干货铺数据库干货铺

在MySQL中经常出现未按照理想情况使用索引的情况,今天记录一种Order by语句的使用导致未按预期使用索引的情况。

1. 问题现象

1.1 SQL语句:

代码语言:javascript
复制
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 执行计划如下:

代码语言:javascript
复制

+----+-------------+-------+------------+-------+-------------------------------------------------------------+--------------------+---------+------+--------+----------+------------------------------------+
| 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 表中索引信息如下:

代码语言:javascript
复制

+------------------+------------+-------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| 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)

运行此SQL耗时约5.7s。从SQL及索引情况来看,使用createDate字段的索引应该会更好才对,为验证此情况,使用force index来强制使用createDate索引运行一次查看结果。

SQL改为如下:

代码语言:javascript
复制
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

修改后执行计划如下:

代码语言:javascript
复制
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 索引,例如:

代码语言:javascript
复制
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改为如下情况:

代码语言:javascript
复制
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

此时执行执行计划如下:

代码语言:javascript
复制
+----+-------------+-------+------------+-------+-------------------------------+------------+---------+------+--------+----------+----------------------------------------------------+
| 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 之后,执行执行计划:

代码语言:javascript
复制
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)

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据库干货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 问题现象
    • 1.1 SQL语句:
      • 1.2 执行计划如下:
        • 1.3 表中索引信息如下:
          • 1.5 简单分析
          • 2 各种不太合理尝试
            • 2.1 强制使用索引
              • 2.2 忽略不理想的索引
                • 2.3 添加组合索引
                • 3. 相对合理的方式
                相关产品与服务
                云数据库 SQL Server
                腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档