【CMU 15-445/645 Database Systems】06 Hash tables

782 阅读4分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

回顾一下,学习DB的自底向上的几个逻辑

  • disk manager 关心物理存储
  • buffer pool manager 关心内存存储
  • access methods 关心数据查找
  • operator execution 关心操作符如何执行
  • query planning 关心如何设计执行顺序

1. hash table

  • 如何选func:速度和冲突率之间的抉择;
  • 如何设计scheme(结构):如何处理冲突的键;

Static hash table

预先知道元素的个数,每个key都是独立的,hash函数能唯一的将key映射到value上,不会产生冲突。

Linear probe hashing

线性探查解决冲突,遇到冲突的时候查找下一个空闲槽位(next free slot);

为了正确的找到目标元素,在存储的slot中要同时记录key和value;

对于non-unique keys(每一个key可能对应多个value),有两种解决方法:

  • 每个key关联到一个list中;
  • 将key和value一起存储在slot中;

Robin hood hashing

尽可能的均衡那些因为冲突导致位置偏离的key需要向后线性探查的位置数。

用下面这个例子来说明。每个slot同时记录当前这个元素“移动”的距离,下面的元素按照ABCDE的顺序写入table:hash(A) = 2; hash(B) = 0; hash(C) = 2; hash(D) = 3; hash(E) = 2;

首先A就在现在的位置上,没有移动,记作0;C也被hash到A所在的位置上,产生冲突,往后移动1位,记作1;D被hash到C目前在的位置,往后移动1位,所以也记作1;E被hash到A所在的位置,首先在index = 2上冲突,和A比较,0 = 0,A维持不变;E往后找1位,在index = 3上和C冲突,和C比较,这时候E因为移动了1格,所以距离变成1,1= 1,所以C还是维持不变;E再往后找,E的距离变成2,但下一格D的距离是1,D[1] < E[2],所以D后推,E留在index = 4的格子,变成第二张图的情况:

Cuckoo hashing

布谷鸟哈希;同时用多个不同的哈希函数映射到多个哈希表;每次只要找到任意一个表中的目标位置可用就写入;如果每一个表的目标位置都被占用了,就选择其中某一个,重新进行hash来提供这个格子。

Dynamic hash table

以上都是静态的哈希表,需要DBMS知道元素数量,例如,线性探查的方式,无论每个元素往后推移多少个slot,需要插入表中的数据总数和表的size是一致的,多了就放不开了,再怎么线性探查也没用。动态哈希表可以根据需求随时resize。介绍三种方法:

chained hashing

使用桶来存储,每个hash key对应的是一个链表。相当于拉链法进行存储;

extendible hashing

扩展hash函数映射的粒度来细化bucket中存储的内容。当粗粒度的hash函数映射到的目标bucket存满的时候,就扩充一次hash函数:

初始,global=2,means通过映射key的二进制最左2位来选择目标bucket;这时候注意到2号bucket已经放满了,所以如果又有一个10开头的数据要插入,这时候发生冲突,那么就把global扩展到3,如图2。

Linear hashing

当某个bucket溢出的时候,增加一个bucket,链式增加,然后生成一个新的哈希函数hash2,将这个split pointer前面的所有bucket都用hash2进行一次计算。

2. 总结

这一节介绍的是哈希表的几种设计思路。 DBMS中会用到很多种数据结构。不同的数据结构,用在不同的功能上。

  • 内部元数据:维护数据库的信息和系统状态。元数据一般和实际数据是一起存储的,也是数据表的形式。
  • 核心数据存储:数据库中的实际数据。第3、4节提到的storage中介绍的存储方式用于存储数据。
  • 临时数据结构:在处理一些query时,有可能需要创建一些临时的数据结构来作为中间表等,用于加速和优化执行过程。本节提到的hash table就可以用于join的结果生成中间表。
  • 表索引:用于数据表中数据的检索。第7、8节中提到的tree index部分用于建立索引。