69.1. 概述

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

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

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

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

哈希索引对于在较大表上使用相等扫描的SELECT和UPDATE的繁重工作进行了最佳优化。在B树索引中,搜索必须在树中向下搜索,直到找到叶子页。在具有数百万行的表中,这种向下搜索会增加数据访问时间。哈希索引中叶子页的等价物称为桶页。相反,哈希索引允许直接访问桶页,从而可能减少较大表中的索引访问时间。在大于shared_buffers/RAM的索引/数据上,这种“逻辑I/O”的减少变得更加明显。

哈希索引被设计用于处理哈希值的不均匀分布。如果哈希值均匀分布,则直接访问桶页效果良好。当插入使得桶页变满时,额外的溢出页链接到该特定的桶页,本地扩展存储来容纳与该哈希值匹配的索引元组。在查询期间扫描哈希桶时,我们需要扫描所有溢出页。因此,就某些数据所需的块访问数而言,不平衡散列索引实际上可能比B树更差。

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

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

如果可以,VACUUM还将尝试将索引元组压缩到尽可能少的溢出页上,以最小化溢出链。如果溢出页变为空,溢出页可以被回收以在其他桶中重用,尽管我们从未将它们返回到操作系统。目前,除了使用REINDEX重建哈希索引外,没有任何收缩哈希索引的方法。也没有减少桶数量的方法。

哈希索引可能会随着索引行数的增加而扩展桶页数。选择哈希键到桶号的映射,以便可以增量扩展索引。当一个新的桶要添加到索引中时,只需要“拆分”一个现有的桶,其中的一些元组将根据更新后的key到桶号的映射转移到新桶。

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