事务把多条语句组成一个原子单元:要么全部提交,要么全部回滚,从而给出 ACID 中的 A(原子性)、C(一致性)、I(隔离性)、D(持久性)。隔离级别调节的是其中的"I" —— 即并发事务之间能看到彼此多少未提交或正在变化的数据。
| 级别 | 脏读 | 不可重复读 | 幻读 |
|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 否 | 可能 | 可能 |
| REPEATABLE READ(InnoDB 默认) | 否 | 否 | 大多不会* |
| SERIALIZABLE | 否 | 否 | 否 |
*InnoDB 的 REPEATABLE READ 使用 MVCC(每个事务读取一致性快照)加上 next-key 锁,因此也能阻止大多数幻读 —— 比 SQL 标准要求的更强。
SELECT @@transaction_isolation; -- 默认为 REPEATABLE-READ
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 在此建立快照
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 两条更新一起生效,或用 ROLLBACK 一起撤销
除非有理由,否则保持 InnoDB 的默认级别(REPEATABLE READ)。READ COMMITTED(Postgres/Oracle 的默认级别)能为高写入系统减少锁争用;SERIALIZABLE 正确,但会把冲突的工作串行化,且更容易发生死锁。
隔离级别是正确性与并发性之间的旋钮。理解这些异常,能让你解释诸如"负载下总额对不上"这类 bug,并选择一个既能保证资金流转事务正确、又不至于无谓限制吞吐量的级别。