前面我们讲解了索引的存储结构,B+Tree的索引结构,以及索引最左侧匹配原则,Explain的用法,今天我们来实战一下 最左侧匹配原则
联合索引有一个最左侧匹配原则
最左匹配原则指的是,当使用联合索引进行查询时,MySQL会优先使用最左边的列进行匹配,然后再依次向右匹配。
假设我们有一个表,包含三个列:A、B、C
创建联合索引(A,B,C) 等同于创建了索引 A, 索引 (A,B), 索引 (A,B,C)
新建表结构 user, user_info
#新建表结构 user
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`id_card` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '身份证ID',
`user_name` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名字',
`age` int NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'
先插入测试数据, 插入 5条测试数据
INSERT INTO `test`.`user` (`id`, `id_card`, `user_name`, `age`) VALUES (1, '11', 'aa', 10);
INSERT INTO `test`.`user` (`id`, `id_card`, `user_name`, `age`) VALUES (2, '22', 'bb', 20);
INSERT INTO `test`.`user` (`id`, `id_card`, `user_name`, `age`) VALUES (3, '33', 'cc', 30);
INSERT INTO `test`.`user` (`id`, `id_card`, `user_name`, `age`) VALUES (4, '44', 'dd', 40);
INSERT INTO `test`.`user` (`id`, `id_card`, `user_name`, `age`) VALUES (5, '55', 'ee', 50);
alter table user add index idx_card_name_age(id_card,user_name,age);
EXPLAIN SELECT * FROM `user` where user_name = "aa";
EXPLAIN SELECT * FROM `user` where age = 10;
EXPLAIN SELECT * FROM `user` where user_name = "aa" and age = 10;
上面我们看到了只要查询语句中不包含A的字段信息,所有的索引全都不生效,扫描全部索引信息,这不是我们想要的
这也就是最左侧匹配原则导致的,所以我们在查询的时候,一定要从最左侧开始查询,也就是查询语句一定要有A查询条件,否则索引不生效
EXPLAIN SELECT * FROM `user` where id_card = "11" ;
EXPLAIN SELECT * FROM `user` where id_card = "11" and user_name = "aa" ;
EXPLAIN SELECT * FROM `user` where id_card = "11" and user_name = "aa" ;
EXPLAIN SELECT * FROM `user` where id_card = "11" and user_name = "aa" and age =10 ;
EXPLAIN SELECT * FROM `user` where age =10 and user_name = "aa" and id_card = "11" ;
EXPLAIN SELECT * FROM `user` where age =10 and user_name = "aa" and id_card = "11" ;
我们可以通过 explain key_len计算到底使用了那个索引字段
通过刚才的验证,我们了解不同的索引,使用的ken_len长度不同,到底这个key_len如何计算,我们如何知道到底用了那个索引?
然后 看下表结构
id_card notNull
user_name 允许null
age 允许null
然后开始计算 ken_len的长度
EXPLAIN SELECT * FROM `user` where id_card = "11" ;
使用了 id_card 单个字段的索引
key_len
= (char(32)) 4 + (notNull)0 + (char)0
= 324 +0 +0 = 128
EXPLAIN SELECT * FROM `user` where user_name = "aa" and id_card = "11" ;
使用了 id_card 和 user_name 2个字段的索引, user_name允许为null +1,
key_len
= (char(32)) * 4 + (notNull)0 + (char)0 + (char(32)) 4 + (Null)1 + (char)0
= 324 + 32*4 +1 = 257
EXPLAIN SELECT * FROM `user` where user_name = "aa" and id_card = "11" and age =10;
使用了 id_card 和 user_name 及 age 三个字段的索引, user_name允许为null +1, age允许为null +1, age类型为int,占4位
key_len
= (char(32)) * 4 + (notNull)0 + (char)0 + (char(32)) 4 + (Null)1 + (char)0 + (int)4 + (Null)1 + (int)0
= 324 + 32*4 +1 + 5= 262
没有用到某个字段的索引,ken_len不会计算它的长度,比如A,C列的查询 id_card和age的查询,不会用到age的索引,只用到了id_card,key_len只会计算 id_card的长度
EXPLAIN SELECT * FROM `user` where id_card = "11" and age=10 ;
key_len = (char(32)) * 4 + (notNull)0 + (char)0 = 128, 只用到了id_card的索引信息
至此,我们了解了联合索引的最左侧匹配原则,也知道了如何去优化查询语句,才能使用到索引,并且知道了key_len分析具体使用了那些索引