0. 单页查询
记录在InnoDB中的组织方式为:
- 在一个页中以主键为搜索条件的查找过程为:在页目录(Page Directory)中使用二分法快速定位到对应的槽(Slot),然后遍历该槽对应的分组中的记录即可快速找到指定的目录。
- 以其他列作为搜索条件的查找则需从最小记录开始依次遍历单链表中的每条记录,并对比其是不是符合搜索条件。
1. 多页查找
那如何在多页中查找呢?
在多页中查找记录分两个步骤:
1.定位到记录所在的页(用到索引)
2.从所在的页中查找相应的记录(如前文part0所述)
记录行格式表示简化如下:
记录放在页中的示意图为:
为所有数据页建立一个目录(索引)需满足:
- 下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值,保证这一状态始终成立的操作过程称为页分裂。
- 每个页对应一个目录项,这些项形成目录。每个目录项包括:页的用户记录中最小的主键值(key表示),页号(page_no表示)。
注:数据页的编号不一定是连续的。
2. InnoDB中的索引方案
为了方便索引的增删改查,以灵活管理所有目录项,InnoDB复用了存储用户记录的数据页来存储目录项,并通过记录头信息里的record_type属性做区分,这些用来表示目录项的记录称为目录项记录。
目录项记录和普通用户记录的不同点:
- 目录项记录的record_type 值是1,而普通用户记录的record_type 值是0。
- 目录项记录只有主键值和页的编号两个列,而普通的用户记录的列是用户自己定义的,可能包含很多列,另外还有InnoDB 自己添加的隐藏列。
- min_rec_mask属性只有在存储目录项记录的页中的主键值最小的目录项记录的min_rec_mask 值为1 ,其他别的记录的min_rec_mask 值都是0 。
当表中的数据非常多,会产生很多存储目录项记录的页,同样为了快速查找,我们将它们组织为:
这种组织形式的数据结构为B+树。
聚簇索引为满足下列两种特性的B+树:
- 使用记录主键值的大小进行记录和页的排序,这包括三个方面的含义:
- 页内的记录是按照主键的大小顺序排成一个单向链表。
- 各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表。
- 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成
一个双向链表。
- B+ 树的叶子节点存储的是完整的用户记录。
所谓完整的用户记录,就是指这个记录中存储了所有列的值(包括隐藏列)。
InnoDB会为表自动创建聚簇索引,聚簇索引(用户记录都存储在了叶子节点)也是数据的存储方式,也就是索引即数据,数据即索引。
3. 二级索引(Secondary index)
聚簇索引只能在搜索条件是主键值时才起作用,对以别的列作为搜索条件的查找,我们可以多建几棵B+树,如以第二列值的大小作为数据页、页中记录的排序规则。
这个B+ 树与上边介绍的聚簇索引有几处不同:
使用记录c2 列的大小进行记录和页的排序,这包括三个方面的含义:
- 页内的记录是按照c2 列的大小顺序排成一个单向链表。
- 各个存放用户记录的页也是根据页中记录的c2 列大小顺序排成一个双向链表。
- 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的c2 列大小顺序排成一个双向链表。
- B+ 树的叶子节点存储的并不是完整的用户记录,而只是c2列+主键这两个列的值。
- 目录项记录中不再是主键+页号的搭配,而变成了c2列+页号的搭配。
所以如果我们现在想通过c2 列的值查找某些记录的话就可以使用我们刚刚建好的这个B+ 树了。以查找c2 列的值为4 的记录为例,查找过程如下:
- 确定目录项记录页
根据根页面,也就是页44 ,可以快速定位到目录项记录所在的页为页42 (因为2 < 4 < 9 )。 - 通过目录项记录页确定用户记录真实所在的页。
在页42 中可以快速定位到实际存储用户记录的页,但是由于c2 列并没有唯一性约束,所以c2 列值为4 的记录可能分布在多个数据页中,又因为2 < 4 ≤ 4 ,所以确定实际存储用户记录的页在页34 和页35中。 - 在真实存储用户记录的页中定位到具体的记录。
到页34 和页35 中定位到具体的记录。 - 但是这个B+ 树的叶子节点中的记录只存储了c2 和c1 (也就是主键)两个列,所以我们必须再根据主键值去聚簇索引中再查找一遍完整的用户记录。这个过程称为回表。
联合索引
先把各个记录和页按照c2列进行排序,然后在记录c2列相同的情况下,再按c3列进行排序。本质上也是一个二级索引,即以c2和c3列的大小为排序规则建立B+树。
注:
- 一个B+树索引的根节点自诞生之日起,便不会再移动。
- 实际上,需要保证在B+树的同一层内节点的目录项记录除页号这个字段以外是唯一的。二级索引的内节点的目录项记录的内容实际上是由三个部分构成的:索引列的值、主键值、页号。
- 一个页面最少存储2条记录
CREATE TALBE 表名 ( 各种列的信息 ··· , [KEY|INDEX] 索引名 (需要被索引的单个列或多个列) )