事务隔离(MySQL)
事务隔离 —— 为什么你改了我还看不见?
在数据库中,事务(Transaction) 用于保证一组数据库操作要么全部成功,要么全部失败,这是一种原子性的操作机制。在 MySQL 中,事务的支持由存储引擎层实现。但并非所有引擎都支持事务,比如 MySQL 原生的 MyISAM 引擎就不支持事务,而 InnoDB 引擎 则是主流的支持事务的引擎。
一、事务的四大特性(ACID)
特性 | 含义 |
---|---|
原子性(Atomicity) | 事务中包含的所有操作要么全部成功,要么全部失败 |
一致性(Consistency) | 事务执行前后,数据库都处于一致状态 |
隔离性(Isolation) | 并发执行的事务之间彼此隔离,互不影响 |
持久性(Durability) | 事务一旦提交,对数据库的修改就是永久性的,即使系统故障也不会丢失 |
二、隔离性与隔离级别
当多个事务并发执行时,若没有隔离,就可能导致以下问题:
- 脏读(Dirty Read):读取到未提交事务的数据
- 不可重复读(Non-repeatable Read):同一事务中多次读取同一数据,值却不同
- 幻读(Phantom Read):同一事务中两次查询返回的记录数不同
为解决这些问题,SQL 标准定义了四种隔离级别:
1. 四大隔离级别说明
隔离级别 | 描述 | 会发生的问题 |
---|---|---|
Read Uncommitted(读未提交) | 可以读取未提交事务修改的数据 | 脏读、不可重复读、幻读 |
Read Committed(读已提交) | 只能读取已提交事务修改的数据 | 不可重复读、幻读 |
Repeatable Read(可重复读) | 同一事务内多次读取结果一致(MySQL 默认) | 幻读(依赖间隙锁解决) |
Serializable(可串行化) | 强制事务串行执行,防止并发 | 无脏读/不可重复读/幻读,但效率低 |
注意:隔离级别越高,性能越差。需要根据业务需求做权衡。
2. 示例说明(图示说明略,可自己绘图添加)
假设有两个事务 A 和 B,对某行数据进行读写操作:
- 在 读未提交 下,事务 A 会看到事务 B 的未提交修改
- 在 读已提交 下,A 只能看到 B 已提交的修改
- 在 可重复读 下,A 在整个事务中看到的始终是开始时的数据快照
- 在 串行化 下,B 的写操作会被阻塞,直到 A 完成
3. MySQL 中隔离级别的实现方式
- Read Uncommitted:直接读取最新数据,无视事务
- Read Committed:每次 SQL 执行前创建一个视图(Read View)
- Repeatable Read:在事务开始时创建视图,期间一直使用同一个视图
- Serializable:加锁控制并发,防止冲突
在 MySQL 中,默认隔离级别为 Repeatable Read,但 Oracle 默认是 Read Committed。
查询当前隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';
三、事务隔离的实现机制(MVCC)
以“可重复读”为例,MySQL 使用 多版本并发控制(MVCC) 来实现事务隔离。
1. 回滚日志机制
- 每条记录在更新时都会生成一条回滚日志(undo log)
- 当前值可以通过逐步回滚恢复到历史版本
- 不同事务通过自己的视图(Read View)看到的版本可能不同
例如,某值从 1 → 2 → 3 → 4,回滚日志如下:
当前值 | 回滚日志 |
---|---|
4 | 3、2、1 |
视图 A 启动于值为 1 时,则通过回滚可看到 1;视图 B 则可能看到 2。
2. MVCC 的优点
- 允许读取操作不加锁,提升并发性能
- 实现隔离级别无需加重锁负担
3. 回滚日志的回收问题
- 回滚日志会在无事务再引用时被清除
- 长事务 会阻止日志清理,导致存储膨胀
实例:某系统数据仅 20GB,但 undo 日志达 200GB,最终只能重建数据库解决。
四、事务的启动方式及建议
1. 显式启动事务
BEGIN;
-- 或
START TRANSACTION;
-- 提交或回滚
COMMIT;
ROLLBACK;
2. 隐式启动方式:autocommit=0
SET autocommit = 0;
- 此模式下,即使执行一个简单的
SELECT
,也会自动开启事务 - 若未手动
COMMIT
或ROLLBACK
,事务将一直占用资源,形成 隐性长事务
建议
- 使用
SET autocommit=1
(默认),通过显式语句控制事务 - 使用
COMMIT WORK AND CHAIN
可在提交后立即开启下一个事务
-- 查看运行超过60秒的事务
SELECT *
FROM information_schema.innodb_trx
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;
五、总结
- 事务隔离是数据库并发控制的核心机制
- 在 MySQL 中,推荐使用默认的 Repeatable Read + MVCC 实现事务隔离
- 尽量避免隐式长事务,主动控制事务生命周期
- 根据实际业务权衡性能与隔离性的需求