选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.1 已于 2016 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

52.4. 索引锁定注意事项 #

索引访问方法必须处理多个进程并发更新同一个索引的情况。PostgreSQL 核心系统会在索引扫描期间对索引获取 AccessShareLock,并在更新索引时(包括普通的 VACUUM)获取 RowExclusiveLock。由于这些锁类型彼此不冲突,所以访问方法必须自行处理它可能需要的任何细粒度锁定。对整个索引的排他锁只会在索引创建、销毁或 REINDEX 期间获取。

构建一种支持并发更新的索引类型,通常需要对所需行为进行大量而细致的分析。对于 B-树和 hash 索引类型,可以阅读 src/backend/access/nbtree/READMEsrc/backend/access/hash/README 中涉及的设计决策。

除了索引自身内部一致性的要求外,并发更新还会带来父表(即 )与索引之间一致性的问题。由于 PostgreSQL 把对堆的访问和更新与对索引 的访问和更新分离开来,因此存在一些窗口期,在这些窗口期内索引 可能与堆不一致。我们通过以下规则处理这个问题:

  • 先创建新的堆条目,再创建它对应的索引条目。(因此并发索引 扫描很可能看不到这个堆条目。这是可以接受的,因为索引读取者 本来也不会关心未提交的行。但也请见 第 52.5 节。)

  • 当某个堆条目将要被删除(由 VACUUM 执行)时, 必须先删除它的所有索引条目。

  • 索引扫描必须在保存 amgettuple 最近一次返回条目的 索引页面上保持钉住状态(pin),而 ambulkdelete 不能从被其他后端钉住的页面中删除条目。下面会解释为什么需要 这条规则。

Without the third rule, it is possible for an index reader to see an index entry just before it is removed by VACUUM, and then to arrive at the corresponding heap entry after that was removed by VACUUM. This creates no serious problems if that item number is still unused when the reader reaches it, since an empty item slot will be ignored by heap_fetch(). But what if a third backend has already re-used the item slot for something else? When using an MVCC-compliant snapshot, there is no problem because the new occupant of the slot is certain to be too new to pass the snapshot test. However, with a non-MVCC-compliant snapshot (such as SnapshotNow), it would be possible to accept and return a row that does not in fact match the scan keys. We could defend against this scenario by requiring the scan keys to be rechecked against the heap row in all cases, but that is too expensive. Instead, we use a pin on an index page as a proxy to indicate that the reader might still be in flight from the index entry to the matching heap entry. Making ambulkdelete block on such a pin ensures that VACUUM cannot delete the heap entry before the reader is done with it. This solution costs little in run time, and adds blocking overhead only in the rare cases where there actually is a conflict.

这种解决方案要求索引扫描是同步的:我们必须在扫描到相应索引条目之后立刻取出对应的堆元组。由于多种原因,这会比较昂贵。相反,异步扫描可以先从索引中收集许多 TID,再在稍后某个时刻访问堆元组,这样索引锁定的开销要小得多,也能实现更高效的堆访问模式。根据前面的分析,对于非 MVCC 兼容快照,我们必须采用同步方式;而对于使用 MVCC 快照的查询,异步扫描则是可行的。

amgetbitmap 索引扫描中,访问方法不会为任何返回的元组保持索引页面的钉住状态。因此,只有把这种扫描与 MVCC 兼容的快照一起使用才是安全的。

当未设置 ampredlocks 标志时,在可串行化事务中使用该索引访问方法的任何扫描,都会在整个索引上获取一个非阻塞谓词锁。这会与并发可串行化事务向该索引插入任何元组形成读写冲突。如果在一组并发可串行化事务之间检测到某些特定模式的读写冲突,为了保护数据完整性,其中某个事务可能会被取消。设置该标志则表示该索引访问方法实现了更细粒度的谓词锁,这通常会降低这类事务取消的频率。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。