数据库

[数据库之十三] 数据库索引之散列索引

2021-06-24  本文已影响0人  小胡_鸭

一、什么是散列

  顺序文件组织的一个缺点是我们必须访问索引结构来定位数据,这将导致过多的 I/O 操作。基于散列(hashing)技术的文件组织使我们能够避免访问索引结构,散列也提供了一种构造索引的方法。

  桶(bucket):表示能存储一条或多条记录的一个存储单位,通常一个桶就是一个磁盘块,也可能小于或大于一个磁盘块。

散列函数

h(K) → B

K 表示所有搜索码值的集合

B 表示所有桶地址的集合

h 是一个从 K 到 B 的函数,即散列函数


二、散列的作用


三、静态散列

1、散列函数的要求

  理想的散列函数把存储的码均匀地分布到所有桶中,使每个桶含有相同数目的记录,因此我们希望选择一个把搜索码值分配到桶中并且具有下列分布特性的散列函数:

  一般情况下,散列函数就是要根据搜索码的值计算出一个很大的整数,然后对桶的数量求模,得到对应的记录应该放在哪个桶里面,所以这个很大的整数只要具有数据无关的随机性,就能保证数据分布在所有桶中是均匀和随机的。

2、桶溢出

  桶允许存储的数据的空间是有限的,当插入记录所映射到的桶的空间不足时,称为桶溢出
  桶溢出可能的原因有:

桶偏斜发生的原因有:

  解决桶溢出的方法:

3、缺陷

  桶的个数和每个桶的容量不好规划,如果小了,容易造成桶溢出;如果为了以后的数据增长设计得比较大,就会浪费当前的空间。

4、散列索引

  散列可用于索引结构的创建,即散列索引,将搜索码及其相应的指针组织成散列文件结构。散列索引是一种辅助索引,不需要作为聚集索引结构来使用。

下图展示了为搜索码 ID 建立的散列索引,散列函数为对 ID 对 8 取模,每个桶的大小为 2,若有超过两条数据的桶,则需使用溢出桶。


四、动态散列

  使用静态散列时,要求固定桶地址集合(即要使用多少个桶,每个桶大小是固定的),有三种选择:

动态散列技术允许散列函数动态改变,以适应数据库增大或缩小的需要,可扩充散列就是一种常见的动态散列技术。

1、数据结构

  使用可扩充散列,首先要选择一个分布均匀和随机的散列函数 h,产生的值是 b 位二进制整数(典型的 b 值是 32)。

  如上图所示,32 位二进制,最多可以产生 232 个不同的整数,超过 40 亿个,如果用每个整数来代表一个桶,最多可以有超过 40 亿个桶,一般用不了这么多。当用不了这么多的时候,可以不取整个散列值,而是取 b 位的前 i 位,比如需要 8 个桶,那取前三位就可以表示不同的桶。随着数据库的大小变化,i 也随之增大和减小,如下所示:

  我们把右边叫做桶地址表,指明了使用多少位前缀,表中每条记录存储了散列值及对应指向的桶的地址。随着数据文件的增长,需要使用更多的位来表示不同的桶。事实上,桶和表的初始状态并不为上图所示,如果没必要的话,是不会一开始就创建这么多桶的,因为会造成空间的浪费,所以有可能桶地址表中几个连续的表项指向同一个桶,这多个表项拥有相同的前缀,但前缀长度可能小于 i。

  如上图所示,假设现在要插入一条记录,计算出的散列值对应桶地址表的 0 1 ... ,但是尚未为其单独创建一个桶,而 0 0 ... 对应的桶已经被创建了,并且还有空余的空间,那么就将这条记录插入到桶 0 中。

  每个桶都会记录一个整数,表示桶中所有记录的散列值的共同前缀的位数,设每个桶的编号为 j,这个记录位数为 ij,则对桶 0 来说,j 为 0,ij 为 1,如果 ij = i,则说明这个桶存储的记录的散列值只对应桶地址表的一条记录。

2、查询和更新

  根据搜索码查询数据时,先用散列函数计算出搜索码值的散列值,再从桶地址表找到对应记录,最后找到对应的桶,再从桶中检索;而更新无非是在查询的基础上多了一步对数据进行更新,由于更新的字段可能包含在搜索码中,所以算法如下:

3、插入

算法如下:

扩位后如图:

  如果原来桶 0 的四条记录散列值前 3 位都是 0 0 0 ...,那么为了插入新记录,需要再次扩位到 4 位,一般情况下,只要选用合适的散列函数,是不太可能导致多次扩位桶分裂的。如果桶 0 中所有的记录具有相同的搜索码,并且新记录的搜索码也一样,那不管使用什么散列函数,其散列值必然相同,不管扩位桶分裂多少次,都无法在该桶中插入该记录,这种情况下,应该使用溢出桶。

4、删除

  删除其实就是插入的逆操作,先查询桶,再删除数据,插入桶满分裂,删除则是桶空合并,当然合并需要考虑所有的桶的数据存储情况,合并后的桶不能装不下原来的两个桶的记录。

算法如下:

  缩位之前如图:

  缩位之后如图:

 桶合并前,桶 1 删除了记录后为空,可以选择跟桶 0 合并,桶记录共同前缀减 1 位。

 桶合并后如图:

五、动态散列和静态散列的比较

可扩充散列的优点:


六、散列的缺陷

  散列不适用于范围查询,比如 where xxx between c1 and c2xxx > c 这种操作,因为散列用作索引时是一种辅助索引,而不是顺序索引,顺序索引比较适合用来处理范围查询。

  对顺序索引来说(比如 B+ 树),以 between c1 and c2 为例,只要找到第一个 ≥ c1 的数据所在的页,以及最后一个 ≤ c2 的数据所在的页,这两页中间的所有页中的数据,都是满足查询条件的数据。

  而对于散列来说,满足条件的数据分散在不同的桶中,且桶中的数据不是按照搜索码顺序存储的,这意味着,可能要找到所有的桶,并逐一比较桶中的所有数据才能筛选出满足查询条件的记录,效率低下。

上一篇 下一篇

猜你喜欢

热点阅读