数据库事务的隔离级别是为了平衡并发性能和数据一致性而设计的标准,主要分为四个级别,按隔离程度从低到高排列如下:
1. 读未提交(Read Uncommitted)
- 问题允许:脏读、不可重复读、幻读。
- 特点:事务可以读取其他未提交事务的修改。这是最低的隔离级别,性能高但一致性差。适用于对数据准确性要求不高的场景。
2. 读已提交(Read Committed)
- 解决的问题:脏读。
- 问题允许:不可重复读、幻读。
- 特点:事务只能读取已提交的数据。大多数数据库(如Oracle、PostgreSQL)的默认级别。通过行级锁或MVCC实现,但同一事务多次读取可能结果不同。
3. 可重复读(Repeatable Read)
- 解决的问题:脏读、不可重复读。
- 问题允许:幻读(部分数据库如MySQL通过Next-Key锁可避免)。
- 特点:保证同一事务中多次读取同一数据的结果一致。MySQL的默认隔离级别。通过事务期间持有锁或多版本控制实现。
4. 串行化(Serializable)
- 解决的问题:脏读、不可重复读、幻读。
- 特点:强制事务串行执行,完全隔离。一致性最高,但并发性能最差。适用于对数据准确性要求极高的场景。
并发问题说明
1. 脏读(Dirty Read)
定义:事务A读取了事务B 未提交 的修改,若事务B回滚,事务A读到的是无效数据。
场景示例:银行转账
- 事务A(转账操作):
BEGIN;
UPDATE account SET balance = balance - 100 WHERE user = 'Alice';
-- Alice扣款100元
UPDATE account SET balance = balance + 100 WHERE user = 'Bob';
-- Bob收款100元(未提交)
- 事务B(查询Bob余额):
BEGIN;
SELECT balance FROM account WHERE user = 'Bob'; -- 读到了+100元(但事务A未提交)
- 事务A回滚:
ROLLBACK; -- 转账失败,数据恢复原状
- 结果: • 事务B读到了事务A未提交的余额(Bob账户显示+100元),但实际转账未成功,导致数据不一致。
隔离级别影响:
- 若使用 读未提交(Read Uncommitted):允许脏读。
- 若使用 读已提交(Read Committed)及以上:事务B会阻塞,直到事务A提交或回滚后才读取结果。
2. 不可重复读(Non-repeatable Read)
定义:同一事务中多次读取同一数据,结果不同(其他事务 提交了更新)。
场景示例:用户余额查询
- 事务A(多次查询余额):
BEGIN;
SELECT balance FROM account WHERE user = 'Alice'; -- 第一次查询:余额500元
- 事务B(修改余额并提交):
BEGIN;
UPDATE account SET balance = 400 WHERE user = 'Alice'; -- 扣款100元
COMMIT;
- 事务A再次查询:
SELECT balance FROM account WHERE user = 'Alice'; -- 第二次查询:余额400元
COMMIT;
- 结果: • 事务A在同一个事务中两次读取Alice的余额,结果不一致(500元 → 400元)。
隔离级别影响:
- 若使用 读已提交(Read Committed):允许不可重复读。
- 若使用 可重复读(Repeatable Read)及以上:事务A的两次查询结果一致(通过MVCC或锁机制保证)。
3. 幻读(Phantom Read)
定义:同一事务中多次范围查询,结果集的行数不同(其他事务 提交了插入或删除)。
场景示例:统计订单数量
- 事务A(统计订单):
BEGIN;
SELECT COUNT(*) FROM orders WHERE user = 'Alice'; -- 第一次查询:3个订单
- 事务B(插入新订单并提交):
BEGIN;
INSERT INTO orders (user, amount) VALUES ('Alice', 200); -- 新增一个订单
COMMIT;
- 事务A再次统计:
SELECT COUNT(*) FROM orders WHERE user = 'Alice'; -- 第二次查询:4个订单
COMMIT;
- 结果: • 事务A的两次范围查询结果集行数不同(3 → 4),出现“幻影行”。
隔离级别影响:
- 若使用 可重复读(Repeatable Read): • MySQL通过Next-Key锁避免幻读。 • 其他数据库(如PostgreSQL)在可重复读级别下可能仍允许幻读。
- 若使用 串行化(Serializable):完全禁止幻读。
三者的核心区别
问题类型 | 操作类型 | 关键特征 | 示例场景 |
---|---|---|---|
脏读 | 读取未提交的数据 | 读到其他事务未提交的修改 | 转账中途读取到未提交余额 |
不可重复读 | 更新(UPDATE) | 同一数据多次读取结果不同 | 余额在查询期间被其他事务修改 |
幻读 | 插入或删除(INSERT/DELETE) | 范围查询的结果集行数变化 | 统计订单数量时新增了订单 |
数据库差异
- MySQL:默认隔离级别为可重复读,InnoDB引擎通过Next-Key锁避免幻读。
- PostgreSQL/Oracle:默认级别为读已提交。
- SQL Server:默认为读已提交,但可通过选项调整。
选择合适的隔离级别需权衡数据一致性和性能。低级别适合高并发但容忍暂时不一致的场景,高级别则用于需要强一致性的关键业务。