07 - 总结:一张表看懂所有锁
走到最后一章。前面的锁概念看起来很多,其实可以用几个维度串起来。这一章把它们汇总成速查表和决策图,方便你随时回看。
一句话总结全系列:锁是「用排队换正确」;PostgreSQL 靠 MVCC 让读写不互相阻塞,只在写写冲突时才真正排队。
7.1 锁的分类全景图
数据库锁
│
┌──────────────────┼──────────────────┐
按粒度分 按模式分 按策略分(思路)
(锁多大范围) (读锁还是写锁) (怎么应对冲突)
│ │ │
┌────┴────┐ ┌────┴────┐ ┌─────┴─────┐
表锁 行锁 共享锁 排他锁 悲观锁 乐观锁
(整张表) (几行) (读锁) (写锁) (先锁再改) (版本号检测)
并发差 并发好 读读相容 独占 冲突多用 冲突少用
7.2 核心概念速查表
| 概念 | 一句话 | 关键区别 |
|---|---|---|
| 表锁 | 锁整张表 | 简单、并发差,用于改表结构 |
| 行锁 | 只锁涉及的行 | 并发好、开销大,日常主力 |
| 共享锁(S/读锁) | 我在读,你也能读 | 读读相容 |
| 排他锁(X/写锁) | 我在改,谁都别碰 | 读写、写写互斥 |
| 悲观锁 | 假设会冲突,先锁再改 | FOR UPDATE,冲突多时用 |
| 乐观锁 | 假设不冲突,版本号检测 | 不加锁 + 重试,冲突少时用 |
| MVCC | 多版本,读写不阻塞 | PG 并发核心,写写仍靠行锁 |
| 死锁 | 互相等锁成环 | 统一加锁顺序来避免 |
7.3 兼容性规则(背下来)
读 + 读 → ✅ 相容(可以同时)
读 + 写 → ❌ 互斥(要排队)
写 + 写 → ❌ 互斥(要排队)
口诀:读读相容,沾写就斥。
在 PostgreSQL 里,因为 MVCC 的存在,还要补一句:
普通读(SELECT)不加锁 → 靠 MVCC 读快照,读写实际不阻塞
只有显式 FOR UPDATE / FOR SHARE,或 UPDATE/DELETE,才加行锁
7.4 实战决策:并发场景该怎么办
遇到「先查后改」的并发场景?
│
▼
能不能用一条 UPDATE 搞定?(如 SET x = x - 1 WHERE ...)
│
┌────┴────┐
能 不能(必须在应用里读出来算)
│ │
▼ ▼
直接 UPDATE 冲突激烈吗?
(天然原子) │
┌────┴────┐
激烈 不激烈
│ │
▼ ▼
悲观锁 乐观锁
(FOR UPDATE) (版本号+重试)
7.5 PostgreSQL vs MySQL(InnoDB) 的关键差异(拓展)
如果你以后接触 MySQL,注意这些不同:
| 方面 | PostgreSQL | MySQL InnoDB |
|---|---|---|
| 并发核心 | MVCC(旧版本单独存,靠 VACUUM 清理) | MVCC(旧版本存在 undo log) |
| 幻读处理 | Repeatable Read 就防住了幻读 | 靠间隙锁(Gap Lock) 防幻读 |
| 间隙锁 | 没有 InnoDB 那种间隙锁概念 | 有,锁「行之间的间隙」防插入 |
| 意向锁 | 概念较弱 | 明确的表级意向锁 |
结论:通用理论(粒度、模式、乐观悲观)两者相通,但具体实现细节(尤其是幻读和间隙锁)差别较大。学 PG 时不必纠结 InnoDB 的间隙锁。
7.6 五条核心心法
1. 没有并发就没有锁 —— 锁是为了并发正确
2. 读读相容,沾写就斥 —— 兼容性的核心
3. PG 靠 MVCC,读写不互相挡 —— 只有写写才真正排队
4. 冲突少用乐观锁,冲突多用悲观锁 —— 实战选择
5. 统一加锁顺序 + 缩短事务 + 应用重试 —— 防死锁三件套
结语
锁的世界看起来名词繁多,但拆开看,无非是:
在「多大范围」上、加「读锁还是写锁」、用「先锁还是后检测」的策略,来保证并发下的数据正确。而 PostgreSQL 用 MVCC 把「读」这条最频繁的路径从锁里解放了出来。
把这句话理解透,再配合本章的速查表,你就能从容应对绝大多数并发场景。
上一章 ← 06 - 死锁:产生、排查与避免 | 回到 README