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)

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

(0)

相关推荐

  • 搜索引擎springboot集成(elasticSearch)

    各位小伙伴们,今天是今年的最后一天,这是我今年的最后一篇博客,在这里祝大家新年快乐!本次讲的是近几年比较流行的search搜索引擎,本文写的比较粗略,希望大家看了会有所收获,如若写错,请在评论区指出, ...

  • 敖丙工作以来总结的大厂SQL调优姿势

    这天我正在午休呢,公司DBA就把我喊醒了,说某库出现大量慢SQL,很快啊,很快,我还没反应过来,库就挂了,我心想现在的用户不讲武德啊,怎么在我睡觉的时候大量请求呢. 这是很常见的一个场景哈,因为很多业 ...

  • SAP HU & 序列号里的Sales Order号码不一致导致PGI失败问题之对策

    SAP HU & 序列号里的Sales Order号码不一致导致PGI失败问题之对策 笔者所在的项目上,某日收到业务部门用户报问题说某个DN发货过账,报错:"The indicato ...

  • 趋势丨蔗糖理想替代品——阿洛酮糖简介

    导语 作为蔗糖的理想替代品,阿洛酮糖是什么?有何特点? 文:中国食品报网丨欣文 图:Stockfood 我们曾分享过一篇关于甜味剂的解读文章,对甜味剂的优点及工业应用进行了介绍,指出世界范围内无糖和低 ...

  • 是什么导致中医受到一些人的压制?

    压制中医的状态还在延续,尽管国家开始重视,但是目前改观不大.经过大量信息分析,综合几方面因素,出乎预料,竟然是下列五大原因: 追求商业利润,这点西医远远大于中医.中医很难发财,医非仁爱不可托,非廉洁不 ...

  • 导致空压机故障99%的原因

    螺杆压缩机作为用途广泛的工业机器之一,在运行的时候有很多需要注意的操作细节去把握,才能保障设备平稳.高效运行,提高生产效率.整理了设备在使用中,一些常见故障的原因. 一.无法启动造成压缩机无法启动的可 ...

  • 浅析抗体偶联药物的理想抗原靶点应该具有哪些特征

    近年来抗体偶联药物(ADC)已经成为抗肿瘤药物研发的热点,可以将细胞毒药物直接运输到癌细胞发挥杀伤作用.合适的抗原靶点.高度特异性的抗体.理想的偶联子和高效的偶联药物的选择是一个成功的ADC药物需要具 ...

  • 这是理想中的户型,150㎡拥有5个大阳台享受,南北通透很难买到

    很多人都抱怨家里阳台太少了,那今天分享这套房子,面积150㎡,拥有超大客厅,最关键是家里拥有5个阳台,南边2个,北边3个,妥妥的南北通透的好户型,网友纷纷直呼:想买,想买! 客厅里选择无主灯设计,吊顶 ...

  • 少年派大结局,钱三一邓小琪进入理想大学,江天昊林妙妙留守江州

    追了一个多月的<少年派>,今天终于迎来大结局. 在前几集中,我们可以看到邓小琪在经历妈妈的事件之后,努力学习,奋发向上.终于稳稳地进了她的理想大学中戏. 钱三一,一如既往的稳定,仍然是市高 ...

  • 千人死亡导致美国参战:1915年5月7日英国卢西塔尼亚号被潜艇击沉

    萨沙历史上的今天. 作者:萨沙 本文章为萨沙原创,谢绝任何媒体转载 1915年5月7日:英国卢西塔尼亚号邮轮在爱尔兰外海被德国U-20潜艇击沉,造成1198人身亡. 一般认为,这是美国参加第一次世界大 ...

  • 理想信念

    出生已标层面, 后天全靠沉淀, 人生初定理想, 积极主动表现, 一路思考改变, 坚持方能高远, 立足本职表现, 看重总结实践, 更新思维信念, 未来成功可见.