cmu 15-445 课程笔记:B+索引

143 阅读6分钟

table indexes

索引是表的部分属性的子集的副本,索引的本质就是个小表,把大表的某些列(针对列创建索引)抽取出来做一个索引表。 数据库要保证索引表和原始表的数据要逻辑上同步,原始表有数据更改,索引表要同步更新。

索引表的目的:加速执行。当执行时,要求选择最好的索引方案,对某列扫描时,可以直接对索引进行扫描,以减少执行时间。

建立索引时,要权衡:索引的存储,以及大表的更新时,索引表同步更新的开销,即存储和维护的成本;和索引减少执行时间的效果;二者之间要进行平衡。

B+ 树

B-树

B树(1971)B+树(1973) B+树是一个自平衡的数据结构,存储数据,在该结构中搜索、顺序查找、插入、删除的时间复杂度O(logn)

O(logn)的含义就是,随着数据的增长,该数据结构上的搜索、插入、删除等操作不是线性增长的,是log函数增长的。

B+ 树是一个二叉树的扩展,对于在磁盘上按整页读写时很有优势。

B+树是一个M-way,父节点可以有M个子节点。每个节点有多个数据。

性质

perfectly balanced(每个叶节点的深度都相同)

每个节点至少是半满的(除了根节点) M/2 - 1 <= keys <= M - 1

每个内部节点,如果有k个数据,就有k+1个非空孩子

例子

image.png

实例网址:www.cs.usfca.edu/~galles/vis…

image.png

最底层存的是就是所有的数据,上层就是一层又一层的索引(类似是多级索引)。

nodes节点

是key/value;

所有节点的key都是我们所选的索引的值(例如某个列:年龄列的值)

对于inner node:value是下一层孩子节点的地址

leaf node: value是行的id(或者地址)

节点中的多个数据都是按照顺序存的。

为什么说B+树对于页存取而言是非常友好的:B+树的节点就可以控制成一个页的大小,统一逻辑单位和物理单位

leaf nodes

叶子节点间要有指针:链接上下兄弟节点

image.png

如果按页存储:

image.png

image.png

另一种存储方法:

image.png

leaf node values

方法一:Record IDs。行的id,例如指向该年龄的那一行,拿到id后需要由一定的机制找到该行 PostgreSQL、SQL server、DB2、Oracle

方法二:Tuple Data。value是该行的整个行数据(主键索引),二级索引必须存储Record ID SQLite、SQL server、MySQL、Oracle

image.png

B树 VS B+树

B树中间节点也都是数据,B+树中间节点只是索引,叶节点是数据

B树空间上高效。

B+树的搜索

支持等值索引

image.png

图示:两列的联合索引(列1,列2),例如搜索(A,*),针对此搜索,尽量确定前面的值(如果后面值确定,那得利用跳跃搜索了,但并不是所有数据库都支持跳跃索引)

image.png

B+树的插入

找到插入叶子节点;插入节点,可能会涉及节点的分裂;

image.png

B+树的删除

找到删除的叶节点,删除节点,如果叶子节点过少,可能会涉及叶子节点的合并(有些数据库对B+树的维护会延迟,防止删除完后,后续会有插入,导致树的合并和删除操作过多)

image.png

B+树的重复值(duplicate keys)

方案一:对key上加上record ID 本身value可以是Record ID,此时把key和Record ID合并起来作为key。

image.png

方案二:节点溢出。外链接一个存储重复值的节点

image.png

Clustered Indexes

聚集索引(聚簇索引)。数据是否按照主键顺序进行存储的,行的数据是否存储到索引的叶节点中。

image.png

非聚簇索引:存个指针,所以数据逻辑顺序和物理顺序是乱的

image.png

扫描遍历时,比较耗时,优化方法是事先缓存一下所需数据所在页。

image.png

B+TREE DESIGN CHOICES

Node size

磁盘存取的时间越长,应当将B+树节点内数据放的越大(即size越大)。争取一次存取处理大量数据。 当存取能力越强,节点大小可以越小

现在,对于MySQL而言,节点大小设定成页大小。而每个节点的大小也取决于工作负载。叶子节点的遍历较多时,B+树节点可以设置比较大一些;点查询比较多时,可以设定节点大小较小。AP型选大,TP型选小。

合并阈值

延迟合并的操作:可以降低B+树组织的时间,频繁合并导致性能较差(因为合并完可能又得拆分)

变长的keys

当字符串作为索引时,可能是变长的varchar。但是节点大小应该是定长,如何存储。

方法一:存储指针。存放指向变长值的指针,确保key一样长

方法二:B+树的节点本身的key就是变长的节点(不建议,会跟页大小不对应)

方法三:padding,补充长度,强制让数据定长

方法四:key Map/Indirection 节点存储的是一堆slot,每个slot也是一个指针(没听太明白)

另外方案:参考MySQL中一个页中的数据不可以超过一个页的一半大小,也即数据大小其实是可以变化的。所以可以还是以页为节点大小,对于每个数据大小不限定,多大都可以存,不过节点就是一个页大小,存满就不再继续存了。

Intra-node search 节点内部的扫描

节点内部的数据很多,页大小可能是16k,一行数据也就是几百字节,所以一个页(节点)可能存几百上千条数据。 例如:

image.png

方法一:线性扫描(Linear)。相比把叶子节点从磁盘上存取的时间,全页(节点)扫描的时间开销很小了

方法二:二分查找。页内的数据本身就是有序的,所以可以执行二分查找

方法三:推断(找规律)从最小值4,最大值为10的七个数据中找8

image.png

B+树的优化方案

前缀压缩

image.png

重复冗余处理

冗余的key进行压缩 image.png

按批插入

B+树在一个一个插入数据的时候,效率比较低下。可以选择将插入数据,组成一个批次(多个数据),先将该批次的所有数据创建一个B+树,再将该B+树和主B+树merge起来

image.png

总结

B+树几乎是对于做DBMS索引而言的必定选择