编程数据工程师

描述 SQL 中事务隔离(隔离级别)的使用原则,以及如何为应用程序选择正确的隔离级别。请为每个级别举例说明异常情况。

用 Hintsage AI 助手通过面试

答案

事务隔离 影响并发事务如何相互看到变化。这是 ACID 特性的一个重要部分。ANSI SQL 中有四个基本隔离级别:

  • 读取未提交(READ UNCOMMITTED) — 可以看到其他事务的未提交更改(脏读,dirty reads)。
  • 读取已提交(READ COMMITTED) — 只看到已提交的更改;防止脏读,但允许不可重复读(non-repeatable reads)。
  • 可重复读(REPEATABLE READ) — 同一事务中看到的数据保持不变。避免了脏读和不可重复读,但可能出现幻读(phantom reads)。
  • 可串行化(SERIALIZABLE) — 最严格,事务完全隔离,仿佛顺序执行;消除了所有类型的异常。

选择级别取决于应用程序的要求:

  • 对于报告,通常 REPEATABLE READ 或更高级别就足够;
  • 对于高负载系统,最优折衷是 READ COMMITTED;
  • 对于金融领域 — SERIALIZABLE,尽管性能下降。

示例:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; BEGIN; -- 后续 SELECT 将看到“冻结”的值

反向提问

“REPEATABLE READ 级别是否保证在任何数据库中保护幻读?”

不。在 PostgreSQL 和某些其他数据库中,REPEATABLE READ 级别只防止脏读和不可重复读,但不一定保护幻读。在 MySQL/InnoDB 中,REPEATABLE READ 本质上是 SERIALIZABLE,但在其他数据库中则不然。

示例:
-- 在一个事务中读取 SELECT * FROM orders WHERE amount > 100; -- 在另一个事务中插入新值 amount > 100 并提交 -- 如果隔离级别低于 SERIALIZABLE,第一个事务在重选时会看到“幻影”行

由于对主题细节了解不足而导致的真实错误示例


故事

一个金融服务仅将隔离级别设置为 READ COMMITTED 以提高性能 — 用户看到了已经被其他进程更改的金额,余额出现了不一致。


故事

在酒店预订系统中发生了同一房间的双重预订 — 事务没有隔离当前预订的加载,级别为 READ COMMITTED。


故事

从 MySQL 迁移到 PostgreSQL:开发人员习惯了 REPEATABLE READ 保护幻读,但迁移后出现了预料之外的“悬挂”订单,当在同一事务中进行重复查询时。