PostgreSQL - 锁
PostgreSQL - 并发与锁
Section titled “PostgreSQL - 并发与锁”理解 MVCC 并发控制
Section titled “理解 MVCC 并发控制”PostgreSQL 使用一种名为多版本并发控制(MVCC)的复杂模型来管理并发。与使用读锁的系统不同,MVCC 允许读写操作同时进行而互不阻塞。当一行数据被更新时,PostgreSQL 会创建该行的新版本而不是覆盖旧版本。事务会看到数据库的一致“快照”,确保读取操作是非阻塞的,并且具有高性能。
由于 MVCC,您通常不需要手动锁定表进行读取。PostgreSQL 会自动处理大部分锁定,并且是在行级别进行。例如,对一行的 UPDATE 或 DELETE 操作会在该行上自动放置一个排他锁,并在事务期间一直保持,从而阻止其他事务修改它。
显式锁定:何时以及为何使用
Section titled “显式锁定:何时以及为何使用”虽然自动锁定足以满足大多数操作的需求,但在某些情况下,您需要更高级别的手动控制锁定(例如,整个表)。这通过 LOCK 命令完成,该命令必须在事务块 (BEGIN...COMMIT) 内部执行。
显式锁定的常见用例包括:
- 执行复杂的、多阶段的数据更新或维护,在此期间您必须阻止任何并发修改以确保一致性。
- 防止其他会话获取可能与您的事务长时间运行操作冲突的更强锁。
BEGIN;LOCK TABLE table_name IN lock_mode;-- ... your operations here ...-- ... 您的操作在此处 ...COMMIT;PostgreSQL 提供了各种锁模式,从限制性最小到限制性最大。其中几个关键的模式是:
ACCESS SHARE(由SELECT获取):仅与ACCESS EXCLUSIVE冲突。ROW EXCLUSIVE(由UPDATE、INSERT、DELETE获取):与SHARE和EXCLUSIVE模式冲突。SHARE:允许其他会话读取(通过SELECT),但不允许修改表。ACCESS EXCLUSIVE:限制性最强的模式。与所有其他锁冲突。阻止任何其他事务读取或写入表。DROP TABLE使用的就是这种模式。
没有 UNLOCK TABLE 命令。事务期间获取的所有锁都会在事务以 COMMIT 或 ROLLBACK 结束时自动释放。
当两个(或更多)事务相互等待对方释放锁时,就会发生死锁。PostgreSQL 的锁管理器可以自动检测死锁。当检测到死锁时,它会通过引发错误来终止其中一个事务,允许另一个事务继续执行。
死锁示例:
-- Session 1:-- 会话 1:BEGIN;UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- Session 2:-- 会话 2:BEGIN;UPDATE accounts SET balance = balance - 200 WHERE id = 2;
-- Session 1 (now waits for Session 2 to release lock on id=2):-- 会话 1(现在等待会话 2 释放 id=2 上的锁):UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- Session 2 (now waits for Session 1 to release lock on id=1 -> DEADLOCK):-- 会话 2(现在等待会话 1 释放 id=1 上的锁 -> 死锁):UPDATE accounts SET balance = balance + 200 WHERE id = 1;-- ERROR: deadlock detected-- 错误:检测到死锁最佳实践: 为避免死锁,请确保所有访问多个资源的应用都以相同、一致的顺序锁定它们。
咨询锁(Advisory locks)是一种协作式锁定机制。PostgreSQL 不会自动强制执行它们;这取决于应用程序逻辑来正确地获取和释放它们。它们对于管理应用程序级别的并发非常有用,例如确保后台作业只有一个实例在同时运行。
示例:确保单个后台工作进程。
-- A background worker process would run this:-- 一个后台工作进程会运行此命令:-- The number 12345 is an arbitrary key for our lock.-- 数字 12345 是我们锁定的任意键。
SELECT pg_try_advisory_xact_lock(12345);
-- If the result is 'true', the lock was acquired.-- 如果结果是 'true',则表示锁已获取。-- If 'false', another worker already holds the lock.-- 如果是 'false',则表示另一个工作进程已持有该锁。
-- The lock is automatically released at the end of the transaction.-- 锁将在事务结束时自动释放。