Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

62.4. 索引锁定注意事项 #

索引访问方法必须处理多个进程对索引的并发更新。在索引扫描期间,核心PostgreSQL系统在索引上获取AccessShareLock,而在更新索引(包括普通VACUUM)时获取RowExclusiveLock。由于这些锁类型不会发生冲突,访问方法负责处理它可能需要的任何细粒度锁定。只有在创建索引、销毁索引或REINDEX期间才获取整个索引上的ACCESS EXCLUSIVE锁(使用CONCURRENTLY而不是获取SHARE UPDATE EXCLUSIVE)。

构建支持并发更新的索引类型通常需要对所需行为进行广泛且精细的分析。对于 b-tree 和 hash 索引类型,您可以在src/backend/access/nbtree/READMEsrc/backend/access/hash/README中了解涉及的设计决策。

除了索引自身内部一致性要求之外,并发更新还会导致父表()和索引之间的一致性问题。由于PostgreSQL将堆的访问和更新与索引的访问和更新分开,因此索引可能与堆不一致。我们使用以下规则处理这个问题

如果未使用第三条规则,索引读取器有可能在索引条目被 VACUUM 删除之前看到该条目,然后在该条目被 VACUUM 删除之后到达对应的堆条目。如果该项编号在读取器访问它时仍然未使用,则不会产生严重问题,因为 heap_fetch() 将忽略空项插槽。但如果第三个后端已经将该项插槽用于其他内容呢?在使用符合 MVCC 的快照时,不存在问题,因为该插槽的新使用者肯定太新而无法通过快照测试。但使用不符合 MVCC 的快照(例如 SnapshotAny)时,可能会接受并返回实际上不匹配扫描键的行。我们可以要求在所有情况下都针对堆行重新检查扫描键来防御这种情况,但这开销太大。相反,我们将索引页面上的固定段用作代理,以表明读取器仍然可能“从”索引条目“在途中”访问匹配的堆条目。让 ambulkdelete 程序阻塞在该类固定段上可以确保在读取器完成之前,VACUUM 不会删除堆条目。此解决方案几乎不会增加运行时间,并且仅在确实存在冲突的罕见情况下才增加阻塞开销。

此解决方案要求索引扫描“同步”:我们必须在扫描对应的索引条目后立即获取每个堆元组。由于许多原因,这样做开销很大。我们从索引收集大量 TID 并在稍后访问堆元组的“异步”扫描,需要更少的索引锁定开销,并且可以使用更高效的堆访问模式。根据上述分析,我们必须对不符合 MVCC 的快照使用同步方法,但是对于使用 MVCC 快照的查询,可以使用异步扫描。

amgetbitmap 索引扫描中,访问方法不保留任何已返回元组上的索引固定段。因此,仅当与符合 MVCC 的快照一起使用此类扫描时才安全。

当未设置 ampredlocks 标志时,在可序列化事务中使用该索引访问方法的任何扫描都将在完整索引上获取非阻塞谓词锁。这将与由并发可序列化事务将任何元组插入该索引的操作生成读写冲突。如果在多组并发可序列化事务中检测到某些读写冲突模式,则可能会取消其中一个事务以保护数据完整性。当设置标志时,表明索引访问方法实现了更精细的谓词锁定,此锁定往往会降低此类事务取消的频率。