《MySQL是怎样运行的》读书笔记(三) B+树索引

前言

从前面数据存储结构中我们已经知道了页和记录的关系示意图:

其中页a、页b、页c ... 页n 这些页可以不在物理结构上相连,只要通过双向链表相关联即可。

在正式介绍索引之前,我们需要了解一下没有索引的时候是怎么查找记录的。下边先只讨论搜索条件为对某个列精确匹配的情况,即搜索条件中用 = 连接的表达式,比如这样:

SELECT [列名列表] FROM 表名 WHERE 列名 = xxx;

在一个页中的查找

假设目前表中的记录比较少,所有的记录都可以被存放到一个页中,在查找记录的时候可以根据搜索条件的不同分为两种情况:

 以主键为搜索条件

 这个查找过程我们已经很熟悉了,可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。

以其他列作为搜索条件

对非主键列的查找,因为在数据页中并没有对非主键列建立的页目录 ,所以无法通过二分法快速定位相应的槽 。这种情况下只能从最小记录开始依次遍历单链表中的每条记录, 然后对比每条记录是不是符合搜索条件。很显然,这种查找的效率是非常低的。

在很多页中查找

通常情况下表中存放的记录都是非常多的,需要很多的数据页来存储这些记录。在很多页中查找记录可以分为两个步骤:

1. 定位到记录所在的页。

2. 从所在的页内中查找相应的记录。

在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,在每一个页中根据我们刚刚讨论的查找方式去查找指定的记录。因为要遍历所有的数据页,所以这种方式显然是非常耗时的。

因此引出索引的使用。

索引原理

数据准备

先建一个表

该表行结构如下图所示:

在第10页里有3行数据的效果如下:

一个简单的索引方案

按照在一个页中给主键设置页目录的思路,我们也可以想办法为快速定位记录所在的数据页而建立一个别的目录,建这个目录必须满足以下要求:

1. 下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值

下面是原有的表,一页能放三条记录,页10插入3条记录(自动按主键大小排列),该页刚好满

现在插入一条记录 INSERT INTO index_demo VALUES(4, 4, 'a');

新分配的数据页编号可能并不是连续的,也就是说我们使用的这些页在存储空间里可能并不挨着。它们只是通过维护着上一个页和下一个页的编号而建立了链表关系。

另外, 页10 中用户记录最大的主键值是 5 ,而页28 中有一条记录的主键值是 4 ,因为 5 > 4 ,所以这就不符合下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值的要求,所以在插入主键值为 4 的记录的时候需要伴随着一次记录移动,也就是把主键值为 5 的记录移动到页28 中, 然后再把主键值为 4 的记录插入到 页10 中,这个过程的示意图如下:

这个过程表明了在对页中的记录进行增删改操作的过程中,我们必须通过一些诸如记录移动的操作来始终保证这个状态一直成立:下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。这个过程称为页分裂 。

2. 给所有的页建立一个目录项

由于数据页的编号可能并不是连续的,所以在向表中插入许多条记录后,可能是这样的效果:

因为这些 16KB 的页在物理存储上可能并不相邻,所以如果想从这么多页中根据主键值快速定位某些记录所在的页,我们需要给它们做个目录,每个页对应一个目录项,每个目录项包括下边两个部分:

1. 页的用户记录中最小的主键值,我们用 key 来表示。

2. 页号,我们用 page_no 表示。

我们为上边几个页做好的目录就像这样子:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值