5 深入浅出索引(下)
5 深入浅出索引(下)
上一篇介绍了innodb索引的数据结构模型,这一篇将继续索引有关的概念
create table T5 ( ID int primary key, k int NOT NULL DEFAULT 0, s varchar(16) NOT NULL DEFAULT '', index k(k)) engine=InnoDB; insert into T5 values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');
执行语句select * from T5 where k BETWEEN 3 and 5;
需要执行几次索引的搜索操作,会扫描多少行
先看索引的结构
分析sql语句的执行流程:
--1 在k索引树上找到k=3的记录,取得id=300
--2 再到id索引树上查到id=300对应的R3
--3 在k索引上找到k=5的记录,取得id=500
--4 再回到id索引找到id=500的对应值R4
--5 在k索引上取下一个值k=6,不满足条件,循环结束。
在这个过程中,回到主键索引树搜索的过程,称为回表,这个查询过程读了k索引树的3条记录,回表了两次。
在这个例子中,由于查询结果所需要的数据只在主键索引上有,所以不得不回表,那么,有没有可能经过索引优化,避免回表过程呢?
覆盖索引
执行语句
(system@127.0.0.1:3306) [test]> select id from T5 where k BETWEEN 3 and 5;
这时只需要查询id的值,而id的值已经在k索引树上,可以直接提供查询结果,不需要回表,这个查询里,索引k已经”覆盖了”我们的查询需求,称为覆盖索引
由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的优化手段。
在讨论一个问题,在一个市民信箱表上,是否有必须要将身份证和名字建立联合索引?
CREATE TABLE `tuser` ( `id` int(11) NOT NULL, `id_card` varchar(32) DEFAULT NULL, `name` varchar(32) DEFAULT NULL, `age` int(11) DEFAULT NULL, `ismale` tinyint(1) DEFAULT NULL, PRIMARY KEY (`id`), KEY `id_card` (`id_card`), KEY `name_age` (`name`,`age`) ) ENGINE=InnoDB
现在有一个高频需求,根据市民的身份证号查询他的姓名,建立一个联合索引(身份证号、姓名),在这个查询上用到覆盖索引,不需要在回表查整行记录,减少语句的执行时间。
索引字段的维护是有代价的,因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑。
最左前缀原则
B+树这种索引结构,可以利用索引的”最左前缀”
前面的(name,age)索引来分析
可以看到,索引项是按照索引定义里面出现字段的顺序排序的。
查询语句” where name like ‘张%’ ”能够用上索引,查询到第一个符合条件的记录是id3,然后向后遍历,直到不满足条件为止。
可以看到,不只是索引的全部定义,只要满足最左前缀,就可以利用索引来加速检索,这个最左前缀可以是联合字段索引的最左N个字段,也可以是字符串索引的最左M个字符。
基于上面对最左前缀索引的说明:在建立联合索引的时候,如何安排索引内的字段顺序。
评估标准是,索引的复用能力,因为可以支持最左前缀,所以当已经有了(a,b)这个联合索引后,一般不需要单独在a上建立索引了。第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。
联合索引(a,b),查询条件只有b的语句,是无法使用该索引的,得新建一个(b)的索引才能使用。
索引下推
前面说到满足最左前缀原则的时候,最左前缀可以用于在索引中定位记录
mysql> select * from tuser where name like '张 %' and age=10 and ismale=1;
Mysql 5.6引入了索引下推优化(index condition pushdown),可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
思考题
表结构定义如下
CREATE TABLE `geek` ( `a` int(11) NOT NULL, `b` int(11) NOT NULL, `c` int(11) NOT NULL, `d` int(11) NOT NULL, PRIMARY KEY (`a`,`b`), KEY `c` (`c`), KEY `ca` (`c`,`a`), KEY `cb` (`c`,`b`) ) ENGINE=InnoDB;
由于历史原因表需要ab做联合主键,ca和cb两个索引的查询
select * from geek where c=N order by a limit 1;
select * from geek where c=N order by b limit 1;
分析联合索引ca,cb和索引c之间的合理性
--添加索引c
--添加索引cb
分析
表记录
--a--|b--|c--|d
主键ab的聚簇索引组织顺序相当于order by a,b,也就是先按a排序,再按b排序,c无序
索引ca的组织是先按照c排序,再按照a排序,同时记录主键
--c--|a--|主键部分b(注意这里不是ab,而是只有b)
这个跟索引c的数据是一样的
索引cb是先c排序,再按b排序,同时记录主键
--c--|b--|主键部分a--
所以,索引ca可以去掉,cb可以保留
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构