一、事务ACID特性
1. 核心定义
事务是数据库操作的逻辑单元,要么全部执行成功,要么全部执行失败。ACID是事务的四大特性:
| 特性 | 定义 | 实现机制 |
|---|---|---|
| 原子性(Atomicity) | 事务中的所有操作要么全部成功,要么全部回滚,不可分割 | - 日志 :通过Undo Log 记录操作前状态,失败时回滚 - 锁机制:确保事务操作的原子性 |
| 一致性(Consistency) | 事务执行前后,数据库从一个一致状态转换到另一个一致状态 | - 约束检查 :主键、外键、唯一性约束等 - 业务逻辑 :应用层保证 - 原子性+隔离性+持久性:共同保证一致性 |
| 隔离性(Isolation) | 多个事务并发执行时,一个事务的操作不会影响其他事务 | - 锁机制 :行锁、表锁、间隙锁等 - MVCC(多版本并发控制):通过版本号控制并发读 |
| 持久性(Durability) | 事务提交后,数据永久保存到数据库,即使系统崩溃也不会丢失 | - Redo Log :预写日志,事务提交前先写入Redo Log,系统崩溃后通过Redo Log恢复 - 双写缓冲:InnoDB的Double Write Buffer,防止页断裂 |
2. 面试题:事务的ACID特性?如何实现?
回答模板:
-
ACID定义:
- 原子性:事务操作不可分割,要么全成要么全败
- 一致性:事务前后数据库状态一致
- 隔离性:并发事务间互不干扰
- 持久性:事务提交后数据永久保存
-
实现机制:
- 原子性 :依赖Undo Log,记录操作前状态,失败时回滚
- 一致性:由原子性、隔离性、持久性共同保证,辅以约束检查和业务逻辑
- 隔离性 :通过锁机制 (行锁、表锁)和MVCC实现
- 持久性 :依赖Redo Log,事务提交前写入Redo Log,系统崩溃后恢复
二、事务隔离级别
1. 四大隔离级别
SQL标准定义了四个隔离级别,从低到高依次为:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现原理 | MySQL默认 |
|---|---|---|---|---|---|
| 读未提交(Read Uncommitted) | ✅ 允许 | ✅ 允许 | ✅ 允许 | 无锁,直接读取最新数据 | 否 |
| 读已提交(Read Committed) | ❌ 禁止 | ✅ 允许 | ✅ 允许 | MVCC(Read View),每次查询生成新的Read View | 否 |
| 可重复读(Repeatable Read) | ❌ 禁止 | ❌ 禁止 | ❌ 禁止(InnoDB) | MVCC(Read View)+ 间隙锁,同一事务共享Read View | 是 |
| 串行化(Serializable) | ❌ 禁止 | ❌ 禁止 | ❌ 禁止 | 表级锁,事务串行执行 | 否 |
2. 核心隔离级别详解
读未提交(Read Uncommitted)
- 特点 :事务可以读取其他事务未提交的数据
- 问题:脏读(读取到其他事务回滚的数据)
- 适用场景:对数据一致性要求极低,几乎不使用
读已提交(Read Committed)
- 特点 :事务只能读取其他事务已提交的数据
- 问题:不可重复读(同一事务内,多次查询同一记录返回不同结果)
- 实现 :每次查询生成新的Read View(MVCC),只读取已提交的版本
- 适用场景:对数据一致性要求一般,如普通业务系统
可重复读(Repeatable Read)
- 特点 :同一事务内,多次查询同一记录返回相同结果
- 问题:幻读(同一事务内,多次查询同一范围返回的记录数不同)
- MySQL InnoDB实现 :
- MVCC :同一事务共享Read View,确保可重复读
- 间隙锁(Gap Lock):防止幻读,锁定记录间的间隙,禁止插入新记录
- 适用场景:对数据一致性要求较高,如金融系统
串行化(Serializable)
- 特点 :事务串行执行,完全隔离
- 实现:表级锁,事务执行时锁定整个表
- 问题:并发性能极差
- 适用场景:对数据一致性要求极高,几乎不使用
3. 面试题:MySQL默认的隔离级别是什么?
回答 :MySQL默认隔离级别是可重复读(Repeatable Read) ,通过MVCC+间隙锁实现,既保证了数据一致性,又有较好的并发性能。
三、并发事务问题
1. 核心问题定义
| 问题 | 产生条件 | 产生原因 | 解决方案 |
|---|---|---|---|
| 脏读(Dirty Read) | 读未提交隔离级别 | 读取到其他事务未提交的数据,该数据可能被回滚 | 升级隔离级别到读已提交或更高 |
| 不可重复读(Non-Repeatable Read) | 读已提交隔离级别 | 同一事务内,多次查询同一记录,因其他事务更新/删除该记录导致结果不同 | 升级隔离级别到可重复读或更高 |
| 幻读(Phantom Read) | 可重复读隔离级别 | 同一事务内,多次查询同一范围,因其他事务插入新记录导致记录数不同 | MySQL InnoDB通过间隙锁 解决,或升级到串行化 |
2. 幻读问题详解
定义 :同一事务内,多次执行相同范围查询,返回的记录数不同(因其他事务插入新记录)。
产生原因:
- 事务A执行范围查询(如
select * from user where id > 10),返回2条记录 - 事务B插入一条
id=11的记录并提交 - 事务A再次执行相同查询,返回3条记录,产生幻读
解决方案:
- MySQL InnoDB :使用间隙锁(Gap Lock) ,锁定记录间的间隙,防止其他事务插入新记录
- 示例:查询
id > 10时,锁定(10, +∞)的间隙,禁止插入id在该范围内的新记录
- 示例:查询
- 串行化隔离级别:事务串行执行,彻底避免幻读
3. 面试题:如何解决幻读问题?
回答要点:
- 间隙锁(Gap Lock) :MySQL InnoDB在可重复读隔离级别下,通过间隙锁锁定记录间的间隙,防止其他事务插入新记录
- Next-Key Lock:间隙锁+行锁的组合,锁定记录和相邻间隙,彻底解决幻读
- 串行化隔离级别:事务串行执行,完全隔离,避免所有并发问题
- 应用层控制:如使用唯一索引、乐观锁等,但效率较低
四、MVCC(多版本并发控制)
1. 核心原理
MVCC通过版本号 控制并发读,允许多个事务同时读取同一记录的不同版本,避免读-写冲突,提高并发性能。
2. 关键机制
- 版本号 :每条记录包含
trx_id(事务ID)和roll_ptr(回滚指针,指向Undo Log) - Read View :事务读取数据时生成的快照 ,包含:
m_ids:当前活跃事务ID列表min_trx_id:最小活跃事务IDmax_trx_id:最大事务IDcreator_trx_id:创建该Read View的事务ID
3. 可见性规则
- 记录的
trx_id < min_trx_id:记录已提交,可见 - 记录的
trx_id > max_trx_id:记录由未来事务创建,不可见 min_trx_id ≤ trx_id ≤ max_trx_id:trx_id不在m_ids中:记录已提交,可见trx_id在m_ids中:记录由活跃事务创建,不可见,需通过roll_ptr回滚到可见版本
4. 隔离级别与MVCC
- 读已提交 :每次查询生成新的Read View,只读取已提交的版本
- 可重复读 :同一事务内共享一个Read View,确保可重复读
五、锁机制补充
1. 锁类型
- 按粒度分 :
- 行锁:锁定单条记录,并发性能高
- 表锁:锁定整张表,并发性能低
- 间隙锁:锁定记录间的间隙,防止幻读
- 按模式分 :
- 共享锁(S锁):读锁,多个事务可同时持有
- 排他锁(X锁):写锁,仅一个事务可持有
- 意向共享锁(IS锁):表级锁,指示事务准备获取行共享锁
- 意向排他锁(IX锁):表级锁,指示事务准备获取行排他锁
2. 锁的兼容性
| 请求锁类型 | 已有S锁 | 已有X锁 |
|---|---|---|
| S锁 | ✅ 兼容 | ❌ 冲突 |
| X锁 | ❌ 冲突 | ❌ 冲突 |
总结
事务的ACID特性是数据库一致性的核心保障,隔离级别通过锁机制和MVCC实现不同程度的并发控制。MySQL默认的可重复读隔离级别平衡了一致性和性能,通过MVCC解决不可重复读,通过间隙锁解决幻读。
理解ACID特性、隔离级别、并发问题及解决方案,是数据库优化和面试的重点。在实际应用中,需根据业务需求选择合适的隔离级别,既保证数据一致性,又兼顾并发性能。