数据库事务详解
目录
- 一.事务的ACID原则
- 二.事务的隔离级别
- 三.数据库并发事务,会带来哪些问题
- 四.事务的隔离级别解决的问题
一.事务的ACID原则
1.原子性:
事务中的所有操作要么全部成功提交,要么在发生错误时全部回滚。
2.一致性:
事务对于修改操作,要求数据修改前和修改后的状态保持一致
3.隔离性:
一个事务的执行不会被其它事务干扰
隔离性是通过MVCC或锁机制来保证。
4.持久性:
一个事务一旦被提交,其修改会永久保存到数据库中
二.事务的隔离级别
1.读未提交
2.读已提交
3.可重复读:一个事务在执行的过程中,多次读取同一数据结果一致,可避免脏读和不可重复读,但幻读仍有可能发生。
4.串行化:最高的隔离级别,可以防止脏读、不可重复读以及幻读。
三.数据库并发事务,会带来哪些问题
- 脏读(Dirty Read):读取 “未提交” 的数据
定义:一个事务(T1)读取了另一个事务(T2)尚未提交的修改数据,而后续 T2 因异常回滚(Rollback),导致 T1 读取到的数据是 “无效的脏数据”。
核心问题:读的是 “临时数据”,数据最终可能被撤销,导致 T1 基于脏数据做的决策错误。
- 仅在最低的 读未提交(Read Uncommitted) 隔离级别下会发生;
- 只要隔离级别提升到 读已提交(Read Committed) 及以上,脏读会被完全避免(因为只允许读取 “已提交” 的数据)。
- 不可重复读(Non-repeatable Read):同一事务内 “重复读不一致”
定义:一个事务(T1)在同一执行过程中,多次读取同一数据,但期间另一个事务(T2)修改并提交了该数据,导致 T1 前后两次读取的结果不一致。
核心问题:“同一事务内的读一致性” 被破坏 —— 事务期望 “一旦读取某数据,后续再读该数据时结果不变”,但实际被其他已提交事务修改。
- 在 读已提交(Read Committed) 级别下会发生(允许读取已提交数据,但不阻止其他事务修改);
- 提升到 可重复读(Repeatable Read) 级别后,会通过 “行锁” 或 “多版本并发控制(MVCC)” 避免 ——T1 读取后,其他事务无法修改该数据,直到 T1 结束。
- 幻读(Phantom Read):同一查询 “行数变化”
定义:一个事务(T1)在同一执行过程中,多次执行相同的范围查询(如 “查询库存 < 10 的商品”),期间另一个事务(T2)插入 / 删除了符合该范围的新数据并提交,导致 T1 前后两次查询的 “结果行数不一致”(像出现了 “幻影” 一样)。
核心问题:“范围查询的一致性” 被破坏 —— 事务期望 “范围查询的结果集固定”,但实际因其他事务的增删,行数发生变化。
- 在 可重复读(Repeatable Read) 级别下可能发生(多数数据库如 MySQL 的 InnoDB 通过 “间隙锁” 优化,可减少幻读,但未完全杜绝极端场景);
- 只有在最高的 串行化(Serializable) 级别下,才会通过 “表锁” 完全避免 —— 事务串行执行,不允许并发修改,范围查询期间其他事务无法插入 / 删除符合条件的数据。
异常类型 | 核心表现 | 触发场景 | 最低避免隔离级别 |
---|---|---|---|
脏读 | 读 “未提交” 数据 | 其他事务修改未提交 | 读已提交(Read Committed) |
不可重复读 | 同一行数据多次读结果不一致 | 其他事务修改并提交同一行 | 可重复读(Repeatable Read) |
幻读 | 同一范围查询多次读行数不一致 | 其他事务插入 / 删除并提交新数据 | 串行化(Serializable) |
四.事务的隔离级别解决的问题
- READ_UNCOMMITTED
事务读取:不加锁
事务写入:加写锁
解决问题:脏写
存在问题:脏读,不可重复读、幻读。 - READ_COMMITTED
事务读取:加读锁(每次 select 完成都会释放读锁)
事务写入:加写锁
解决问题:脏写、脏读
存在问题:不可重复读、幻读。 - REPEATABLE_READ
事务读取:加读锁(每次 select 完不会释放锁,而是事务结束后才释放)(如果是 Mysql 的 innodb 还会加间隙锁)。
事务写入:加写锁
解决问题:脏写、脏读、不可重复读,幻读(如果是 Mysql 的 innodb 则已解决)
存在问题:幻读(如果是 Mysql 的 innodb 则不存在)。 - SERIALIZABLE
不管读取还是修改所有的事务串行化执行,一个事务的执行必须等其他事务结束。