Hash函数的性质:
一致性:具有相同关键字的值被赋给同一个桶中。
随机性:每个桶将会有相同数据的记录,而不考虑文件中关键字的真实分布。
最坏性:把所有的关键字映射到同一个桶中,使得访问时间和文件中关键字的数量成正比。
Static Hashing:
如果没有空间剩余,将会分配overflow buckets, 用链表把它们连接起来。(长的链表降低了性能)
Deficiency:
一:如果初始桶的数量很少,随着文件的增长,性能将会因为太多的overflow buckets下降。
二:如果分配期望增长的空间,那么大量的空间在开始的时候都浪费了。
三:如果数据库比较小,空间再次浪费了。
One Solution: 周期的用新的Hash函数重新组织文件。
缺点是:昂贵,打乱了正常的操作。
Better solution: 动态的改变桶的数目。
Extendible Hashing
既然桶装满了,为何不用双倍的桶来组织文件?
一:只需要分裂溢出的桶。(但是读写所有的文件是很昂贵的)
Idea:使用指向桶的指针目录,通过双倍指针目录来双倍桶。因为指针目录比文件小,所以双倍指针目录会更划算。
其中,指针目录(Directory)的大小为4。比如要插入5,它的二进制为(0101), 因为Global Depth = 2, 所以找到(01)的指针目录,插入所指向的桶中。
如果要插入20呢?因为20为(10100), 所以要插入00的指针目录中,但Bucket A 已经满了,这个时候,分裂Bucket A, 如果需要的话,还需要双倍指针目录(Directory)。
这个时候,可以看出Bucket A 分裂了,它的Local Depth为3了。所以32(100000), 16(10000)分配到一起;而4(100),12(1100), 20(10100)分配到一起。同时双倍了Directory。可以看到,Bucket C没有满,所以Local Depth为2。但是Global Depth中的001和101指向了它。
插入9(1001)呢?(这个时候导致分裂,但是没有导致双倍Directory)
Global depth of directory p: Max # of bits needed to tell which bucket an entry belongs to.
Local depth of a bucket q: # of bits used to determine if an entry belongs to this bucket.
Each bucket has pointers to it from the directory.
When does bucket split cause directory doubling?
If the bucket has only 1 pointer to it from the directory, doubling the directory; Otherwise, simply redistribute the pointers after splitting.
When to merge bucket and shrink directory during deletion?
Merge Bucket: merge with its split image when bucket becomes empty.
Shrink directory: if every directory element and its split image directory entry point to the same bucket, shrink directory by ½.
Deciency:
Directory can grow large if the distribution of hash values is skewed.
Multiple entries with same hash value cause problems!
Linear Hashing
1:Handle long overflow chains.
2:Handle duplicates.
3: Idea: use a family of hash functions h0, h1, h2, ... hi+1 doubles the range of hi (similar to directory doubling) .
4: Splitting proceeds in “round”. (Round ends when all initial buckets are split. ).
5: Current round number is Level, and current function is hLevel。
address(level,key) = hash(key) mod N * (2level)
当插入43 (101011)时, 导致overflow Bucket, 因为指针没有指向它,所以不分裂。但是此时Next指针向后移动而且指针指向的桶分裂。
在Next指针前面的hash用H(level+1). 后面的用H(level).
通过指针的移动来分裂桶。
(All Above come from PPT of Ji Liping, HIT Shenzhen Graduate School)