Skip to content

隔离级别:READ UNCOMMITTED 到 SERIALIZABLE ​

并发问题 ​

多个事务同时访问数据时,会产生以下问题:

1. 脏读(Dirty Read) ​

读取到其他事务未提交的数据。

sql
-- 事务 A
BEGIN;
UPDATE accounts SET balance = 200 WHERE id = 1;
-- 未提交

-- 事务 B
BEGIN;
SELECT balance FROM accounts WHERE id = 1;  -- 200(脏数据)
COMMIT;

-- 事务 A
ROLLBACK;  -- 回滚,balance 恢复为 100

事务 B 读到了事务 A 未提交的数据(200),但 A 最终回滚了。

2. 不可重复读(Non-repeatable Read) ​

同一事务中两次读取同一数据,结果不同。

sql
-- 事务 A
BEGIN;
SELECT balance FROM accounts WHERE id = 1;  -- 100

-- 事务 B
BEGIN;
UPDATE accounts SET balance = 200 WHERE id = 1;
COMMIT;

-- 事务 A
SELECT balance FROM accounts WHERE id = 1;  -- 200(不同了)
COMMIT;

3. 幻读(Phantom Read) ​

同一事务中两次查询,结果集的行数不同。

sql
-- 事务 A
BEGIN;
SELECT COUNT(*) FROM orders WHERE user_id = 1;  -- 5 行

-- 事务 B
BEGIN;
INSERT INTO orders (user_id, product, amount) VALUES (1, 'Mouse', 25);
COMMIT;

-- 事务 A
SELECT COUNT(*) FROM orders WHERE user_id = 1;  -- 6 行(多了一行)
COMMIT;

四种隔离级别 ​

SQL 标准定义了四种隔离级别,从低到高:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED可能可能可能
READ COMMITTED不可能可能可能
REPEATABLE READ不可能不可能可能
SERIALIZABLE不可能不可能不可能

READ UNCOMMITTED:读未提交 ​

最低隔离级别,允许脏读。

sql
-- PostgreSQL
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- MySQL
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;

特点:

  • 允许读取未提交的数据
  • 性能最高,并发最好
  • 数据一致性最差

应用场景:统计报表、日志分析(允许不精确)

READ COMMITTED:读已提交 ​

只能读取已提交的数据,防止脏读。

sql
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT balance FROM accounts WHERE id = 1;  -- 只看到已提交的值
COMMIT;

特点:

  • 防止脏读
  • 允许不可重复读和幻读
  • PostgreSQL 默认级别

应用场景:大多数 Web 应用、电商系统

REPEATABLE READ:可重复读 ​

同一事务中多次读取同一数据,结果一致。

sql
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT balance FROM accounts WHERE id = 1;  -- 100
-- 其他事务修改并提交
SELECT balance FROM accounts WHERE id = 1;  -- 仍然是 100
COMMIT;

特点:

  • 防止脏读和不可重复读
  • 允许幻读(SQL 标准)
  • MySQL InnoDB 默认级别(实际上也防止幻读)

应用场景:金融系统、需要数据一致性的应用

SERIALIZABLE:可串行化 ​

最高隔离级别,完全隔离,如同事务串行执行。

sql
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT * FROM orders WHERE user_id = 1;
-- 其他事务无法插入、更新、删除该用户的订单
COMMIT;

特点:

  • 防止所有并发问题
  • 性能最低,并发最差
  • 可能产生死锁

应用场景:关键金融交易、需要严格一致性的场景

设置隔离级别 ​

会话级别 ​

sql
-- PostgreSQL
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- MySQL
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

事务级别 ​

sql
-- PostgreSQL
BEGIN ISOLATION LEVEL REPEATABLE READ;

-- MySQL
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;

查看当前隔离级别 ​

sql
-- PostgreSQL
SHOW TRANSACTION ISOLATION LEVEL;

-- MySQL
SELECT @@transaction_isolation;

PostgreSQL vs MySQL ​

PostgreSQL ​

  • 默认:READ COMMITTED
  • REPEATABLE READ 使用 MVCC,防止幻读
  • SERIALIZABLE 使用 SSI(Serializable Snapshot Isolation)

MySQL InnoDB ​

  • 默认:REPEATABLE READ
  • REPEATABLE READ 使用 Next-Key Locks,防止幻读
  • READ UNCOMMITTED 很少使用

实际测试 ​

测试脏读 ​

sql
-- 会话 1
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;

-- 会话 2
BEGIN;
UPDATE accounts SET balance = 200 WHERE id = 1;
-- 不提交

-- 会话 1
SELECT balance FROM accounts WHERE id = 1;  -- 200(脏读)

-- 会话 2
ROLLBACK;

-- 会话 1
SELECT balance FROM accounts WHERE id = 1;  -- 100(之前读到的是脏数据)
COMMIT;

测试不可重复读 ​

sql
-- 会话 1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT balance FROM accounts WHERE id = 1;  -- 100

-- 会话 2
BEGIN;
UPDATE accounts SET balance = 200 WHERE id = 1;
COMMIT;

-- 会话 1
SELECT balance FROM accounts WHERE id = 1;  -- 200(不可重复读)
COMMIT;

测试幻读 ​

sql
-- 会话 1
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT COUNT(*) FROM orders WHERE user_id = 1;  -- 5

-- 会话 2
INSERT INTO orders (user_id, product, amount) VALUES (1, 'Mouse', 25);
COMMIT;

-- 会话 1(MySQL)
SELECT COUNT(*) FROM orders WHERE user_id = 1;  -- 仍然 5(防止幻读)

-- 会话 1(PostgreSQL)
SELECT COUNT(*) FROM orders WHERE user_id = 1;  -- 仍然 5(MVCC)
COMMIT;

选择隔离级别 ​

场景推荐级别原因
Web 应用READ COMMITTED平衡性能与一致性
金融系统REPEATABLE READ / SERIALIZABLE严格一致性
统计报表READ UNCOMMITTED允许不精确,提高性能
秒杀活动SERIALIZABLE防止超卖
普通查询默认级别无需特殊处理

性能影响 ​

隔离级别越高,性能越低:

text
READ UNCOMMITTED  <-- 最快
READ COMMITTED
REPEATABLE READ
SERIALIZABLE      <-- 最慢

原因:

  • 更高级别需要更多锁
  • 更多的冲突和等待
  • SERIALIZABLE 可能需要重试事务

练习 ​

  1. 测试脏读:设置 READ UNCOMMITTED,复现脏读场景。

  2. 测试不可重复读:设置 READ COMMITTED,观察不可重复读。

  3. 测试幻读:设置 REPEATABLE READ,测试是否防止幻读。

  4. 隔离级别对比:同一查询在不同隔离级别下的结果对比。

  5. 性能测试:对比不同隔离级别下的并发插入性能。

  6. 死锁模拟:在 SERIALIZABLE 下模拟死锁。

  7. 查看默认级别:查看数据库的默认隔离级别。

  8. 实际场景:设计秒杀活动,选择合适的隔离级别防止超卖。

完成练习后,打开本章答案页核对。

下一步 ​

学会了隔离级别。下一节学习锁机制:理解行锁、表锁、意向锁,掌握死锁的处理。

回到第五章概览 · 返回SQL 学习路线

用心记录每一步成长