目录
先给结论
做实验
1.根据时间范围查询
什么是key_len?
2.根据时间范围和 is_delete 查询
最左匹配原则
2.根据时间范围和 blog_type 查询
如果加上id会怎么样
并不是索引一定会走
1.IN子表数量过多
2.单次查询超过30%
先给结论
先说结论,是的。一个sql中的一张表,最多只会走一个索引!
就拿之前日记本系统的日记表举例:
DROP TABLE IF EXISTS `tbl_syn_blog`;
CREATE TABLE `tbl_syn_blog` (
`id` int NOT NULL,
`title` varchar(100) DEFAULT NULL,
`user_id` int DEFAULT NULL COMMENT '用户ID',
`blog_type` int DEFAULT NULL COMMENT '类型',
`content` blob COMMENT '内容',
`is_delete` varchar(1) DEFAULT '0',
`create_date` varchar(20) DEFAULT NULL,
`update_date` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_blog_main` (`create_date`,`blog_type`,`is_delete`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
这张表有一个普通联合索引idx_blog_main
。 分别是:create_date
, blog_type
, is_delete
。
另外ID是主键
主键肯定有索引。
做实验
1.根据时间范围查询
explain SELECT * from tbl_syn_blog where create_date >= '2023-06-05' and create_date < '2023-06-06'
联合索引走了,key_len=63。
什么是key_len?
在Mysql中执行explain的结果中有一列为key_len,那么key_len的含义是什么呢?
key_len:表示索引使用的字节数,根据这个值可以判断索引的使用情况,特别是在组合索引的时候,判断该索引有多少部分被使用到非常重要。
索引字段的附加信息: 可以分为变长和定长数据类型讨论,当索引字段为定长数据类型时,如char,int,datetime,需要有是否为空的标记,这个标记占用1个字节(对于not null的字段来说,则不需要这1字节);对于变长数据类型,比如varchar,除了是否为空的标记外,还需要有长度信息,需要占用两个字节。
对于,char、varchar、blob、text等字符集来说,key len的长度还和字符集有关,latin1一个字符占用1个字节,gbk一个字符占用2个字节,utf8一个字符占用3个字节。
create_date
字段是这样的
varchar属于变长字段,长度为20,又因为是utf8字符集,根据计算公式,create_date
索引长度 = 20 * 3 + 2 + 1 = 63(utf8每个字符为3bytes,允许为NULL,加1byte,变长数据类型,加2bytes)
2.根据时间范围和 is_delete 查询
EXPLAIN SELECT
*
FROM
tbl_syn_blog
WHERE
create_date >= '2023-06-05'
AND create_date < '2023-06-06'
AND is_delete = '0'
欸??
为啥没变呢,不是多了一个查询条件is_delete吗?
最左匹配原则
所谓最左前缀原则,顾名思义是最左优先,以最左边的为起点任何连续的索引都能匹配上。 create_date
和 is_delete
这两个匹配项能确保联合索引idx_blog_main
被触发,但实际匹配到的只有 create_date
(因为不连续)。
2.根据时间范围和 blog_type 查询
EXPLAIN SELECT
*
FROM
tbl_syn_blog
WHERE
create_date >= '2023-06-05'
AND create_date < '2023-06-06'
AND blog_type = 2
create_date 和 blog_type 一起,符合最左匹配原则,并且是连续的,所以都匹配到了。
INT类型的字段,允许NULL,key_len = 4 + 1 = 5,所以总长度 = 63+5=68。
如果加上id会怎么样
EXPLAIN SELECT
*
FROM
tbl_syn_blog
WHERE
create_date >= '2023-06-05'
AND create_date < '2023-06-06'
AND blog_type = 7
AND id = 3
如图,加上id作为where条件后,key_len直接变成了4,也就是说只有id的索引生效了。
这也说明了,执行器会根据具体的情况,选择最佳的索引去走,一次sql查询中,一张表最多只会走一个索引。
并不是索引一定会走
有一些特殊情况,索引能触发但是不会匹配。
1.IN子表数量过多
EXPLAIN SELECT
*
FROM
tbl_syn_blog
WHERE
create_date >= '2023-06-05'
AND create_date < '2023-06-06'
AND blog_type in (SELECT id from sys_blog_type)
sys_blog_type
表中的id很多,有几十万,那么就导致IN子句查出来的数量很多,会导致blog_type
不走索引。
2.单次查询超过30%
EXPLAIN SELECT
*
FROM
tbl_syn_blog
WHERE 1=1
AND blog_type = 5
AND create_date >= '2023-01-20'
AND create_date < '2023-06-28'
因为上面sql中的日期范围很大,对应数量非常多,超过了总表的30%,那么执行器会认为还不如全表扫描,于是索引就失效了。
还有很多索引失效的场景,这边就不一一赘述了。
总结:一个sql中的一张表,最多只会走一个索引。