当前位置 博文首页 > gegeman:MySQL复合索引探究

    gegeman:MySQL复合索引探究

    作者:gegeman 时间:2021-02-04 00:22

     

    复合索引(又称为联合索引),是在多个列上创建的索引。创建复合索引最重要的是列顺序的选择,这关系到索引能否使用上,或者影响多少个谓词条件能使用上索引。复合索引的使用遵循最左匹配原则,只有索引左边的列匹配到,后面的列才能继续匹配。本文主要探究复合索引的创建顺序与使用情况。

     

    (一)复合索引的概念

    在单个列上创建的索引我们称为单列索引,在2个以上的列上创建的索引称为复合索引。在单个列上创建索引相对简单,通常只需要考虑列的选择率即可,选择性越好,代表数据越分散,创建出来的索引性能也就更好。通常,某列选择率的计算公式为:
    selectivity = 施加谓词条件后返回的记录数 / 未施加谓词条件后返回的记录数
    可选择率的取值范围是(0,1],值越小,代表选择性越好。
    对于复合索引(又称为联合索引),是在多个列上创建的索引。创建复合索引最重要的是列顺序的选择,这关系到索引能否使用上,或者影响多少个谓词条件能使用上索引。复合索引的使用遵循最左匹配原则,只有索引左边的列匹配到,后面的列才能继续匹配。

     

    (二)什么情况下会使用复合索引的列

    复合索引遵循最左匹配原则,只有索引中最左列匹配到,下一列才有可能被匹配。如果左边列使用的是非等值查询,则索引右边的列将不会被查询使用,也不会被排序使用。

     

    实验:哪些情况下会使用到复合索引

     复合索引中的哪些字段被使用到了,是我们非常关心的问题。网络上一个经典的例子:

    -- 创建测试表
    CREATE TABLE t1(
    c1 CHAR(1) not null,
    c2 CHAR(1) not null,
    c3 CHAR(1) not null,
    c4 CHAR(1) not null,
    c5 CHAR(1) not null
    )ENGINE innodb CHARSET UTF8;
    
    -- 添加索引
    alter table t1 add index idx_c1234(c1,c2,c3,c4);
    
    --插入测试数据
    insert into t1 values('1','1','1','1','1'),('2','2','2','2','2'),
    ('3','3','3','3','3'),('4','4','4','4','4'),('5','5','5','5','5');

     需要探索下面哪些查询语句使用到了索引idx_c1234,以及使用到了索引的哪些字段?

    (A) where c1=? and c2=? and c4>? and c3=?
    (B) where c1=? and c2=? and c4=? order by c3
    (C) where c1=? and c4=? group by c3,c2
    (D) where c1=? and c5=? order by c2,c3
    (E) where c1=? and c2=? and c5=? order by c2,c3
    (F) where c1>? and c2=? and c4>? and c3=?

    A选项:

    mysql> explain select c1,c2,c3,c4,c5 from t1 where c1='2' and c2='2' and c4>'1' and c3='2';
    +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
    | id | select_type | table | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                 |
    +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
    |  1 | SIMPLE      | t1    | NULL       | range | idx_c1234     | idx_c1234 | 12      | NULL |    1 |   100.00 | Using index condition |
    +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+

    使用的索引长度为12,代表4个字段都使用了索引。由于c1、c2、c3都是等值查询,所以后面的c4列也可以用上。

    注:utf8编码,一个索引长度为3,这里12代表4个字段都用到该索引。

     

    B选项:

    mysql> explain select c1,c2,c3,c4,c5 from t1 where c1='2' and c2='2' and c4='2' order by c3;
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------------+
    | id | select_type | table | partitions | type | possible_keys | key       | key_len | ref         | rows | filtered | Extra                 |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------------+
    |  1 | SIMPLE      | t1    | NULL       | ref  | idx_c1234     | idx_c1234 | 6       | const,const |    1 |    20.00 | Using index condition |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------------+

     使用的索引长度为6,代表2个字段使用了索引。根据最左使用原则,c1、c2使用了索引。因为查询中没有c3谓词条件,所以索引值使用到c2后就发生了中断,导致只使用了c1、c2列。这里SQL使用了order by排序,但是在执行计划Extra部分未有filesort关键字,说明在索引中按照c3字段顺序读取数据即可。

    这里特别留意,虽然索引中的c3字段没有放在索引的最后,但是确实使用到了索引中c2字段的有序特性,因为执行计划的Extra部分未出现"fileasort"关键字。这是为什么呢?这里用到了MySQL5.6版本引入的Index Condition Pushdown (ICP) 优化。其核心思想是使用索引中的字段做数据过滤。我们来整理一下不使用ICP和使用ICP的区别:

    如果没有使用ICP优化,其SQL执行步骤为:

    1.使用索引列c1,c2获取满足条件的行数据。where c1='2' and c2='2'
    2.回表查询数据,使用where c4='2'来过滤数据
    3.对数据排序输出

    如果使用了ICP优化,其SQL执行步骤为:

    1.使用索引列c1,c2获取满足条件的行数据。where c1='2' and c2='2'
    2.在索引中使用where c4='2'来过滤数据
    3.因为数据有序,直接按顺序取出满足条件的数据

     

    C选项:

    mysql>  explain select c2,c3 from t1 where c1='2' and c4='2' group by c3,c2;
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------------------------------------------+
    | id | select_type | table | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra                                                     |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------------------------------------------+
    |  1 | SIMPLE      | t1    | NULL       | ref  | idx_c1234     | idx_c1234 | 3       | const |    2 |    14.29 | Using where; Using index; Using temporary; Using filesort |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------------------------------------------+

     使用的索引长度为3,代表1个字段使用了索引。根据最左使用原则,c1使用了索引。因为查询中没有c2谓词条件,所以索引值使用到c1后就发生了中断,导致只使用了c1列。该SQL执行过程为:

    1.在c1列使用索引找到c1='2'的所有行,然后回表使用c4='2'过滤掉不匹配的数据
    2.根据上一步的结果,对结果中的c3,c2联合排序,以便于得到连续变化的数据,同时在数据库内部创建临时表,用于存储group by的结果。

    C选项扩展:

    mysql> explain select c2,c3 from t1 where c1='2' and c4='2' group by c2,c3;
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+
    | id | select_type | table | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra                    |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+
    |  1 | SIMPLE      | t1    | NULL       | ref  | idx_c1234     | idx_c1234 | 3       | const |    2 |    14.29 | Using where; Using index |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+

     使用的索引长度为3,代表1个字段使用了索引。根据最左使用原则,c1使用了索引。

     

    D选项:

    mysql> explain select c2,c3 from t1 where c1='2' and c5='2' order by c2,c3;
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+------------------------------------+
    | id | select_type | table | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra                              |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+------------------------------------+
    |  1 | SIMPLE      | t1    | NULL       | ref  | idx_c1234     | idx_c1234 | 3       | const |    2 |    14.29 | Using index condition; Using where |
    +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+------------------------------------+

     使用的索引长度为3,代表1个字段都使用了索引。根据最左使用原则,c1使用了索引。因为查询中没有c2谓词条件,所以索引值使用到c1后就发生了中断,导致只使用了c1列。

    D选项扩展:

    mysql> explain select c2,c3 from t1 where c1='2' and c5=
    
    下一篇:没有了