深入浅出数据库索引

一、索引的常见模型

1. 哈希表

是键值对(key-value)存储结构,只要根据 key 就可以找到 value。

可以理解为一个数组,对 key 进行哈希计算,换算成一个确定的位置,把 value 放入此位置。

因为存储hash冲突的情况,多个value可能在同一个位置上,使用链表,后来的就追加到链表中。

例如存储身份证号和名字的信息:

深入浅出数据库索引

这种结构只适用于 等值查询 场景,如果要找某个区间的用户就需要全部扫描一遍了。

2. 有序数组

深入浅出数据库索引

按照身份证号递增的顺序保存。

有序数组非常适合 等值查询和范围查询

例如查找 [ID_X, ID_Y] 内的,先用二分法找到 ID_X,然后向右遍历,直到找到大于 ID_Y 的就结束了。

有序数组的优势是查询效率高,缺点是更新麻烦,插入一个记录就需要挪动后面所有的记录,效率低。

有序数组索引只适用于 静态存储引擎 ,不会修改的数据情况。

3. 搜索树

先看最经典的二叉树:

深入浅出数据库索引

二叉树中,左儿子 < 父节点,父节点 < 右儿子。

例如查找 ID_2,路径就是:

UserA -> UserC -> UserF -> User2

二叉树搜索效率最高,但实际数据库却不适用二叉树,因为索引是需要写到磁盘上的,例如一棵100万节点的二叉树,高度是20,一次查询需要访问20个数据块,从磁盘随机读一个数据块需要10ms左右的寻址时间,那么这个查询就需要200ms,很慢了。

所以需要使用N叉树,每个节点可以有多个儿子,儿子间从左到右递增,这样可以让查询过程访问尽量少的数据块儿。

二、InnoDB 索引

1. 为什么使用 B+ tree 索引模型?

例如插入数据:1,a,b,c,d,e。

二叉树的结构:

深入浅出数据库索引

红黑树的结构:

深入浅出数据库索引

B+ tree 的结构:

深入浅出数据库索引

二叉树上面说过,效率太低,高度完全不可控,红黑树优化了不少,但高度仍然不可控,B+ tree 的高度恒定,而且只在叶子节点中存储数据,叶子节点间也是顺序链接的,做范围查找时非常高效。

2. 为什么建议使用自增主键?

涉及到1个概念:页分裂。

插入时,如果新值比现有值都大,那么只需要在后面插入即可,否则需要挪动现有数据。

如果插入目标位置的数据页满了,就要申请新的数据页,并挪动数据。

所以自增ID能够连续申请页空间,提升空间的操作效率和利用率。

三、MySQL 索引的3个重要概念

例如有一个表:

深入浅出数据库索引

有2个索引,ID 和 k。

深入浅出数据库索引

InnoDB 主键索引的叶子节点存储的是行数据,非主键索引的叶子节点存储的是主键。

查询 k 在 [3,5] 区间内的所有行数据:

select * from T where k betweet 3 and 5

在 k 索引树上找到符合条件的,得到 ID 值,回到 ID 索引树取得行数据。

回到主键索引搜索的过程称为 回表

1. 覆盖索引

上面的查询如果改为:

select ID from T where k betweet 3 and 5

只查询ID这个字段,那么只搜索 k 树索引即可,不需要回表了。

索引 k 已经覆盖了我们的查询需求,这种情况称为 覆盖索引

覆盖索引可以显著提升查询性能,所以做优化时可以考虑是否适合使用此方式。

例如用户表,有身份证号、姓名字段,身份证号是唯一的,建了一个索引。

如果业务中有根据身份证号搜索姓名的高频需求,就可以创建一个( 身份证号+姓名 )的联合索引,实现覆盖索引,即使增加了索引的维护成本也是值得的。

2. 最左前缀原则

深入浅出数据库索引

索引项是按照字段顺序排序的。

查询“张三”时,会快速定位到 ID_4,然后向后遍历需要的结果。

查询“张%”也可以用这个索引,定位到 ID_3,向后找,直到不满足条件为止。

所以,只要满足最左前缀就可以用到索引。

涉及到一个问题:如何安排索引内的字段顺序?

需要考虑 索引的复用能力 ,如果通过调整顺序,可以少维护一个索引,那么这个顺序就是优先考虑的。

3. 索引下推

例如用户表有字段:ID,name,age,ismale …,建有一个索引(name,age)。

现在查询:

select * from tuser where name like '张 %' and age=10 and ismale=1;

MySQL5.6 之前,查询过程是:

根据索引找到以“张”开头的名字的ID,然后回表,进一步判断 age 和 ismale。

在 5.6 引入了 索引下推 ,在索引中找到“张”开头的以后,先在索引中判断其 age 是否符合条件,因为 age 已经在索引中,不需要回表判断了,把 age 不符合要求的先过滤掉,然后再回表判断 ismale,这就减少了回表次数,提升了查询效率。

欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 721575865

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

相关推荐