Skip to content

两个事物修改同一条记录,这个时候再去读这个记录会怎么样 ?读到的结果是一样的还是不一样的?

两个事务修改同一条记录后,再去读这条记录的结果取决于 数据库的隔离级别事务执行的时序。结果可能一样(读到最新提交值),也可能不一样(读到不同版本或未提交值)。常见隔离级别下:

  • 读未提交:可能读到未提交的修改(脏读),结果不一样。
  • 读已提交:读到已提交的最新值,结果可能随提交顺序变化。
  • 可重复读:读到事务开始时的版本,结果一致但可能不是最新。
  • 串行化:严格顺序执行,结果一致且最新。

关键事实

  1. 并发修改
    • 两个事务(T1、T2)同时修改同一条记录,涉及锁或 MVCC(多版本并发控制)。
  2. 读时机
    • 读发生在修改前、中、后,结果不同。
  3. 隔离级别影响
    • 数据库(如 MySQL InnoDB)通过隔离级别控制读到的数据版本。

具体分析(以 MySQL InnoDB 为例)

初始数据

  • 表 account,记录:id=1, balance=100。

1. 读未提交(Read Uncommitted)

  • 场景
    • T1:UPDATE account SET balance = 200 WHERE id = 1;(未提交)。
    • T2:UPDATE account SET balance = 300 WHERE id = 1;(未提交)。
    • T3:SELECT balance FROM account WHERE id = 1;。
  • 结果:T3 可能读到 200 或 300(脏读),取决于谁先写。
  • 特点:读到未提交数据,结果不一样且不稳定。

2. 读已提交(Read Committed)

  • 场景
    • T1:UPDATE balance = 200;(提交)。
    • T2:UPDATE balance = 300;(未提交)。
    • T3:SELECT balance;。
  • 结果:T3 读到 200(最新提交值),若 T2 后提交,T3 再次读可能是 300。
  • 特点:避免脏读,但可能不可重复读,结果随提交时序变化。

3. 可重复读(Repeatable Read,InnoDB 默认)

  • 场景
    • T3 开始事务。
    • T1:UPDATE balance = 200;(提交)。
    • T2:UPDATE balance = 300;(提交)。
    • T3:SELECT balance;(两次)。
  • 结果:T3 两次读到 100(事务开始时的快照),与 T1、T2 修改无关。
  • 特点:MVCC 提供快照隔离,结果一致但可能不是最新。

4. 串行化(Serializable)

  • 场景
    • T1 和 T2 修改顺序执行(加锁阻塞)。
    • T3 读发生在 T1、T2 提交后。
  • 结果:读到 300(最后提交值),结果一致且最新。
  • 特点:完全隔离,性能最低。

面试角度

  • 锁机制
    • 悲观锁:T1 修改加锁,T2 阻塞,T3 读最新提交值。
    • 乐观锁:T1、T2 提交时检查冲突,T3 读取决于提交成功者。
  • MVCC
    • InnoDB 用版本号(事务 ID)隔离,T3 读快照。
  • 实际影响
    • 读未提交:脏读风险,数据不一致。
    • 可重复读:避免不可重复读,但可能幻读。
  • 性能对比
    • 低隔离级别快但一致性差,高隔离级别慢但安全。
  • 面试点
    • 问“读到什么”时,提隔离级别。
    • 问“如何保证一致”时,提串行化或锁。