索引,启动!!
- 各个数据页可以组成一个
双向链表
- 每个数据页中的记录会按照主键值从小到大的顺序组成一个
单向链表
- 每个数据页都会为存储在它里边儿的记录生成一个
页目录
(槽) - 通过主键查找某条记录的时候可以在
页目录
中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
创建一个表
CREATE TABLE index_demo(
c1 INT,
c2 INT,
c3 CHAR(1),
PRIMARY KEY(c1)
ROW_FORMAT = Compact;
省略行格式中的一些信息
一个简单的索引方案
回到正题,我们在根据某个搜索条件查找一些记录时为什么要遍历所有的数据页呢?因为各个页中的记录并没有规律,我们并不知道我们的搜索条件匹配哪些页中的记录,所以 不得不 依次遍历所有的数据页。所以如果我们想快速的定位到需要查找的记录在哪些数据页中该咋办?还记得我们为根据主键值快速定位一条记录在页中的位置而设立的页目录么?我们也可以想办法为快速定位记录所在的数据页而建立一个别的目录,建这个目录必须完成下边这些事儿:
再插入一条记录
页分裂
页与页之间不一定是连续的
再插入数据
记录页与页之间的目录
- key:主键最小值
- page_no:页号
- 通过二分可以定位数据所在的页
那么我们改用什么形式来储存目录项呢,答案也是页
为了区分数据页和目录页中的数据就要用的头信息中的record_type
目录项记录
的record_type
值是1,而普通用户记录的record_type
值是0。目录项记录
只有主键值和页的编号两个列,而普通的用户记录的列是用户自己定义的,可能包含很多列,另外还有InnoDB
自己添加的隐藏列(事务指针,回滚指针)。- 还记得我们之前在唠叨记录头信息的时候说过一个叫
min_rec_mask
的属性么,只有在存储目录项记录
的页中的主键值最小的目录项记录
的min_rec_mask
值为1
,其他别的记录的min_rec_mask
值都是0
。
索引递推
当目录页内存不够是该怎么办?
- 为存储该用户记录而新生成了
页31
。 - 因为原先存储
目录项记录
的页30
的容量已满(我们前边假设只能存储4条目录项记录
),所以不得不需要一个新的页32
来存放页31
对应的目录项。 - 同时我们也要记录页30和页32的关系
现在该怎么查找呢
以查找主键值为20
的记录为例:
-
确定
目录项记录
页我们通过
页33
,找到页30
和页32
,又因为页30
表示的目录项的主键值的范围是[1, 320)
,页32
表示的目录项的主键值 不小于320
,所以主键值为20
的记录对应的目录项记录在页30
中。 -
通过
目录项记录
页确定用户记录真实所在的页。在一个存储
目录项记录
的页中通过主键值定位一条目录项记录(槽) -
在真实存储用户记录的页中定位到具体的记录。
在一个存储用户记录的页中通过主键值定位一条用户记录(槽)
页目录中槽记录的是槽中分组的主键最大值,目录项记录页中数据记录的是主键最小值
B+树
随着数据的增加,B+数就形成了
- 表中页可以称为节点
- 叶节点就是数据页
- 非叶节点就是目录项页
聚簇索引
我们上边介绍的B+
树本身就是一个目录,或者说本身就是一个索引。它有两个特点:
-
使用记录主键值的大小进行记录和页的排序,这包括三个方面的含义:
- 页内的记录是按照主键的大小顺序排成一个单向链表。
- 各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表。
- 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成一个双向链表。
-
B+
树的叶子节点存储的是完整的用户记录。所谓完整的用户记录,就是指这个记录中存储了所有列的值(包括隐藏列)。
我们把具有这两种特性的B+
树称为聚簇索引
二级索引
聚簇索引
只能在搜索条件是主键值时才能发挥作用
二级索引则是搜索条件为其他列时使用
以c2为搜索条件举例,叶节点只存放索引和主键,想查找其他信息需要回表
回表:当我们通过c2列的值定位到主键值时,需要用主键值到聚簇索引
中再查一遍,所以这种B+
树也被称为二级索引
联合索引
我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引,比方说我们想让B+
树按照c2
和c3
列的大小进行排序,这个包含两层含义:
- 先把各个记录和页按照
c2
列进行排序。 - 在记录的
c2
列相同的情况下,采用c3
列进行排序
为c2
和c3
列建立的索引的示意图如下:
如图所示,我们需要注意一下几点:
- 每条
目录项记录
都由c2
、c3
、页号
这三个部分组成,各条记录先按照c2
列的值进行排序,如果记录的c2
列相同,则按照c3
列的值进行排序。 B+
树叶子节点处的用户记录由c2
、c3
和主键c1
列组成。
千万要注意一点,以c2和c3列的大小为排序规则建立的B+树称为联合索引,本质上也是一个二级索引。它的意思与分别为c2和c3列分别建立索引的表述是不同的,不同点如下:
- 建立
联合索引
只会建立如上图一样的1棵B+
树。 - 为c2和c3列分别建立索引会分别以
c2
和c3
列的大小为排序规则建立2棵B+
树。
B+树建立过程
- 建立索引一开始只有一个
根节点
页面,当我们插入数据,根节点是数据页,记录用户数据 - 当我们插入的数据量超过根节点容量时,会创造一个页a并将根节点数据复制过去,然后页a页分裂形成页b和页a(按主键或其他列大小排序),这时根节点页则成为目录项页,记录页a和页b的最小主键数据和页号
- 一个B+树索引的根节点自诞生之日起,便不会再移动。这样只要我们对某个表建立一个索引,那么它的
根节点
的页号便会被记录到某个地方,然后凡是InnoDB
存储引擎需要用到这个索引的时候,都会从那个固定的地方取出根节点
的页号,从而来访问这个索引。
目录项页,记录页a和页b的最小主键数据和页号
- 一个B+树索引的根节点自诞生之日起,便不会再移动。这样只要我们对某个表建立一个索引,那么它的
根节点
的页号便会被记录到某个地方,然后凡是InnoDB
存储引擎需要用到这个索引的时候,都会从那个固定的地方取出根节点
的页号,从而来访问这个索引。