很多人听完 MVCC 会误以为:
“PostgreSQL 不靠锁。”
不对。
更准确地说是:
PostgreSQL 尽量不用锁解决读写可见性问题,但仍然大量使用锁解决冲突问题。
锁分几类,你先抓最实用的两类:
1. 表级锁 用于控制整个表上的某些操作能不能并行。
例如:
- 普通
SELECT拿很弱的锁 TRUNCATE会拿很强的锁- 某些 DDL 会阻塞别的读写
2. 行级锁 用于控制“同一行”能不能被别人改。
例如抢占一张通行证:
BEGIN;
SELECT *
FROM permits
WHERE permit_id = 42
FOR UPDATE;
-- 当前事务确认后再改
UPDATE permits
SET status = 'revoked'
WHERE permit_id = 42;
COMMIT;FOR UPDATE 的意思可以理解为:
先把这份档案拿到锁柜里,别人现在不能改它。
常见行锁模式有:
FOR UPDATEFOR NO KEY UPDATEFOR SHAREFOR KEY SHARE
你不用一开始全背,但要记住:
查询也可以加锁。
最常见的死锁长这样:
事务 A:先锁 1 号档案,再等 2 号档案
事务 B:先锁 2 号档案,再等 1 号档案于是彼此卡死。
PostgreSQL 会自动检测死锁,并回滚其中一个事务。
避免死锁最有效的方法,不是祈祷,而是:
- 多表、多行更新时保持固定顺序
- 事务尽量短
- 不要开着事务等用户点按钮
- 真要高一致性,做好失败重试