Postgresql 中文操作指南
72.1. Overview #
PostgreSQL 包含一个持久化磁盘哈希索引的实现,该实现完全支持崩溃恢复。任何数据类型都可以通过哈希索引进行索引,包括没有明确线性排序的数据类型。哈希索引仅存储所索引数据的哈希值,因此对要索引的数据列的大小没有限制。
哈希索引仅支持单列索引,并且不允许唯一性检查。
哈希索引仅支持 = 运算符,因此指定范围操作的 WHERE 子句将无法利用哈希索引。
每个哈希索引元组仅存储 4 字节的哈希值,而不是实际的列值。因此,当对较长的数据项(如 UUID、URL 等)进行索引时,哈希索引可能比 B 树小得多。没有列值也使所有哈希索引扫描都具有损耗。哈希索引可以参与位图索引扫描和反向扫描。
哈希索引最适合于对大表使用相等性扫描的 SELECT 和 UPDATE 繁重的工作负载。在 B 树索引中,搜索必须向下遍历树,直到找到叶页面。在包含数百万行的数据表中,此类遍历会增加对数据的访问时间。哈希索引中叶页面等效项称为桶页面。相比之下,哈希索引允许直接访问桶页面,从而有可能减少大型表中的索引访问时间。在大于 shared_buffers/RAM 的索引/数据上,这种“逻辑 I/O”的减少会更加明显。
哈希索引已被设计为能应对哈希值的不均匀分布。如果哈希值均匀分布,则直接访问桶页面效果很好。当插入项使桶页面变满时,会将其他溢出页面链接到该特定桶页面,从而局部扩展与哈希值匹配的索引元组的存储空间。在查询期间扫描哈希桶时,我们需要扫描所有溢出页面。因此,对于某些数据,不平衡的哈希索引在所需的块访问数方面实际上可能比 B 树更差。
由于会发生溢出情况,我们可以说哈希索引最适合唯一、几乎唯一的数据或每哈希桶中行数较少的数据。一种避免问题的可能方法是使用部分索引条件从索引中排除高度非唯一的值,但这在很多情况下可能不合适。
与 B 树类似,哈希索引执行简单的索引元组删除操作。这是一项延迟维护操作,它会删除已知可以安全删除(其项标识符的 LP_DEAD 位已设置)的索引元组。如果插入操作发现页面上没有可用空间,我们会尝试删除死索引元组以避免创建新的溢出页面。如果页面当时被固定,则无法删除。死索引指针的删除也会在 VACUUM 期间进行。
如果可以,VACUUM 还会尝试将索引元组压缩到尽可能少的溢出页面上,从而最大限度地减少溢出链。如果溢出页面变为空,则溢出页面可以回收以在其他桶中重复使用,但我们绝不会将它们返回给操作系统。目前没有办法缩减哈希索引,只能通过使用 REINDEX 重建它。也没有办法减少桶的数量。
哈希索引在索引行数增加时,可能扩展存储桶页面的数量。哈希键到存储桶号码的映射被选择,以便可以渐增扩展索引。在新存储桶被添加到索引时,准确地一个已存在的存储桶需要“拆分”,并且其一些元组将根据更新的键到存储桶号码映射被转移到新存储桶。
扩展在前景发生,这可能会增加用户插入的执行时间。因此,哈希索引可能不适合行数快速增加的表。