Hash索引
一、两种类型的Hash表
- Hash表:假设有编号为 0 到 B-1 的B个位置(桶)存放数据,使用一个Hash函数,把需要存储的数据作为参数计算一个介于 0 到 B-1的值,将这个数据存放到这个值对应的位置(桶),所有数据存放完毕,则形成一张Hash表
1.静态Hash表
桶数目B不变
2. 动态Hash表:
桶数据允许改变,动态Hash表分为分为两种
2.1 可扩展Hash表
- 为桶增加一个中间层,即使用一个指针数组来表示桶,而不是直接使用数据快作为桶。指针指向具体存储数据的数据块
- 指针数组能增长,数组长度总是2的幂。每增长一次,指针数组长度翻倍
- Hash函数为每个键值计算出一个足够长的二进制序列,使用二进制序列中从第一位开始的若干位作为键的Hash值,这个值对应的就是一个指针
2.2 线性Hash表
- 桶数B由需要存储的记录数决定
- 用于标识桶数的二进制位数为[log2B]
- 桶的增长较为缓慢。
- 如何插入元组:计算元组的Hash值,设这个值为K。假设此时[log2B]的值为i。将K转换为二进制数,从右开始取i位,得到的数即为元组要放入的桶数
二. postgresql中用做索引的Hash表为线性Hash表
Hash表有四种不同类型的页面,分别为元页、桶页、溢出页、位图页:
1 元页:
- 每个Hash索引都有一个元页。元页为索引的0号页,不属于任何桶。
- 元页记录了Hash的版本号、Hash索引记录的索引元组数目以及桶和位图的信息。
- 通过元页可以了解Hash索引总体使用情况,在索引的插入,溢出页的分配回收以及Hash表的扩展等过程中,都要用到元页
数据结构和各成员意义如下:
2.桶页
- Hash表有多个桶,每个桶由一个或多个页组成,每个桶的第一页称为桶页,其他页称为溢出页。
- 桶页随着桶的建立而建立,如果有溢出页,则桶页结构中的hasho_nextblkno字段指向溢出页,形成链结构
- 桶页的分配:splitpoint值修改时,将进行桶的分配,每次分配的数目都是2的幂次。0号和1号桶时Hash表初始化时候分配的,当splitpoint为2时,进行一次分配,将同时分配2号、3号桶页。当需要第5个桶时,splitpoint值为3,将分配4~7号桶页,依此类推。每次分配桶页时候,也会在桶页后按需求分配一定数量的溢出页,溢出页数量记录在hashm_spare数组中
- 每次分配的桶页在磁盘上是连续存储的,因此,可以根据桶页数计算桶所对应的磁盘块号:
注:hashm_spare记录第i次分裂后溢出页个数和位图页个数的和
3.溢出页
- 当某个元组在它所属的桶中放不下时,就需要将其放在该桶的溢出页。溢出页和桶页之间使用双向链表连接。
- 元页中使用数组hashm_spares记录每次桶的扩展时分配的溢出页数
- 溢出页一旦分配,便一直存在,即使被回收也只是标记为空闲,并没有释放物理空间
4.位图
当溢出页上的元组被移除时,就要将溢出页回收,回收溢出页并不是把它还给操作系统,而是继续由PostgreSQL管理,以便下一次需要时使用。因此需要一种机制来记录每一个溢出页是否可用。
- 用于管理Hash索引溢出页和位图页本身的使用情况
- 在位图中,对于每个溢出页和位图页都有一个比特位标识其使用情况。0表示该页可用,1表示该页不可用。位图页的格式由页头PageHeaderData,页尾Special Space和中间的位数组组成,中间的位数组的每一位对应一个溢出页或位图。位图中的位数必须为2的幂次
- 通过元页中的hashm_mapp数组可以找到位图对应的块号
- 利用元页中的hashm_mapp以及上面提到的hashm_spares可以快速查找到空闲溢出页
5.Hash表页面分配示例:
1.初始化
2.为0号桶分配溢出页0和溢出页1:
3.增加一个桶
4.为2号桶增加一个溢出页
三.Hash索引的实现
1.Hash表的创建:
- 初始化索引的元页、桶以及位图页
- 调用扫描函数对待索引的表进行扫描,生成索引元组
- 将索引元组插入到Hash表
2.元组的插入:
读取元组的信息,计算该索引元组应插入的桶号,若该桶有足够的空间,则直接插入,否则申请溢出页。完成插入后,系统根据元页信息判断是否需要增加新的桶
3.溢出页的分配和回收
索引元组插入过程中如果桶中没有空间,就需要创建一个溢出页存放索引元组。当对Hash表进行扩展后,原来存放在溢出页中的索引元组可能会被移入到新增加的桶中,这时就需要对溢出页进行回收。
溢出页的分配:
溢出页的回收
4. Hash表的扩展
每次插入后,用当前记录总数 r 和当前桶数目相除,计算 r/n ,若比率太大,则对Hash表进行扩展,即增加一个桶到Hash表中。如果新加入的桶的桶号二进制表示为1a2a3......ai,那么就试图分裂桶号为0a2a3......ai 的桶的元组,这个桶中部分元组(计算Hash值,取i+1位后,第一位为1的元组)将被移动到新的桶中
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构