6.2 索引
为每个页建立一个目录项,包含以下两个部分:
- 页的用户记录中最小的主键值,用key表示;
- 页号,用page_no表示。
InnoDB复用了之前存储用户记录的数据页来存储目录项,记录头信息中的record_type属性:
- 0:普通用户记录
- 1:目录项记录
- 2:Infimum记录
- 3:Supremum记录
注意区分目录项记录和普通用户记录:
而当目录项记录很多的时候,就需要再在上层建立一个目录项:
这种组织形式类似B+树,真正存放用户记录的其实是B+树的叶子节点,其余用来存放目录项记录的节点为非叶子节点/内节点。
聚簇索引
B+树本身就是一个目录(本身就是一个索引),有如下两个特点:
- 使用记录主键值进行记录和页的排序:
- 页内记录按照主键大小顺序排程一个单向链表,页内记录被划分为若干组,每组主键值最大的记录在页内的偏移量会作为槽存在Page Directory中。
- 各个存放用户记录的页也根据用户记录的主键大小顺序,排成一个双向链表。
- 存放目录项记录的页分为不同层级,同一层级中的页也根据页中目录项记录的主键大小顺序排成双向链表。
- B+树的叶子节点存储的是完整的用户记录(即存储了所有列的值,包括隐藏列)。
在InnoDB中存储引擎会自动创建聚簇索引,而且聚簇索引就是数据的存储方式。
二级索引
聚簇索引只在搜索条件是主键时才发挥作用。如果想以别的列为搜索条件,就可以多建几个B+树,并且不同B+树中的数据采用不同的排序规则。
此时的B+树叶子节点存储的就不是完整的用户记录了,而是索引列+主键这两个列的值。
在查询时,还需要进行回表操作,因为以非主键列的大小为排序规则的B+树需要执行回表操作才可以定位到完整的用户记录。通过二级索引记录携带的主键信息,到聚簇索引中重新定位完整的用户记录。
联合索引
也可以同时以多个列的大小作为排序规则,即同时为多个列建立索引。
其本质也是一个二级索引,因为每条就像都是按照建立索引的顺序来排序。
InnoDB中B+树索引的注意事项
- 根页面从创建之初便不动
实际上B+树的形成过程如下:
每当为某个表创建一个B+树索引(聚簇索引不是人为创建,默认存在),都会为该索引创建一个根节点页面。最开始表中没有数据时,每个B+树对应的根节点中既没有用户记录,也没有目录项记录;
随后向表中插入用户记录时,先把用户记录存储到该根节点中;
在根节点中的可用空间用完后继续插入记录,此时会将根节点中的所有记录复制到一个新分配的页,然后对该新页进行页分裂操作,得到另一个新页。此时新插入的记录根据键值大小分配到这两个页中。根节点此时升级为存储目录项记录的页。
在此过程中,一个B+树索引的根节点自从创建开始就不会再移动(即页号不再改变)。这样只要建立一个索引,那么它的根节点的页号就会被记录到某个地方,后续用到该索引时,都会从那个固定的地方取出根节点页号,从而访问该索引。
- 内节点中目录项记录的唯一性
二级索引内节点的目录项记录实际包含主键值,所以内节点中目录项记录肯定是具有唯一性的:
- 一个页面至少容纳2条记录
创建和删除索引的语句
//创建表时建立索引
CREATE TABLE 表名(
各个列信息...
(KEY|INDEX) 索引名 (需要被索引的单个列或多个列)
)
//修改表结构时建立索引
ALTER TABLE 表名 ADD (INDEX|KEY) 索引名 (需要被索引的单个列或多个列);
//修改表结构时删除索引
ALTER TABLE 表名 DROP (INDEX|KEY) 索引名;