SQL 标准的隔离级别
数据库不是都串行,而是提供不同的隔离等级,在 性能 vs 隔离 之间取平衡:
- Read Uncommitted(读未提交)
- 可以读到别的事务还没提交的数据(脏读)。
- 几乎不用,隔离性最低,性能最好。
- Read Committed(读已提交)
- 只能读到已提交的数据。
- 常见问题:不可重复读(同一条记录在同一事务里读两次,可能不一样)。
- Oracle, PostgreSQL, SQL Server 默认级别。
- PostgreSQL 用的是 行级锁 (Row-Level Lock)。
- 当事务更新某一行时,别的事务不能同时更新那一行(会等锁)。
- 所以不会出现“两个人同时改同一行,最后一个覆盖掉前一个”的情况。
- Repeatable Read(可重复读)
- 使用 MVCC 快照,保证同一事务内多次查询结果一致。
- 但可能出现 幻读(比如别的transaction再插入新行就看不到)。
- MySQL 默认级别(InnoDB 实现时用 MVCC 避免幻读)。
- Serializable(可串行化)
- 强制事务串行执行(加锁或序列化),避免所有并发问题。
- 隔离性最好,但性能最差(几乎就是你说的“只能一个个执行”)。
- 最强隔离级别,PostgreSQL 用 可串行化快照隔离 (SSI, Serializable Snapshot Isolation) 实现。
- 不是真的全串行,而是通过检测事务之间的冲突,如果检测到可能违背串行一致性,会让事务失败并回滚。
- 所以应用端需要捕获错误并重试事务。
实现方式
数据库不会真的把所有事务串行执行(那性能太差),常见实现方式有:
-
MVCC(多版本并发控制):
给数据加时间戳/版本号,每个事务读写自己的“快照”。
→ MySQL InnoDB、PostgreSQL 默认机制。
-
锁机制(Locking):
对行/表加共享锁或排它锁,控制访问顺序。
→ SQL Server、Oracle 都大量使用锁。
这样保证隔离性的同时,允许并发。
举个例子
两个事务并发执行:
-- 事务 A
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 事务 B
BEGIN;
UPDATE account SET balance = balance - 200 WHERE id = 1;
- 在 Read Uncommitted 下,B 可能读到 A 的未提交余额。
- 在 Read Committed 下,B 只能读到 A 提交后的余额。
- 在 Repeatable Read 下,B 在事务中始终看到一致的余额(通过 MVCC 快照)。
- 在 Serializable 下,数据库会让 A、B 串行执行,避免冲突。