联合索引最左匹配原则

什么时候创建组合索引?

当我们的where查询存在多个条件查询的时候,我们需要对查询的列创建组合索引

为什么不对每一列创建索引

  • 减少开销
  • 覆盖索引
  • 效率高

减少开销:假如对col1、col2、col3创建组合索引,相当于创建了(col1)、(col1,col2)、(col1,col2,col3)3个索引

覆盖索引:假如查询SELECT col1, col2, col3 FROM 表名,由于查询的字段存在索引页中,那么可以从索引中直接获取,而不需要回表查询

效率高:对col1、col2、col3三列分别创建索引,MySQL只会选择辨识度高的一列作为索引。假设有100w的数据,一个索引筛选出10%的数据,那么可以筛选出10w的数据;对于组合索引而言,可以筛选出100w10%10%*10%=1000条数据

最左匹配原则

只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合,所以在建立联合索引的时候查询最频繁的条件要放在左边

假设我们创建(col1,col2,col3)这样的一个组合索引,那么相当于对col1列进行排序,也就是我们创建组合索引,以最左边的为准,只要查询条件中带有最左边的列,那么查询就会使用到索引

创建测试表

CREATE TABLE `student` (
  `id` int(11) NOT NULL,
  `name` varchar(10) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_id_name_age` (`id`,`name`,`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

填充100w测试数据

DROP PROCEDURE pro10;
CREATE PROCEDURE pro10()
BEGIN
    DECLARE i INT;
    DECLARE char_str varchar(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    DECLARE return_str varchar(255) DEFAULT '';
    DECLARE age INT;
    SET i = 1;
    WHILE i < 5000000 do
        SET return_str = substring(char_str, FLOOR(1 + RAND()*62), 8);
        SET i = i+1;
        SET age = FLOOR(RAND() * 100);
        INSERT INTO student(id, name, age) values(i, return_str, age);
    END WHILE;
END;
CALL pro10();

场景测试

EXPLAIN SELECT * FROM student WHERE id = 2;
可以看到该查询使用到了索引

 

EXPLAIN SELECT * FROM student WHERE id = 2 AND name = 'defghijk';
可以看到该查询使用到了索引

 

EXPLAIN SELECT * FROM student WHERE id = 2 AND name = 'defghijk' and age = 8;
可以看到该查询使用到了索引

 

EXPLAIN SELECT * FROM student WHERE id = 2 AND age = 8;
可以看到该查询使用到了索引

 

EXPLAIN SELECT * FROM student WHERE name = 'defghijk' AND age = 8;
可以看到该查询没有使用到索引,类型为index,查询行数为4989449,几乎进行了全表扫描,由于组合索引只针对最左边的列进行了排序,对于name、age只能进行全部扫描

 

EXPLAIN SELECT * FROM student WHERE name = 'defghijk' AND id = 2;
EXPLAIN SELECT * FROM student WHERE age = 8 AND id = 2;
EXPLAIN SELECT * FROM student WHERE  name = 'defghijk' and age = 8 AND id = 2;
可以看到如上查询也使用到了索引,id放前面和放后面查询到的结果是一样的,MySQL会找出执行效率最高的一种查询方式,就是先根据id进行查询

 

总结

如上测试,可以看到只要查询条件的列中包含组合索引最左边的那一列,不管该列在查询条件中的位置,都会使用索引进行查询。下图的查询我的meta_key和另一个字段meta_id有联合索引,meta_value是普通字段,可以看到是使用了

 

 

 

 

引申

CREATE TABLE `student` (
  `id` int(11) NOT NULL,
  `col1` varchar(10) NOT NULL,
  `col2` varchar(10) NOT NULL,
	`col3` varchar(10) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_id_name_age` (`col1`,`col2`,`col3`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

对于联合索引(col1,col2,col3),查询语句SELECT * FROM test WHERE col2=2;是否能够触发索引?
大多数人都会说NO,实际上却是YES。

原因:

  1. EXPLAIN SELECT * FROM test WHERE col2=2;
  2. EXPLAIN SELECT * FROM test WHERE col1=1;

观察上述两个explain结果中的type字段。查询中分别是:

  • type: index
  • type: ref

index:这种类型表示mysql会对整个该索引进行扫描。要想用到这种类型的索引,对这个索引并无特别要求,只要是索引,或者某个联合索引的一部分,mysql都可能会采用index类型的方式扫描。但是呢,缺点是效率不高,mysql会从索引中的第一个数据一个个的查找到最后一个数据,直到找到符合判断条件的某个索引。所以,上述语句会触发索引。
ref:这种类型表示mysql会根据特定的算法快速查找到某个符合条件的索引,而不是会对索引中每一个数据都进行一一的扫描判断,也就是所谓你平常理解的使用索引查询会更快的取出数据。而要想实现这种查找,索引却是有要求的,要实现这种能快速查找的算法,索引就要满足特定的数据结构。简单说,也就是索引字段的数据必须是有序的,才能实现这种类型的查找,才能利用到索引。

 

最后来点例子:

  • where a=1 只使用了索引 a;
  • where a=1 and b=2 只使用了索引 a,b;
  • where a=1 and b=2 and c=3 使用a,b,c;
  • where b=1 or where c=1 不使用索引;
  • where a=1 and c=3 只使用了索引 a;
  • where a=3 and b like 'xx%' and c=3 只使用了索引 a,b。

 

 

 

最左匹配原则:

在mysql建立联合索引时会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,示例:
对列col1、列col2和列col3建一个联合索引

  1. KEY test_col1_col2_col3 on test(col1,col2,col3);

联合索引 test_col1_col2_col3 实际建立了(col1)、(col1,col2)、(col,col2,col3)三个索引。

  1. SELECT * FROM test WHERE col1=“1” AND clo2=“2” AND clo4=“4”

上面这个查询语句执行时会依照最左前缀匹配原则,检索时会使用索引(col1,col2)进行数据匹配。

注意

索引的字段可以是任意顺序的,如:

  1. SELECT * FROM test WHERE col1=“1” AND clo2=“2”
  2. SELECT * FROM test WHERE col2=“2” AND clo1=“1”

这两个查询语句都会用到索引(col1,col2),mysql创建联合索引的规则是首先会对联合合索引的最左边的,也就是第一个字段col1的数据进行排序,在第一个字段的排序基础上,然后再对后面第二个字段col2进行排序。其实就相当于实现了类似 order by col1 col2这样一种排序规则。

有人会疑惑第二个查询语句不符合最左前缀匹配:首先可以肯定是两个查询语句都保函索引(col1,col2)中的col1、col2两个字段,只是顺序不一样,查询条件一样,最后所查询的结果肯定是一样的。既然结果是一样的,到底以何种顺序的查询方式最好呢?此时我们可以借助mysql查询优化器explain,explain会纠正sql语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值