索引
什么是索引?
索引是定义在存储表基础上,有助于无需检查所有记录而快速定位所需记录的一种辅助存储结构,由一系列存储在磁盘上的索引项组成。
而索引项又由两个字段组成:索引字段:用于进行检索的表属性;行指针:指向包含索引字段的行的在磁盘上的位置。
索引分为两类:
- 顺序索引:基于值的排序
- 散列索引:基于将值均匀分布到若干个桶中
顺序索引又可分为主索引和辅助索引:
- 主索引:索引字段经过排序,主索引有时也被称为非聚集索引
- 辅助索引:索引字段未经过排序,辅助索引有时也被称为聚集索引
顺序索引还可以分成稠密索引和稀疏索引:
- 稠密索引:主文件(表)中的每一行都对应一个索引项
- 稀疏索引:主文件中并不是每一行都有索引项
辅助索引必须是稠密索引,稀疏索引必须是主索引。
散列索引分为两类:
- 静态索引:散列桶的数目不变
- 动态索引:散列桶的数目随索引项的数目变化而变化
对散列函数的要求:
- 分布是均匀的:即每个桶分配到的索引项的可能的数目相同
- 分布是随机的:即在一般情况下,不管搜索码值是如何分布的,每个桶应分配的索引项数目几乎相同。更确切的说,散列值不应与搜索码的任何外部可见的排序相关。
偏斜:某些桶分配的记录比其他桶多,所以即使其他桶有空间,某个桶也可能溢出。
造成偏斜的原因:
- 多条记录具有相同的搜索码
- 所选的散列函数可能分布不均匀
处理溢出的方法:
- 线性探查法:将溢出的索引项放到相邻的桶中,这种方法也被称为开地址法。
- 溢出链:将溢出的索引项用链表连接,这种方法也被称为闭地址法。
B+树
添加节点
如果一个节点里没有多余的空间来存放插入的键值对,那么需要将节点进行分裂。
需要将中间值的键值对提升到其父节点中。如果这个节点不是叶子节点那么在将其中间值键值对上升到父节点中后,要在这个节点中删除那个键值对。否则,就需要保留这个键值对。
下面给出一个例子:
对上面这颗树,依次插入250和280。
先插入:
然后分离节点,提升将中间值键值对(这里是250)提升到其父节点中:
因为这个节点是叶子节点,所以将中间值提升到父节点后,节点仍然保留这个键值对。
然后父节点满了,需要进一步分离:
这里父节点不是叶子节点,所以将中间值的键值对(这里是200)提升到根节点后,需要在父节点中删除这个键值对。
删除节点
从B+树中删除一个节点的时候需要注意保持每个节点的利用率在50%以上。也就是说,叶子节点中的键值对的个数需要占ceil((n-1)/2)个,其中n是每个节点的指针个数。
此外,非根节点非叶子节点的节点的子节点的个数应该在ceil(n/2)到n之间。
进行了删除操作后,如果一个节点中的键值对的个数不满足上述要求,那么就需要与其兄弟节点进行合并。
与兄弟节点合并了后,需要将这两个兄弟节点之间的父节点删除掉。如果删除了父节点后,合并出来的新节点没有父节点,那么也与其相邻的在进行一次节点合并。
下面给出一个例子:
在这颗树中删除节点550。
先进行删除:
删除后发现,其中有一个节点不满足要求,所以需要进行合并:
发现新合并的节点没有父节点,所以要与其相邻的节点合并: