索引是一种利用某种规则的数据结构与实际数据的关系加快数据查找的功能。我们的数据库中存储有大量的内容,而索引能够通过数据节点,根据特定的规则和算法快速查找到节点对应的实际文件的位置。简单来说索引就像书的目录,能够帮助我们准确定位到书籍具体的内容。
最近在学习索引的时候遇到了一个问题,下面我们通过重现的方式来看一下。
首先建立一个如下测试表:
CREATE TABLE `simple_table` ( `id` int NOT NULL AUTO_INCREMENT, `c1` datetime DEFAULT NULL, `c2` datetime DEFAULT NULL, PRIMARY KEY ( `id`), KEY `c2__idx` ( `c2`), KEY `fun_c1_idx` (( cast( `c1` as date))) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
fun_c1_idx: 是 mysql8 开始支持的函数索引
然后往这个表里随机插入 1000 条数据。
select * from simple_table where date(c2) = '2022-01-01';
可以看到上面的这条 SQL 语句不能走索引。因为索引树中存储的是列的实际值和主键值,所以对条件字段做函数操作是会让索引失效的。简单来说就是,如果拿 ‘2022-01-01’ 去匹配,将无法定位到索引树中的值。因此正确选择是放弃走索引,选择全表扫描。
我们再看下一条 SQL。
select id,c2 from simple_table where date(c2) = '2022-01-01';
与第一条不同,这条 SQL 只返回了部分列,而且这些列都在索引中了。然后我们用 explain 分析一下这条 SQL 的执行计划,判断它能否走索引:

上图可以明显看到 key 值为
c2__idx
,即走了索引。
这里就很奇怪,不是说对条件字段做函数操作是会让索引失效吗,为什么这里又走了索引?
这就是我当时在学习时遇到的问题,后来我发现是因为我没有搞清楚 “走索引” 的意思。大家都知道索引能加快查询,但是索引能加快查询的原因你知道么?答案是减少了查询的次数。
现在我们回到上面的 SQL,可以看到虽然 key 值为
c2__idx
,但是 rows 值为 1000。也就是扫描了扫描全表,即
c2__idx
的所有记录。但是由于
c2__idx
已经包含了所有需要查询的列,优化器才选择了走这个索引。
最后再来思考一个问题,使用了索引是否一定快?这个问题我们通过一个具体例子看一下:
select * from simple_table; select * from simple_table where id > 0;
