↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

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 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3
历史版本PostgreSQL 7.4 已于 2010 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

12.3. 显式锁定 #

PostgreSQL提供多种锁模式来控制对表中数据的并发访问。当MVCC无法给出期望的行为时,这些模式可用于由应用控制的锁定。此外,大多数PostgreSQL命令会自动获取适当模式的锁,以确保被引用的表在命令执行期间不会以不兼容的方式被删除或修改。(例如,ALTER TABLE不能与同一张表上的其他操作并发执行。)

要查看数据库服务器中当前尚未释放的锁列表,可以使用 pg_locks 系统视图(第 43.32 节)。有关监控锁管理器子系统状态的更多信息,请参见 第 23 章。

12.3.1. 表级锁 #

下面的列表展示了可用的锁模式,以及 PostgreSQL 自动使用它们的场景。 请记住,所有这些锁模式都是表级锁,即使名称中包含 “row”(行)一词也是如此;这些锁模式的名称是历史遗留的。名称在某种程度上反映了每种锁模式的典型用法——但语义都是一样的。一种锁模式与另一种的唯一实质区别,是各自与之冲突的锁模式集合。两个事务不能同时在同一张表上持有相互冲突模式的锁。(不过,事务永远不会与自身冲突。例如,一个事务可以先取得 ACCESS EXCLUSIVE 锁,之后再在同一张表上取得 ACCESS SHARE 锁。)不冲突的锁模式可以由多个事务同时持有。特别要注意,有些锁模式是自冲突的(例如, ACCESS EXCLUSIVE 锁同一时刻只能被一个事务持有),而另一些不是自冲突的(例如, ACCESS SHARE 锁可以被多个事务持有)。锁一旦取得,就会保持到事务结束。

表级锁模式

ACCESS SHARE

只与 ACCESS EXCLUSIVE 锁模式冲突。

SELECT 和 ANALYZE 命令会在被引用的表上获取这种模式的锁。通常,任何只读取表而不修改它的查询都会获取这种锁模式。

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、REINDEX、CLUSTER和VACUUM FULL命令获取。这也是未显式指定模式的LOCK TABLE语句的默认锁模式。

提示

只有 ACCESS EXCLUSIVE 锁才会阻塞 SELECT(不带 FOR UPDATE)语句。

12.3.2. 行级锁 #

除了表级锁之外,还有行级锁。当某一行被更新(或删除或标记为更新)时,会自动获得该特定行上的行级锁。这种锁一直保持到事务提交或回滚为止。行级锁不影响数据查询;它们只会阻塞对同一行的写入者。要在不实际修改某行的情况下获取该行上的行级锁,可以使用SELECT FOR UPDATE选择该行。注意,一旦获得了特定的行级锁,事务就可以多次更新该行而无须担心冲突。

PostgreSQL 不会在内存中保存任何关于已修改行的信息,因此一次加锁的行数没有限制。不过,锁定一行可能会导致一次磁盘写;例如,SELECT FOR UPDATE 会修改被选中的行以标记它们,因此会产生磁盘写入。

除了表级锁和行级锁之外,还使用页级共享/排他锁来控制对共享缓冲池中表页面的读写访问。这些锁会在取出或更新某一行后立即释放。应用开发者通常不需要关心页级锁,这里提到它们只是为了完整性。

12.3.3. 死锁 #

使用显式锁可能会增加死锁的发生概率。所谓死锁,是指两个(或更多)事务各自持有对方想要的锁。例如,如果事务 1 在表 A 上获得了排他锁,然后试图再获取表 B 上的排他锁,而事务 2 已经持有表 B 上的排他锁,现在又想获取表 A 上的排他锁,那么两者都无法继续进行。PostgreSQL 会自动检测死锁,并通过中止其中一个事务来解决这个问题,使其他事务得以继续完成。(具体会中止哪个事务很难预测,也不应依赖这种预测。)

还要注意,死锁也可能由于行级锁而发生(因此,即使没有使用显式锁,它们也可能出现)。考虑下面这种情况:两个并发事务都在修改一个表。第一个事务执行:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;

这会在指定账号的那一行上获取一个行级锁。然后第二个事务执行:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;

第一条 UPDATE 语句成功地在指定行上获取了一个行级锁,因此它成功更新了那一行。然而,第二条 UPDATE 语句发现它试图更新的行已经被锁住了,于是它等待持有该锁的事务结束。此时,事务二正在等待事务一结束之后才能继续执行。现在,事务一执行:

UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;

事务一试图在指定行上获取一个行级锁,但它做不到,因为事务二已经持有了这个锁。于是它必须等待事务二完成。这样一来,事务一被事务二阻塞,而事务二又被事务一阻塞:死锁条件形成了。PostgreSQL 会检测到这种情况,并中止其中一个事务。

防范死锁的最佳办法通常是通过确保所有使用数据库的应用以一致的顺序在多个对象上获取锁来避免死锁。前面死锁例子的原因正在于此:如果两个事务以相同的顺序更新行,就不会发生死锁。还应确保事务在一个对象上首先获取的锁是该对象所需的最高模式。如果无法提前验证这一点,那么可以在运行时重试因死锁而中止的事务来处理死锁。

只要没有检测到死锁,寻求表级锁或行级锁的事务就会无限期等待冲突锁被释放。这意味着让应用长时间保持事务打开并不是好主意(例如等待用户输入时)。

提交更正

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