哈希表将键映射到值。它提供平均 O (1) 的操作复杂度(最坏情况下为 O (n))和 O (n) 的存储复杂度。
由两部分组成: Hash Function和Hashing Scheme(发生冲突后的处理)
DBMS 一般只关注散列速度和冲突率,不考虑安全性
SOTA:Facebook XXHash3
静态散列方案是一种散列表大小固定的方案。
动态散列方案能够根据需要调整哈希表的大小,而无需重建整个表。
表索引是表列子集的复制,通过对这些属性的子集进行组织或排序,以实现高效访问。因此,DBMS可以对表索引进行查找,以更快地找到某些图元,而不是执行顺序扫描。DBMS 确保表和索引的内容在逻辑上始终保持同步。
B+Tree 是一种自平衡树形数据结构,它能保持数据排序,并允许在 O(log(n))内进行搜索、顺序访问、插入和删除。
B-Trees 在所有节点中存储键和值,而 B+ 树只在叶子节点中存储值。现代的 B+Tree 实现结合了其他 B-Tree 变体的特征,例如 B link-Tree 中使用的同胞指针。
B+Tree 是一棵 M 向搜索树(其中 M 代表一个节点可拥有的最大子节点数),具有以下属性:
对于叶节点,键来自索引所基于的属性。每个节点上的k/v数组几乎都是按键排序的。叶节点值的两种方法是记录 ID 和元组数据。记录 ID 指的是元组位置的指针,通常是主键。拥有元组数据的叶子节点会在每个节点中存储元组的实际内容。
对于内部节点来说,值包含指向其他节点的指针,而键可以被看作是引导桩。 它们引导着树的遍历,但并不代表叶子节点上的键(以及它们的值)。内部节点只拥有叶子节点中的键。
对于重复的key,一种方法是增加记录id作为key的一部分,另一种方法是允许叶节点溢出到包含重复密钥的溢出节点中。
表按照主键指定的排序顺序,以堆栈或索引组织的存储方式存储。 由于有些 DBMS 总是使用聚类索引,因此如果表没有显式主键,它们就会自动将隐藏行 id 作为主键。如果使用聚类索引的属性访问图tuple,DBMS可以直接跳转到页面。
由于直接从非聚类索引中检索tuple的效率很低,因此 DBMS 可以先找出它需要的所有tuple,然后根据它们的页面 id 对它们进行排序。
Node Size一般取决于存储介质或者workload。
删除后立即合并叶子节点可能会导致混乱,大量连续的删除和插入操作会导致不断的拆分和合并。可以设置分批合并,即多个合并操作同时进行,从而减少在树上进行昂贵的写入延迟的时间。
支持可变长度的键:
节点内搜索:
由于 B+Tree 的每个节点都存储在缓冲池中的一个页面中,因此每次加载一个新页面时,都需要从缓冲池中获取该页面。为了完全跳过这一步,可以存储实际的原始指针来代替页面 ID。与手动获取整个树并手动放置指针相比,我们只需在正常遍历索引时存储页面查找产生的指针即可。需要注意的是,我们必须跟踪哪些指针被转换,并在指针指向的页面被unpin和驱逐时,将指针转换回页面 ID。
在 B+Tree 的初始构建过程中,按照常规方法插入每个键会导致不断的拆分操作。由于我们已经给叶子提供了同级指针,所以如果我们构建一个叶子节点的排序链表,然后使用每个叶子节点的第一个键自下而上地建立索引,那么初始插入数据的效率就会高得多。
前缀压缩。
对于非唯一key,只存储一次key。
只存储将查找正确路由到叶子节点所需的最小前缀。
逻辑正确性:这意味着线程能够读取它应该读取的值,例如,线程应该读回它之前写入的值。
物理正确性:这意味着对象的内部表示是正确的,例如,数据结构中不存在会导致线程读取无效内存位置的指针。
实现latch的基本原理是现代 CPU 提供的原子比较和交换(compare-and-swap,CAS)指令。通过该指令,线程可以检查内存位置的内容,查看其是否具有特定值。如果有,CPU 就会将旧值换成新值。否则,内存位置将保持不变。
在动态哈希表上相对更复杂。
利用CAS也是一种方法。
Basic:
Improved:
B+树代码必须能应对失败的锁存器获取。由于latch的持有时间(相对)较短,如果线程试图获取叶节点上的latch,但该latch不可用,那么它应迅速中止操作(释放持有的任何latch),然后重新开始操作。
数据库系统会将 SQL 编译成查询计划。查询计划是一棵运算符树。
DBMS 需要对数据进行排序,因为根据关系模型,表中的tuple没有特定的顺序。排序使用 ORDER BY、GROUP BY、JOIN 和 DISTINCT 操作符。如果需要排序的数据适合内存,那么 DBMS 可以使用标准排序算法(如快排)。如果数据不适合在内存中进行排序,那么 DBMS 就需要使用外部排序,这种排序可以根据需要溢出到磁盘,并且优先选择顺序 I/O,而不是随机 I/O。
如果查询包含一个带 LIMIT 的 ORDER BY,那么 DBMS 只需扫描一次数据就能找到前 N 个元素。这就是 top-n 堆排序。堆排序的理想情况是前 N 个元素都在内存中,这样 DBMS 只需在扫描数据时维护一个内存中的优先队列即可。
外部合并排序是对大到内存无法容纳的数据进行排序的标准算法。这是一种 "分而治之 "的排序算法,它将数据分割然后分别进行排序。
外部合并排序的一种优化方法是在后台预取下一次运行,并在系统处理当前运行时将其存储在第二个缓冲区中。这样可以持续利用磁盘,减少每一步 I/O 请求的等待时间。
对于 DBMS 来说,使用现有的 B+tree 索引辅助排序有时比使用外部合并排序算法更有优势。特别是,如果索引是聚簇索引,DBMS 就可以直接遍历 B+tree 索引。由于索引是聚类的,数据将以正确的顺序存储,因此 I/O 访问将是顺序的。
实现聚合有两种方法:排序和散列。
排序:DBMS 首先根据 GROUP BY 键对图元进行排序。如果所有数据都在缓冲池中(如 quicksort),可以使用内存内排序算法;如果数据大小超出内存,可以使用外部合并排序算法。然后,DBMS 会对排序后的数据执行顺序扫描,以计算聚合。运算符的输出将按键排序。在执行排序聚合时,必须对查询操作进行排序,以最大限度地提高效率。 例如,如果查询需要过滤,最好先执行过滤,然后对过滤后的数据进行排序,以减少需要排序的数据量。
哈希: