pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PostgreSQL 提供多种锁模式来控制对表中数据的并发访问。当 MVCC 无法给出期望的行为时,这些模式可用于由应用控制的锁定。此外,大多数 PostgreSQL 命令会自动获取适当模式的锁,以确保被引用的表在命令执行期间不会以不兼容的方式被删除或修改。(例如,ALTER TABLE 不能与同一张表上的其他操作并发执行。)
下面的列表展示了可用的锁模式,以及 PostgreSQL 自动使用它们的场景。请记住,所有这些锁模式都是表级锁,即使名称中包含 “row”(行)一词也是如此。这些锁模式的名称是历史遗留的。名称在某种程度上反映了每种锁模式的典型用法——但语义都是一样的。一种锁模式与另一种的唯一实质区别,是各自与之冲突的锁模式集合。两个事务不能同时在同一张表上持有相互冲突模式的锁。(不过,事务永远不会与自身冲突——例如,一个事务可以先取得 ACCESS EXCLUSIVE 锁,之后再在同一张表上取得 ACCESS SHARE 锁。)不冲突的锁模式可以由多个事务同时持有。特别要注意,有些锁模式是自冲突的(例如, ACCESS EXCLUSIVE 同一时刻只能被一个事务持有),而另一些不是自冲突的(例如, ACCESS SHARE 可以被多个事务持有)。锁一旦取得,就会保持到事务结束。
要查看数据库服务器中当前尚未释放的锁列表,可以使用 pg_locks 系统视图。有关监控锁管理器子系统状态的更多信息,请参见 PostgreSQL 7.3.21 管理员指南。
表级锁模式
ACCESS SHARE只与 ACCESS EXCLUSIVE 锁模式冲突。
SELECT 命令会在被引用的表上获取这种模式的锁。一般而言,任何只读表而不修改表的查询都会获取这种锁模式。
ROW SHARE与 EXCLUSIVE 和 ACCESS EXCLUSIVE 锁模式冲突。
SELECT FOR UPDATE 命令会在目标表上获取这种模式的锁(此外,对于引用到但没有用 FOR UPDATE 选取的其他表,还会获取 ACCESS SHARE 锁)。
ROW EXCLUSIVE与SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE锁模式冲突。
UPDATE、 DELETE 和 INSERT 命令会在目标表上获取这种锁模式(此外还会在其他被引用的表上获取 ACCESS SHARE 锁)。一般而言,任何修改表中数据的查询都会获取这种锁模式。
SHARE UPDATE EXCLUSIVE与 SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS EXCLUSIVE 锁模式冲突。这种模式用于保护表不受并发模式更改和 VACUUM 运行的影响。
由VACUUM(不带FULL)获取。
SHARE与 ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS EXCLUSIVE 锁模式冲突。这种模式用于保护表不受并发数据更改的影响。
由 CREATE INDEX 获取。
SHARE ROW EXCLUSIVE与ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE和ACCESS EXCLUSIVE锁模式冲突。
这种锁模式不会被任何 PostgreSQL 命令自动获取。
EXCLUSIVE与 ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS EXCLUSIVE 锁模式冲突。这种模式只允许并发的 ACCESS SHARE,也就是说,只有对该表的读取才能与持有此锁模式的事务并行进行。
这种锁模式不会被任何 PostgreSQL 命令自动获取。
ACCESS EXCLUSIVE与所有模式的锁冲突(ACCESS SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS EXCLUSIVE)。这种模式保证持有者是以任何方式访问该表的唯一事务。
由 ALTER TABLE、DROP TABLE 和 VACUUM FULL 命令获取。它也是未显式指定模式的 LOCK TABLE 语句的默认锁模式。
只有 ACCESS EXCLUSIVE 锁才会阻塞 SELECT(不带 FOR UPDATE)语句。
除了表级锁之外,还有行级锁。当某一行被更新(或删除或标记为更新)时,会自动在该行上获取行级锁。该锁会一直保持到事务提交或回滚。 行级锁不影响数据查询;它们只阻塞同一行的写者。要在不实际修改行的情况下获取某行的行级锁,可以用 SELECT FOR UPDATE 选取该行。注意,一旦取得了某一行的行级锁,事务就可以多次更新该行而不必担心冲突。
PostgreSQL 不会在内存中保存任何关于已修改行的信息,因此一次加锁的行数没有限制。不过,锁定一行可能会导致一次磁盘写;例如,SELECT FOR UPDATE 会修改被选中的行以标记它们,因此会产生磁盘写入。
除了表锁和行锁之外,还使用页级的共享/排他锁来控制对共享缓冲池中表页的读写访问。这些锁在取到或更新一个元组后立即释放。应用开发者通常不需要关心页级锁,但为完整性起见我们提一下。
使用显式锁定可能导致死锁,即两个(或更多)事务各自持有对方想要的锁。例如,事务 1 在表 A 上取得了排他锁,然后试图在表 B 上取得排他锁;而事务 2 已经在表 B 上取得了排他锁,现在又想在表 A 上取得排他锁,于是谁都无法继续。 PostgreSQL 会自动检测死锁情况,并通过中止其中一个涉及的事务来解决死锁,让其余事务得以完成。(究竟哪个事务会被中止很难预测,不应依赖于此。)
防范死锁的最佳办法通常是通过确保所有使用数据库的应用以一致的顺序在多个对象上获取锁来避免死锁。还应确保事务在一个对象上首先获取的锁是该对象所需的最高模式。如果无法提前验证这一点,那么可以在运行时重试因死锁而中止的事务来处理死锁。
只要没有检测到死锁,寻求表级锁或行级锁的事务就会无限期等待冲突锁被释放。这意味着让应用长时间保持事务打开并不是好主意(例如等待用户输入时)。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。