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个非空孩子
例子:
实例网址:www.cs.usfca.edu/~galles/vis…
最底层存的是就是所有的数据,上层就是一层又一层的索引(类似是多级索引)。
nodes节点
是key/value;
所有节点的key都是我们所选的索引的值(例如某个列:年龄列的值)
对于inner node:value是下一层孩子节点的地址
对leaf node: value是行的id(或者地址)
节点中的多个数据都是按照顺序存的。
为什么说B+树对于页存取而言是非常友好的:B+树的节点就可以控制成一个页的大小,统一逻辑单位和物理单位
leaf nodes
叶子节点间要有指针:链接上下兄弟节点
如果按页存储:
另一种存储方法:
leaf node values
方法一:Record IDs。行的id,例如指向该年龄的那一行,拿到id后需要由一定的机制找到该行 PostgreSQL、SQL server、DB2、Oracle
方法二:Tuple Data。value是该行的整个行数据(主键索引),二级索引必须存储Record ID SQLite、SQL server、MySQL、Oracle
B树 VS B+树
B树中间节点也都是数据,B+树中间节点只是索引,叶节点是数据
B树空间上高效。
B+树的搜索
支持等值索引
图示:两列的联合索引(列1,列2),例如搜索(A,*),针对此搜索,尽量确定前面的值(如果后面值确定,那得利用跳跃搜索了,但并不是所有数据库都支持跳跃索引)
B+树的插入
找到插入叶子节点;插入节点,可能会涉及节点的分裂;
B+树的删除
找到删除的叶节点,删除节点,如果叶子节点过少,可能会涉及叶子节点的合并(有些数据库对B+树的维护会延迟,防止删除完后,后续会有插入,导致树的合并和删除操作过多)
B+树的重复值(duplicate keys)
方案一:对key上加上record ID 本身value可以是Record ID,此时把key和Record ID合并起来作为key。
方案二:节点溢出。外链接一个存储重复值的节点
Clustered Indexes
聚集索引(聚簇索引)。数据是否按照主键顺序进行存储的,行的数据是否存储到索引的叶节点中。
非聚簇索引:存个指针,所以数据逻辑顺序和物理顺序是乱的
扫描遍历时,比较耗时,优化方法是事先缓存一下所需数据所在页。
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,一行数据也就是几百字节,所以一个页(节点)可能存几百上千条数据。 例如:
方法一:线性扫描(Linear)。相比把叶子节点从磁盘上存取的时间,全页(节点)扫描的时间开销很小了
方法二:二分查找。页内的数据本身就是有序的,所以可以执行二分查找
方法三:推断(找规律)从最小值4,最大值为10的七个数据中找8
B+树的优化方案
前缀压缩
重复冗余处理
冗余的key进行压缩
按批插入
B+树在一个一个插入数据的时候,效率比较低下。可以选择将插入数据,组成一个批次(多个数据),先将该批次的所有数据创建一个B+树,再将该B+树和主B+树merge起来
总结
B+树几乎是对于做DBMS索引而言的必定选择