从 ACID 到 MVCC,MySQL 事务与隔离级别超详解
目录
- 事务的基本概念
- 什么是事务?
- 事务的典型问题(火车票售票案例)
- 事务的ACID属性
- 支持事务的存储引擎
- 事务提交方式
- 自动提交
- 手动提交
- 事务常见操作
- 基本操作流程
- 重要结论
- 事务隔离级别
- 读未提交 (Read Uncommitted)
- 读提交 (Read Committed)
- 可重复读 (Repeatable Read)
- 串行化 (Serializable)
- 隔离级别的查看与设置
- 查看隔离级别
- 设置隔离级别
- 关键要点总结
- 事务核心特性
- 实际应用建议
- 注意事项
- 如何理解隔离性
- MVCC(多版本并发控制)概述
- MVCC实现原理
- 快照读与当前读
- RR 与 RC的本质区别
- Read View结构
- 可见性判断流程
事务的基本概念
什么是事务?
- 事务是一组DML语句组成的逻辑单元
- 这些语句要么全部成功,要么全部失败,是一个整体
- 保证数据操作的完整性和一致性
事务的典型问题(火车票售票案例)
客户端A检查票数>0 → 准备卖票
客户端B检查票数>0 → 准备卖票
客户端A更新票数-1
客户端B更新票数-1
结果:同一张票被卖了两次
事务的ACID属性
-
原子性 (Atomicity)
- 事务中的所有操作要么全部完成,要么全部不完成
- 不会结束在中间环节
- 发生错误时回滚到事务开始前的状态
-
一致性 (Consistency)
- 事务开始前和结束后,数据库完整性不被破坏
- 写入数据必须完全符合预设规则
-
隔离性 (Isolation)
- 多个并发事务同时执行时互不干扰
- 防止交叉执行导致的数据不一致
-
持久性 (Durability)
- 事务提交后,对数据的修改是永久的
- 即使系统故障也不会丢失
支持事务的存储引擎
- InnoDB: 支持事务(默认引擎)
- MyISAM: 不支持事务
-- 查看数据库引擎
SHOW ENGINES;
事务提交方式
自动提交
SHOW VARIABLES LIKE 'autocommit'; -- 查看自动提交状态
SET AUTOCOMMIT=0; -- 关闭自动提交
SET AUTOCOMMIT=1; -- 开启自动提交
手动提交
- 使用
BEGIN或START TRANSACTION开始事务 - 使用
COMMIT提交事务 - 使用
ROLLBACK回滚事务
事务常见操作
基本操作流程
BEGIN; -- 开始事务
SAVEPOINT save1; -- 设置保存点
INSERT INTO account VALUES (1, '张三', 100);
SAVEPOINT save2; -- 设置保存点
INSERT INTO account VALUES (2, '李四', 10000);
ROLLBACK TO save2; -- 回滚到保存点
ROLLBACK; -- 回滚整个事务
COMMIT; -- 提交事务
重要结论
- 输入
BEGIN后必须通过COMMIT提交才会持久化 - 事务可以手动回滚,操作异常时MySQL自动回滚
- InnoDB中每条SQL默认封装成事务自动提交
- MyISAM不支持事务
事务隔离级别
读未提交 (Read Uncommitted)
- 所有事务可以看到其他未提交事务的执行结果
- 问题:脏读、幻读、不可重复读
- 实际生产中基本不使用
读提交 (Read Committed)
- 只能看到其他已提交事务的改变
- 问题:不可重复读
- 大多数数据库的默认隔离级别(非MySQL)
可重复读 (Repeatable Read)
- 确保同一事务中多次读取看到相同数据
- 解决了不可重复读问题
- MySQL通过Next-Key锁解决了幻读问题
串行化 (Serializable)
- 最高隔离级别,强制事务排序
- 解决所有并发问题
- 但可能导致超时和锁竞争,实际很少使用
隔离级别的查看与设置
查看隔离级别
SELECT @@global.tx_isolation; -- 查看全局隔离级别
SELECT @@session.tx_isolation; -- 查看当前会话隔离级别
SELECT @@tx_isolation; -- 查看当前隔离级别
设置隔离级别
-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
关键要点总结
事务核心特性
- 原子性:保证操作的整体性
- 一致性:保证数据的正确性
- 隔离性:解决并发访问问题
- 持久性:保证数据的永久存储
实际应用建议
- 根据业务需求选择合适的隔离级别
- MySQL默认的RR级别在大多数场景下表现良好
- 高并发场景注意锁竞争问题
- 重要操作显式使用事务保证数据一致性
注意事项
- 只有InnoDB支持事务
- 设置保存点可以实现部分回滚
- 异常终止时未提交的事务会自动回滚
- 提交后的数据修改具有持久性
如何理解隔离性
MVCC(多版本并发控制)概述
- MVCC是一种用来解决读-写冲突的无锁并发控制机制。
它通过为事务分配单向增长的事务ID,并为每个修改保存一个版本,实现读操作不阻塞写操作,写操作也不阻塞读操作,从而提高数据库并发读写的性能。同时,MVCC还能解决脏读、幻读、不可重复读等事务隔离问题,但不能解决更新丢失问题。
MySQL 的隔离级别核心就是通过 多版本并发控制(MVCC) 决定事务能看到数据的 “哪个版本”,避免并发操作引发的脏读、不可重复读等问题。
MVCC实现原理
- 三个记录隐藏字段
DB_TRX_ID(6 byte):最近修改(修改/插入)事务ID,记录创建这条记录/最后一次修改该记录的事务ID。DB_ROLL_PTR(7 byte):回滚指针,指向这条记录的上一个版本(在undo log中)。DB_ROW_ID(6 byte):隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
- undo日志
- MySQL在内存中运行,所有机制(索引、事务、隔离性、日志等)在内存缓冲区中完成,并在合适时刷新到磁盘。
- undo log是MySQL中的一段内存缓冲区,用于保存日志数据,实现写时拷贝。
- Read View
- Read View是事务进行快照读操作时生成的读视图,记录并维护系统当前活跃事务的ID。
- 用于可见性判断,决定当前事务能看到哪个版本的数据。
快照读与当前读
- 快照读:读取历史版本(一般而言),不受加锁限制,提高效率。
- 当前读:读取最新的记录,增删改操作都是当前读,select也有可能当前读(如
select lock in share mode、select for update)。
RR 与 RC的本质区别
RR(REPEATABLE READ)级别:
- 某个事务对某条记录的第一次快照读会创建一个快照及Read View,记录此时所有其他活动事务的快照。
- 此后该事务的快照读都使用同一个Read View,对其他事务的修改不可见。
RC(READ COMMITTED)级别:
- 事务中每次快照读都会新生成一个快照和Read View,因此可以看到别的事务提交的更新。
- 每次快照读都会形成新的Read View,导致不可重复读问题。
Read View结构
class ReadView {
private:trx_id_t m_low_limit_id; // 高水位,大于等于这个ID的事务均不可见trx_id_t m_up_limit_id; // 低水位,小于这个ID的事务均可见trx_id_t m_creator_trx_id; // 创建该Read View的事务IDids_t m_ids; // 创建视图时的活跃事务id列表trx_id_t m_low_limit_no; // 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOGbool m_closed; // 标记视图是否被关闭
};
可见性判断流程
- 拿当前记录的DB_TRX_ID与Read View中的up_limit_id、low_limit_id和活跃事务ID列表(m_ids)进行比较。
- 如果DB_TRX_ID小于up_limit_id,说明事务已提交,可见。
- 如果DB_TRX_ID大于等于low_limit_id,说明事务在当前Read View生成后开启,不可见。
- 如果m_ids包含DB_TRX_ID,说明事务未提交,不可见。
- 否则,遍历下一个版本,直到找到可见版本。
本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~
