Skip to content

72.1. 概述#

PostgreSQL包含持久化磁盘哈希索引的实现,该索引完全可从崩溃中恢复。任何数据类型都可以通过哈希索引进行索引,包括没有明确线性排序的数据类型。哈希索引只存储所索引数据的哈希值,因此对所索引数据列的大小没有限制。

哈希索引仅支持单列索引,不允许唯一性检查。

哈希索引仅支持=运算符,因此指定范围运算的 WHERE 子句将无法利用哈希索引。

每个哈希索引元组只存储 4 字节哈希值,而不是实际的列值。因此,在索引较长的数据项(如 UUID、URL 等)时,哈希索引可能比 B 树小得多。没有列值还会使所有哈希索引扫描都具有丢失性。哈希索引可以参与位图索引扫描和反向扫描。

哈希索引最适合在使用大表上的相等性扫描的 SELECT 和 UPDATE 密集型工作负载。在 B 树索引中,搜索必须向下遍历树,直到找到叶页。在有数百万行的表中,这种遍历会增加对数据的访问时间。哈希索引中叶页的等效项称为存储桶页。相比之下,哈希索引允许直接访问存储桶页,从而有可能减少大表中的索引访问时间。这种“逻辑 I/O”的减少在索引/数据大于 shared_buffers/RAM 时变得更加明显。

哈希索引旨在应对哈希值的分布不均。如果哈希值分布均匀,则直接访问存储桶页面效果很好。当插入导致存储桶页面变满时,将其他溢出页面链接到该特定存储桶页面,从而在本地扩展与该哈希值匹配的索引元组的存储空间。在查询期间扫描哈希存储桶时,我们需要扫描所有溢出页面。因此,对于某些数据,不平衡的哈希索引在所需的块访问次数方面实际上可能比 B 树更差。

由于溢出情况,我们可以说哈希索引最适合唯一的、几乎唯一的数据或每个哈希存储桶中行数较少的数据。避免问题的一种可能方法是使用部分索引条件从索引中排除高度非唯一的值,但这在许多情况下可能不合适。

与 B 树类似,哈希索引执行简单的索引元组删除。这是一个延迟维护操作,用于删除已知可以安全删除的索引元组(其项目标识符的 LP_DEAD 位已设置)。如果插入发现页面上没有可用空间,我们将尝试通过删除无效索引元组来避免创建新的溢出页面。如果页面当时被固定,则无法进行删除。无效索引指针的删除也会在 VACUUM 期间发生。

如果可以,VACUUM 还会尝试将索引元组压缩到尽可能少的溢出页面上,从而最大程度地减少溢出链。如果溢出页面变为空,则可以回收溢出页面以在其他存储桶中重复使用,尽管我们永远不会将它们返回给操作系统。目前除了通过 REINDEX 重建哈希索引之外,没有缩小哈希索引的规定。也没有减少存储桶数量的规定。

随着索引行数的增加,哈希索引可能会增加存储桶页面的数量。哈希键到存储桶编号的映射被选择,以便可以逐步扩展索引。当需要向索引中添加一个新存储桶时,正好需要“拆分”一个现有存储桶,根据更新后的键到存储桶编号映射将其一些元组传输到新存储桶。

扩展发生在前台,这可能会增加用户插入的执行时间。因此,哈希索引可能不适合行数快速增加的表。