PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

LOCK

LOCK — 锁定一个表

Synopsis

LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]

where lockmode is one of:

    ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE
    | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE

描述

LOCK TABLE 获取一个表级锁,必要时会等待任何冲突的锁被释放。如果指定了 NOWAIT,LOCK TABLE 不会为了获取想要的锁而等待:如果不能立即获取到锁,该命令会被中止并发出一个错误。一旦获得,锁将被持有到当前事务结束。 (没有 UNLOCK TABLE 命令;锁总是在事务结束时被释放。)

当一个视图被锁定时,出现在视图定义查询中的所有关系也会用相同的锁模式被递归地锁定。

当为引用表的命令自动获取锁时,PostgreSQL 总是使用限制性最小的锁模式。LOCK TABLE 为你可能需要更具限制性锁定的情形提供了一种手段。例如,假设一个应用在 READ COMMITTED 隔离级别运行一个事务,并且需要确保一个表中的数据在该事务的持续期间保持稳定。要做到这一点,你可以在查询之前获取该表上的 SHARE 锁模式。这将阻止并发的数据更改,并确保对该表的后续读取能看到已提交数据的一个稳定视图,因为 SHARE 锁模式与写者获取的 ROW EXCLUSIVE 锁冲突,而你的 LOCK TABLE name IN SHARE MODE 语句将一直等待,直到任何并发的 ROW EXCLUSIVE 模式锁的持有者提交或者回滚。因此,一旦你获得了锁,就不再有未提交的写操作残留;而且在你释放锁之前,也不会有新的写操作开始。

要在 REPEATABLE READ 或者 SERIALIZABLE 隔离级别运行事务时达到类似的效果,你必须先执行 LOCK TABLE 语句,然后才能执行任何 SELECT 或者数据修改语句。一个 REPEATABLE READ 或者 SERIALIZABLE 事务的数据视图会在它的第一个 SELECT 或者数据修改语句开始时被冻结。在事务中较晚执行的 LOCK TABLE 仍然会阻止并发写 — 但是它不能确保该事务读取的内容对应于最新提交的值。

如果这类事务将要更改表中的数据,那么它应该使用 SHARE ROW EXCLUSIVE 锁模式而不是 SHARE 模式。这能确保这种类型的事务在同一时间只有一个在运行。否则就可能发生死锁:两个事务可能都获取了 SHARE 模式,然后都无法再获取 ROW EXCLUSIVE 模式来真正执行它们的更新。 (注意事务自己的锁从不与自身冲突,所以一个事务可以获取 ROW EXCLUSIVE 模式并同时持有 SHARE 模式 — 但如果其他任何人持有 SHARE 模式则不行。)为了避免死锁,请确保所有事务以相同的顺序在相同的对象上获取锁,并且如果一个对象涉及多种锁模式,那么事务应该总是先获取限制性最强的模式。

关于锁模式和锁定策略的更多信息可以在 Section 13.3 中找到。

参数

name

要锁定的现有表的名称(可以选择用模式限定)。如果在表名之前指定了 ONLY,则只有该表会被锁定。如果没有指定 ONLY,则该表及其所有后代表(如果有)都会被锁定。可选地,可以在表名之后指定 *,以显式指示包含后代表。

命令 LOCK TABLE a, b; 等价于 LOCK TABLE a; LOCK TABLE b;。这些表会按照 LOCK TABLE 命令中指定的顺序被逐一锁定。

lockmode

锁模式指定了这个锁与哪些锁冲突。锁模式在 Section 13.3 中描述。

如果没有指定锁模式,则使用限制性最强的模式 ACCESS EXCLUSIVE。

NOWAIT

指定 LOCK TABLE 不应等待任何冲突的锁被释放:如果不能立即且无需等待地获取指定的锁,事务将被中止。

注记

要锁定一个表,用户必须对指定的 lockmode 拥有正确的特权。如果用户在该表上拥有 MAINTAIN、UPDATE、DELETE 或者 TRUNCATE 特权,则允许任何 lockmode。如果用户在该表上拥有 INSERT 特权,则允许 ROW EXCLUSIVE MODE(或者 Section 13.3 中描述的冲突更少的模式)。如果用户在该表上拥有 SELECT 特权,则允许 ACCESS SHARE MODE。

在视图上执行锁定的用户必须拥有该视图上相应的特权。此外,在默认情况下,视图的拥有者必须拥有底层基本关系上的相关特权,而执行锁定的用户不需要底层基本关系上的任何权限。但是,如果视图将 security_invoker 设置为 true(参见 CREATE VIEW),则必须是执行锁定的用户(而不是视图的拥有者)拥有底层基本关系上的相关特权。

LOCK TABLE 在事务块之外是无用的:锁将只被持有到该语句完成。因此,PostgreSQL 会在 LOCK 被用在事务块之外时报告一个错误。请使用 BEGIN 和 COMMIT(或者 ROLLBACK)来定义一个事务块。

LOCK TABLE 只处理表级锁,因此涉及 ROW 的模式名都是名不副实的。这些模式名通常应该被理解为表示用户想要在被锁定的表内获取行级锁的意图。此外,ROW EXCLUSIVE 模式是一种可共享的表锁。请记住,就 LOCK TABLE 而言,所有锁模式的语义都是相同的,不同的只是关于哪些模式与哪些模式冲突的规则。有关如何获取真正的行级锁的信息,请参见 Section 13.3.2 和 The Locking Clause(位于 SELECT 文档中)。

示例

在即将向一个外键表执行插入时,在一个主键表上获取 SHARE 锁:

BEGIN WORK;
LOCK TABLE films IN SHARE MODE;
SELECT id FROM films
    WHERE name = 'Star Wars: Episode I - The Phantom Menace';
-- Do ROLLBACK if record was not returned
INSERT INTO films_user_comments VALUES
    (_id_, 'GREAT! I was waiting for it for so long!');
COMMIT WORK;

在即将执行一个删除操作时,在一个主键表上获取 SHARE ROW EXCLUSIVE 锁:

BEGIN WORK;
LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE;
DELETE FROM films_user_comments WHERE id IN
    (SELECT id FROM films WHERE rating < 5);
DELETE FROM films WHERE rating < 5;
COMMIT WORK;

兼容性

SQL 标准中没有 LOCK TABLE,标准改用 SET TRANSACTION 来指定事务上的并发级别。PostgreSQL 也支持这种方式;细节请参见 SET TRANSACTION。

除了 ACCESS SHARE、ACCESS EXCLUSIVE 和 SHARE UPDATE EXCLUSIVE 锁模式之外,PostgreSQL 的锁模式和 LOCK TABLE 语法都与 Oracle 中存在的那些兼容。