hash&Hash索引

127 阅读6分钟

前言

想的再多,不如行动起来,大家好,我是啊Q,让我们徜徉在知识的海洋里吧。

一起“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第15天, 点击查看活动详情

上一篇章我们介绍了 Btree和B+tree, 这一篇我们介绍一下hash索引和Hash相关的知识。

Hash

Hash的基本概念

在了解Hashing之前我们了解一下映射(Mapping)一些概念:

映射( Mapping) ****是将两个事物链接在一起的方式,称为“键”和“值”。

键(key) ——唯一标识数据(实体)的标识符。
值(value) ——我们要存储的实际实体(及其详细信息)。

举个列子:

比如每个学生都有一个学号。那么这个学号就是一个key,学生就是一个值,他可以有很多属性,比如班级,学校,学籍等等信息。 那么这个学号和学生之间就建立一种映射关系,我们通过这个学号就可以知道这个学生的一却相关信息。

那么什么是Hashing呢?

Hashing 是一种我们可以从任何键中获取整数值的方法。这个Key可能是也可能不是一个整数,但是在执行散列之后,它会为任何Key返回一个Integer值。

我们需要hash的原因是:输入中给出的键(key)不能用作放置此键值的内存存储位置。

Hashing分两步工作:

  1. 它以任何实体(作为key)作为输入,如果该键不是整数,我们需要通过一定的方法该实体获取一个整数值(N)

  2. 如果我们得到的初始值是一个整数值(N),这个 Integer 就不能用作放置这个 Key-Value 的内存地址。通过散列将此整数转换为另一个整数值,则值可用作内存地址/索引以放置此键值。

举个列子:

学生详细
信息键:学生号(studentNumber)

值:姓名、年龄、地址

我们有 3 名学生:

学生学生号(studentNumber)
学生 1101张三,18 岁,马里亚纳海沟
学生 2105李四,25 岁,珠穆朗玛峰
学生 3106王五,24 岁,贝加尔湖

由于 学生号 是 101、104、106,我们要么需要一个非常大的数组(至少 size= 106)来存储 3 个条目,要么我们可以使用一种叫做Hash Index的东西,它允许我们只使用数组 size = 3。

哈希函数会将 学生号(Key) 作为输入,并将返回hash Index作为输出。

因此哈希函数 eg 可以像 studentNumber % ArraySize 一样简单。

例如,对于 学生1:studentNumber = 101,因此此哈希函数将生成 101%3 = 2。所以学生 1可以放在size=2 处 。请注意,我们已采用 %3,因为数组的大小可以为 3(3 个学生,因此所需空间>=3)。

学生 2:105%3 = 0
学生 3:106%3 = 1

因此 Student1 可以位于数组的第二个索引处,Student1 位于数组的第 0 个索引处,Student2 位于数组的第一个索引处。

哈希表(包含学生名册号):[104, 106, 101]

如果我们想搜索一个学生,例如 studentNumber = 101 怎么办?

他将在101上运行类似的哈希函数,并将得到相同的哈希索引,即 2。
然后我们可以转到 size = 2 的数组索引位置,以获取 id = 101 的学生。

正如您所看到的,使用散列技术您只需要找到 Key 的索引,而不是查找/比较 Details(即 Values)的值。

到此处我们可以看到 Hashing索引的时间复杂度是 O(1)

hash冲突

我们接着一上面列子为列,比如现在来了一个新生:

学生学生号(studentNumber)/键学生信息/值hash code
学生 1101张三,18 岁,马里亚纳海沟2
学生 2105李四,25 岁,珠穆朗玛峰0
学生 3106王五,24 岁,贝加尔湖1
学生 4107王二,27 岁,河湾王国2

学生4:107%3 = 2 。 可以看到,学生1的hash值和学生2的hash值都为2,这就产生了hash冲突。

解决冲突的办法如下图所示:我们将hash值相同的数据通过链表链接起来。

想象一下:如果put的数据计算的hash冲突过于严重,将导致链表过于长。这对于查询将不是好事。时间复杂都变成O(N),那么如何保存hash冲突不严重将变得重要。所以好的hash函数并不如上述简单那么简单。

Hash索引

Hash底层是由Hash表来实现的,存储引擎都会【对所有的索引列计算一个哈希码】(hash code),哈希索引将所有的哈希码存储在索引表文件中,同时在哈希表中保存指向每个数据行的指针,根据键值 <key,value> 存储数据的结构存储<哈希码,指针>,非常适合根据key查找value值,也就是等值查询(单个key查询)

性质

  • 检索速度快:Hash索引能够快速定位到指定键值的数据记录,查找时间复杂度为 O(1)。
  • 不支持范围查询:由于Hash索引中数据的排列是随机的,不支持范围查询。
  • 适合等值查询:Hash索引适合于等值查询,即根据指定的键值进行查询。

作用

Hash索引主要用于加快等值查询的速度。在某些场景下,Hash索引的查询速度甚至可以超过B-Tree索引。

检索过程

对于指定的键值,Hash索引的检索过程如下:

  • 计算键值的哈希值。
  • 在哈希表中查找对应哈希值的桶。
  • 在对应的桶中查找指定键值的数据记录。

需要注意的是,如果哈希冲突,即不同的键值对应相同的哈希值,那么需要进行链式存储,即在桶中使用链表等数据结构存储具有相同哈希值的数据记录。

与B-Tree的区别

相较于B-Tree索引,Hash索引有以下不同之处

  • Hash索引支持快速的等值查询,但不支持范围查询,而B-Tree索引可以支持等值查询和范围查询。
  • Hash索引适合于数据分布比较均匀的场景,而B-Tree索引适合于数据分布不均匀的场景。
  • Hash索引的数据存储是随机的,不支持有序性查询,而B-Tree索引是基于有序的B树数据结构实现的,支持有序性查询。
  • Hash索引在内存中的效率更高,因为它的查找时间复杂度是 O(1),而B-Tree索引则需要多次磁盘IO操作。