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会优选选择全字段排序。

  • 相关阅读:
    JS学习专辑(3) DOM
    JS学习专辑(4) 变量作用域和语句
    JS学习专辑(6) 函数
    JS学习专辑(2) BOM
    WPF Adorner学习(1)
    C# 递归
    IEnumerable和IEnumerator
    C# 索引器
    JS学习专辑(5) 对象和数组
    JS学习专辑(1) 入门
  • 原文地址:https://www.cnblogs.com/catlkb/p/13696933.html
Copyright © 2011-2022 走看看