zoukankan      html  css  js  c++  java
  • 【开发总结】order by 为什么没有走索引?

    1.  现象

    表结构如下

    CREATE TABLE `ACT_HI_INST` (
      `ID` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '主键',
      `INST_ID_` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '流程实例id',
      `BUSINESS_KEY_` varchar(255) COLLATE utf8_bin DEFAULT '' COMMENT '流程编号',
      `CREATE_TIME_` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `UPDATE_TIME_` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      PRIMARY KEY (`ID`),
      UNIQUE KEY `UIDX_INST` (`INST_ID_`),
      UNIQUE KEY `UIDX_BKEY` (`BUSINESS_KEY_`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
    执行下面语句, 发现没有走索引
    explain
    SELECT
    *
    FROM ACT_HI_INST hi 
    ORDER BY hi.BUSINESS_KEY_ DESC;

     

    select count(*) from ACT_HI_INST ;
    -- 4332

    同时我们发现rows与实际表的数量存在差异,使用下面语句重新统计索引信息, 更正统计信息, 更新之后发现 rows 就数据表的数量基本一致(不完全一样是因为rows是采样统计而来的)。

    analyze table ACT_HI_INST;

    也就是说对于下面这条语句,尽管BUSINESS_KEY_  上有索引,MySQL还是选择了全表扫描。为什么会这样呢?

    SELECT * FROM ACT_HI_INST hi ORDER BY hi.BUSINESS_KEY_ DESC;

    2. 猜想

    我猜想原因是 因为MySQL认为即使 使用 BUSINESS_KEY_  索引树进行查询,不需要排序,但是最后仍然需要回表,回表的次数 == 表的大小,这种代价比全表扫描然后排序的代价更大。

    3. 验证

    为了验证我的猜想,我加上在SQL语句最后加上 LIMIT 10 。如下所示

    explain
    SELECT
    *
    FROM ACT_HI_INST hi 
    ORDER BY hi.BUSINESS_KEY_ DESC limit 10;

    果然我们看到加上 LIMIT 的语句走了索引。因为这个时候MySQL认为回表的代价比排序的代价更小,所以这个时候选择了走 BUSINESS_KEY_  索引。

    除了上面这种方式可以验证我的猜想,还有一种方式,如下所示。我们只查询BUSINESS_KEY_ 。

    因为此时叶子节点就包含我们需要查询的字段,这个时候不需要再回表,所以MySQL选择BUSINESS_KEY_  代价最小。

    explain
    SELECT
    hi.BUSINESS_KEY_
    FROM ACT_HI_INST hi 
    ORDER BY hi.BUSINESS_KEY_ DESC ;

    4. 补充

    这部分主要来源于极客时间林晓斌老师的《MySQL实战45讲》第16讲

    order by a

    如果 a 字段上有索引,MySQL innodb引擎是按照上诉的方式进行选择。如果a字段上没有索引,MySQL  innodb引擎 就会有两种排序方式:

    全字段排序 和 rowid 排序。

    全字段排序:将所有要选择的字段加入到sort_buffer中,然后在内存或者外部进行排序。如果能在内存中进行排序就在内存中进行排序。

    如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。

    如果查询要返回的字段很多的话,那么 sort_buffer 里面要放的字段数太多,这样内存里能够同时放下的行数很少,要分成很多个临时文件,排序的性能会很差。这个时候MySQL就会采用rowId排序。

    rowid排序:MySQL取出需要排序的字段和ID放入sort_buffer中进行排序,最后按照排序的结果,通过ID回表,返回数据到客户端。

    MySQL 的一个设计思想是如果内存够,就要多利用内存,尽量减少磁盘访问。所以对应sort_buffer足够大的情况,MySQL会优选选择全字段排序。

  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯VIP基础练习 矩形面积交
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    核心思想:想清楚自己创业的目的(如果你没有自信提供一种更好的产品或服务,那就别做了,比如IM 电商 搜索)
    在Linux中如何利用backtrace信息解决问题
  • 原文地址:https://www.cnblogs.com/catlkb/p/13696933.html
Copyright © 2011-2022 走看看