美文网首页
五、深入浅出索引(下)

五、深入浅出索引(下)

作者: nieniemin | 来源:发表于2021-05-06 19:08 被阅读0次

    在下面这个表T中,执行 select * from T where k between 3 and 5,需要执行几次树的搜索操作,会扫描多少行?

    create table T (
    ID int primary key,
    k int NOT NULL DEFAULT 0, 
    s varchar(16) NOT NULL DEFAULT '',
    index k(k))
    engine=InnoDB;
    
    insert into T values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');
    
    InnoDB的索引组织结构

    这条SQL查询语句的执行流程:

    1. 在k索引树上找到k=3的记录,取得 ID = 300;
    2. 再到ID索引树查到ID=300对应的R3;
    3. 在k索引树取下一个值k=5,取得ID=500;
    4. 再回到ID索引树查到ID=500对应的R4;
    5. 在k索引树取下一个值k=6,不满足条件,循环结束。

    在这个过程中,回到主键索引树搜索的过程,我们称为回表。可以看到,这个查询过程读了k索引树的3条记录(步骤1、3和5),回表了两次(步骤2和4)。

    5.1 覆盖索引

    如果执行的语句是select ID from T where k between 3 and 5,这时只需要查ID的值,而ID的值已经在k索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引k已经“覆盖了”我们的查询需求,我们称为覆盖索引。

    由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

    基于上面覆盖索引的说明,我们来讨论一个问题:在一个市民信息表上,是否有必要将身份证号和名字建立联合索引?

     CREATE TABLE `tuser` (
      `id` int(11) NOT NULL,
      `id_card` varchar(32) DEFAULT NULL,
      `name` varchar(32) DEFAULT NULL,
      `age` int(11) DEFAULT NULL,
      `ismale` tinyint(1) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `id_card` (`id_card`),
      KEY `name_age` (`name`,`age`)
     ) ENGINE=InnoDB</pre>
    

    身份证号是市民的唯一标识。也就是说,如果有根据身份证号查询市民信息的需求,我们只要在身份证号字段上建立索引就够了。而再建立一个(身份证号、姓名)的联合索引,是不是浪费空间?

    如果现在有一个高频请求,要根据市民的身份证号查询他的姓名,这个联合索引就有意义了。它可以在这个高频请求上用到覆盖索引,不再需要回表查整行记录,减少语句的执行时间。

    当然,索引字段的维护总是有代价的。因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑了。这正是业务DBA,或者称为业务数据架构师的工作。

    5.2 最左前缀原则

    在mysql中执行查询时,只能使用一个索引。下面的查询sql语句中,假设我们现在只在name上建索引,可以快速把范围限制在一个结果集A,然后在A中扫描符合条件的age。这样扫描出来的结果集仍然远远大于我们的实际需要。为了提高搜索效率,我们需要考虑运用多列索引,即联合索引。

     select id from tuser where   name=? and age=? 
    

    最左前缀:顾名思义,就是最左优先,上例中我们创建了name,ages多列索引,相当于创建了(name)单列索引,(name,age)组合索引。

    5.3 索引下推

    select * from tuser where name like '张%' and age=10 and ismale=1;
    

    在MySQL 5.6之前,只能从ID3开始一个个回表。到主键索引上找出数据行,再对比字段值。

    而MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

    无索引下推执行流程 索引下推执行流程

    上图中每一个虚线箭头表示回表一次。

    无索引下推执行流程在(name,age)索引里面我特意去掉了age的值,这个过程InnoDB并不会去看age的值,只是按顺序把“name第一个字是’张’”的记录一条条取出来回表。因此,需要回表4次。

    有索引下推执行流程中,InnoDB在(name,age)索引内部就判断了age是否等于10,对于不等于10的记录,直接判断并跳过。在我们的这个例子中,只需要对ID4、ID5这两条记录回表取数据判断,就只需要回表2次。

    总结:
    1、回表:SQL查询过程中,需要回到主键索引树搜索的过程,我们称为回表。
    2、覆盖索引:如果查询条件使用的是普通索引(或是联合索引的最左原则字段),查询结果是联合索引的字段或是主键,不用回表操作,直接返回结果,减少IO磁盘读写读取正行数据
    3、最左前缀:最左优先,以最左边的为起点任何连续的索引都能匹配上
    4、联合索引:根据创建联合索引的顺序,以最左原则进行where检索,比如(age,name)以age=1 或 age= 1 and name=‘张三’可以使用索引,单以name=‘张三’ 不会使用索引,考虑到存储空间的问题,还请根据业务需求,将查找频繁的数据进行靠左创建索引。
    5、索引下推:like 'hello%’and age >10 检索,MySQL5.6版本之前,会对匹配的数据进行回表查询。5.6版本后,会先过滤掉age<10的数据,再进行回表查询,减少回表率,提升检索速度

    相关文章

      网友评论

          本文标题:五、深入浅出索引(下)

          本文链接:https://www.haomeiwen.com/subject/jxwddltx.html