天天看点

最左匹配原则

为什么会有这个 最左匹配原则?

答 : mysql 底层使用的索引是 B+ 树 , B+ 树的存放方式是 从左到右依次有序【特定的结构】,我们写sql时用到联合索引,按照索引的数据结构,按照特定的方式写查询 sql 的条件,最大化的提高查询速度。

最左匹配原则是什么?

简单来讲:在联合索引中,只有左边的字段被用到,右边的才能够被使用到。

左边是带头大哥, 必须在

假如我们创建联合索引 create index idx_a_b on shopTable(a,b);

有如下B+树

最左匹配原则

我们看到 最左边的a 都是有序的,分别是 : 1,1,2,2,3,3 但是右边的b 不一定有序: 1,2,1,4,3,2

但是在a都为 1 的情况下 b是有序的, 如: a=1时 b =1,2 ; a=2时, b= 1,4; a=3时 ,b=1,2;

如果我们筛选数据的时候, 直接筛选b ,整个就是无序的,需要做全表扫描

如果先a,再b 那么 ,就可以利用树来加快查找速度。

联合索引失效的情形

即不满足最左匹配原则

假如建立如下索引

create index idx_name_age on admin(name,age);

mysql> show index from admin;
+-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| admin |          0 | PRIMARY      |            1 | admin_id    | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| admin |          1 | idx_name_age |            1 | name        | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| admin |          1 | idx_name_age |            2 | age         | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

           
  • name和age都走了索引的情况
mysql> explain select * from admin where name='1' and age=2;
+----+-------------+-------+------------+------+---------------+--------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key          | key_len | ref         | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | admin | NULL       | ref  | idx_name_age  | idx_name_age | 44      | const,const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
           
  • 单查age , 未用到索引
mysql> explain select * from admin where age=2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | admin | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
           
  • 单查name, 用到了索引
mysql> explain select * from admin where name='1';
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | admin | NULL       | ref  | idx_name_age  | idx_name_age | 39      | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
           

可以看到, 使用了左边字段 name查 ,可以使用索引, 用了右面的age查,无法用到索引

这里只列举这一种索引失效的情况 ,其余还有: 范围值之后失效

  • 这种情况下: 由于name使用了范围索引, 导致后面的age 没有走索引
mysql> explain select * from admin where name > '1' and age = 1;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | admin | NULL       | range | idx_name_age  | idx_name_age | 39      | NULL |    1 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)