• 一个sql中的一张表最多只会走一个索引吗?
  • 发布于 2个月前
  • 310 热度
    0 评论
先给结论
先说结论,是的。一个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中的一张表,最多只会走一个索引。
用户评论