索引,启动!!

索引,启动!!

  • 各个数据页可以组成一个双向链表
  • 每个数据页中的记录会按照主键值从小到大的顺序组成一个单向链表
  • 每个数据页都会为存储在它里边儿的记录生成一个页目录(槽)
  • 通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录

image_1cov976plf2u1j3g1jp8serjc616.png-87.7kB

创建一个表

CREATE TABLE index_demo(
c1 INT,
c2 INT,
c3 CHAR(1),
PRIMARY KEY(c1)
ROW_FORMAT = Compact;

省略行格式中的一些信息

image-20240203205628734

一个简单的索引方案

回到正题,我们在根据某个搜索条件查找一些记录时为什么要遍历所有的数据页呢?因为各个页中的记录并没有规律,我们并不知道我们的搜索条件匹配哪些页中的记录,所以 不得不 依次遍历所有的数据页。所以如果我们想快速的定位到需要查找的记录在哪些数据页中该咋办?还记得我们为根据主键值快速定位一条记录在页中的位置而设立的页目录么?我们也可以想办法为快速定位记录所在的数据页而建立一个别的目录,建这个目录必须完成下边这些事儿:

image_1caaf26411d51bq7jtrvesr04a.png-29.5kB

再插入一条记录

页分裂

image_1caafbcj1qpo1ad2j8q1ci4136s4n.png-44.5kB

页与页之间不一定是连续的

再插入数据

image_1cab9u9midn61fgq1mi58j0gadm.png-65.7kB

记录页与页之间的目录

  • key:主键最小值
  • page_no:页号
  • 通过二分可以定位数据所在的页image_1caba0afo11fa1cli1nu070m16bg1j.png-119.1kB

那么我们改用什么形式来储存目录项呢,答案也是

为了区分数据页和目录页中的数据就要用的头信息中的record_type

image_1caahuomf15m11e5k19v1bf21inq9.png-145.9kB

  • 目录项记录record_type值是1,而普通用户记录的record_type值是0。
  • 目录项记录只有主键值和页的编号两个列,而普通的用户记录的列是用户自己定义的,可能包含很多列,另外还有InnoDB自己添加的隐藏列(事务指针,回滚指针)。
  • 还记得我们之前在唠叨记录头信息的时候说过一个叫min_rec_mask的属性么,只有在存储目录项记录的页中的主键值最小的目录项记录min_rec_mask值为1,其他别的记录的min_rec_mask值都是0

索引递推

当目录页内存不够是该怎么办?

image_1cacabsrh17a5133q1otf725gi92q.png-135.7kB

  • 为存储该用户记录而新生成了页31
  • 因为原先存储目录项记录页30的容量已满(我们前边假设只能存储4条目录项记录),所以不得不需要一个新的页32来存放页31对应的目录项。
  • 同时我们也要记录页30和页32的关系

image_1cacafpso19vpkik1j5rtrd17cm3a.png-158.1kB

现在该怎么查找呢

以查找主键值为20的记录为例:

  1. 确定目录项记录

    我们通过页33,找到页30页32,又因为页30表示的目录项的主键值的范围是[1, 320)页32表示的目录项的主键值 不小于320,所以主键值为20的记录对应的目录项记录在页30中。

  2. 通过目录项记录页确定用户记录真实所在的页。

    在一个存储目录项记录的页中通过主键值定位一条目录项记录(槽)

  3. 在真实存储用户记录的页中定位到具体的记录。

    在一个存储用户记录的页中通过主键值定位一条用户记录(槽)

页目录中槽记录的是槽中分组的主键最大值,目录项记录页中数据记录的是主键最小值

B+树

随着数据的增加,B+数就形成了

image_1ca80gps314u9121u1rdp9r7md8cm.png-55.6kB

  • 表中页可以称为节点
  • 叶节点就是数据页
  • 非叶节点就是目录项页

聚簇索引

我们上边介绍的B+树本身就是一个目录,或者说本身就是一个索引。它有两个特点:

  1. 使用记录主键值的大小进行记录和页的排序,这包括三个方面的含义:

    • 页内的记录是按照主键的大小顺序排成一个单向链表。
    • 各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表。
    • 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成一个双向链表。
  2. B+树的叶子节点存储的是完整的用户记录。

    所谓完整的用户记录,就是指这个记录中存储了所有列的值(包括隐藏列)。

我们把具有这两种特性的B+树称为聚簇索引

二级索引

聚簇索引只能在搜索条件是主键值时才能发挥作用

二级索引则是搜索条件为其他列时使用

image_1cactc8jg14j91likvmd1h8cn3o4h.png-161.6kB

以c2为搜索条件举例,叶节点只存放索引和主键,想查找其他信息需要回表

回表:当我们通过c2列的值定位到主键值时,需要用主键值到聚簇索引中再查一遍,所以这种B+树也被称为二级索引

联合索引

我们也可以同时以多个列的大小作为排序规则,也就是同时为多个列建立索引,比方说我们想让B+树按照c2c3列的大小进行排序,这个包含两层含义:

  • 先把各个记录和页按照c2列进行排序。
  • 在记录的c2列相同的情况下,采用c3列进行排序

c2c3列建立的索引的示意图如下:

image_1d80rmun21al711ok1tvo1i161rnpp.png-172.2kB

如图所示,我们需要注意一下几点:

  • 每条目录项记录都由c2c3页号这三个部分组成,各条记录先按照c2列的值进行排序,如果记录的c2列相同,则按照c3列的值进行排序。
  • B+树叶子节点处的用户记录由c2c3和主键c1列组成。

千万要注意一点,以c2和c3列的大小为排序规则建立的B+树称为联合索引,本质上也是一个二级索引。它的意思与分别为c2和c3列分别建立索引的表述是不同的,不同点如下:

  • 建立联合索引只会建立如上图一样的1棵B+树。
  • 为c2和c3列分别建立索引会分别以c2c3列的大小为排序规则建立2棵B+树。

B+树建立过程

  • 建立索引一开始只有一个根节点页面,当我们插入数据,根节点是数据页,记录用户数据
  • 当我们插入的数据量超过根节点容量时,会创造一个页a并将根节点数据复制过去,然后页a页分裂形成页b和页a(按主键或其他列大小排序),这时根节点页则成为目录项页,记录页a和页b的最小主键数据和页号
  • 一个B+树索引的根节点自诞生之日起,便不会再移动。这样只要我们对某个表建立一个索引,那么它的根节点的页号便会被记录到某个地方,然后凡是InnoDB存储引擎需要用到这个索引的时候,都会从那个固定的地方取出根节点的页号,从而来访问这个索引。

目录项页,记录页a和页b的最小主键数据和页号

  • 一个B+树索引的根节点自诞生之日起,便不会再移动。这样只要我们对某个表建立一个索引,那么它的根节点的页号便会被记录到某个地方,然后凡是InnoDB存储引擎需要用到这个索引的时候,都会从那个固定的地方取出根节点的页号,从而来访问这个索引。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值