MySQL事务隔离级别
事务四大特性(ACID)
| 特性 | 说明 | MySQL 实现方式 |
|---|---|---|
| 原子性 (Atomicity) | 事务要么全部完成,要么全部不完成 | Undo Log(回滚日志) |
| 一致性 (Consistency) | 事务前后数据完整性一致 | 业务逻辑 + 数据库约束 |
| 隔离性 (Isolation) | 事务之间互不干扰 | MVCC + 锁机制 |
| 持久性 (Durability) | 事务提交后永久生效 | Redo Log(重做日志) |

四种隔离级别
1. READ UNCOMMITTED(读未提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
特点:
- 可以读取其他事务未提交的数据
- 脏读、不可重复读、幻读 都可能发生
使用场景:
- 对数据一致性要求极低的场景
- 统计类查询(如 count(*),允许一定的数据偏差)
- 实际生产环境几乎不使用
示例:
-- 事务A
BEGIN;
UPDATE accounts SET balance = 500 WHERE id = 1; -- 未提交
-- 事务B(READ UNCOMMITTED)
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- 读到500(脏读)
2. READ COMMITTED(读已提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
特点:
- 只能读取其他事务已提交的数据
- 解决脏读,但仍存在不可重复读、幻读
- Oracle、PostgreSQL 的默认级别
实现原理:
- 每次 SELECT 都生成新的 ReadView
- 使用 MVCC 读取最新已提交版本
示例:
-- 事务A
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- 返回100
-- 事务B
BEGIN;
UPDATE accounts SET balance = 200 WHERE id = 1;
COMMIT; -- 提交
-- 事务A再次读取
SELECT balance FROM accounts WHERE id = 1; -- 返回200(不可重复读)
3. REPEATABLE READ(可重复读) ⭐ MySQL默认
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
特点:
- 同一事务内多次读取结果一致
- 解决脏读、不可重复读,部分解决幻读
- MySQL InnoDB 默认隔离级别
实现原理:
- 第一次 SELECT 时生成 ReadView,后续复用
- 使用 Next-Key Lock(临键锁)防止部分幻读
示例:
-- 事务A
BEGIN;
SELECT * FROM accounts WHERE balance > 100; -- 返回id=1,2
-- 事务B
BEGIN;
INSERT INTO accounts(id, balance) VALUES(3, 200); -- 会被阻塞(Next-Key Lock)
COMMIT;
-- 事务A再次读取
SELECT * FROM accounts WHERE balance > 100; -- 仍只返回id=1,2(快照读)
4. SERIALIZABLE(串行化)
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
特点:
- 完全串行执行,性能最差
- 解决所有并发问题(脏读、不可重复读、幻读)
- 通过锁机制而非 MVCC 实现
实现原理:
- 所有 SELECT 自动转为
SELECT ... LOCK IN SHARE MODE - 读写完全互斥
示例:
-- 事务A
BEGIN;
SELECT * FROM accounts WHERE id = 1; -- 自动加共享锁
-- 事务B
BEGIN;
UPDATE accounts SET balance = 300 WHERE id = 1; -- 被阻塞,等待事务A释放锁
MySQL 实现机制详解
1. MVCC(多版本并发控制)
-- 隐藏字段(每行数据)
DB_TRX_ID -- 最近修改的事务ID(6字节)
DB_ROLL_PTR -- 回滚指针,指向undo log(7字节)
DB_ROW_ID -- 隐藏自增ID(6字节)
-- Undo Log 版本链
当前行 → undo_v1 → undo_v2 → undo_v3
2. ReadView(读视图)
// ReadView 结构
class ReadView {
List
<Long> m_ids; // 活跃事务ID列表
Long min_trx_id; // 最小活跃事务ID
Long max_trx_id; // 预分配的下一个事务ID
Long creator_trx_id; // 创建该ReadView的事务ID
}
3. 锁机制
-- 记录锁(Record Lock)
-- 锁定单条记录
-- 间隙锁(Gap Lock)
-- 锁定记录之间的间隙,防止插入
SELECT * FROM users WHERE age > 20 FOR UPDATE;
-- 临键锁(Next-Key Lock = Record Lock + Gap Lock)
-- RR级别默认使用,防止幻读
-- 锁定记录本身和前面的间隙
不同隔离级别的实现差异
RC vs RR 的 ReadView 创建时机
-- READ COMMITTED:每次SELECT都新建ReadView
function read_committed_read() {
create_new_readview(); // 每次新建
find_visible_version();
}
-- REPEATABLE READ:第一次SELECT时创建,后续复用
function repeatable_read() {
if (first_read_in_transaction) {
create_readview(); // 只创建一次
}
find_visible_version(); // 复用同一个ReadView
}
幻读解决方案对比
-- 情况:查询age>20的记录
-- RC级别:可能幻读
BEGIN; -- RC级别
SELECT * FROM users WHERE age > 20; -- 返回3条
-- 另一个事务插入age=25的记录
SELECT * FROM users WHERE age > 20; -- 返回4条(幻读)
-- RR级别:通过Next-Key Lock防止
BEGIN; -- RR级别
SELECT * FROM users WHERE age > 20 FOR UPDATE; -- 加Next-Key Lock
-- 锁住20到正无穷的间隙,其他事务无法插入
配置与监控
查看和设置隔离级别
-- 查看当前隔离级别
SELECT @@transaction_isolation;
SELECT @@global.transaction_isolation;
SELECT @@session.transaction_isolation;
-- 设置全局隔离级别
SET GLOBAL transaction_isolation = 'REPEATABLE-READ';
-- 设置会话隔离级别
SET SESSION transaction_isolation = 'READ-COMMITTED';
监控事务相关指标
-- 查看当前运行的事务
SELECT * FROM information_schema.innodb_trx;
-- 查看锁信息
SELECT * FROM information_schema.innodb_locks;
SELECT * FROM information_schema.innodb_lock_waits;
-- 查看事务历史
SELECT * FROM performance_schema.events_transactions_current;
SELECT * FROM performance_schema.events_transactions_history;
my.cnf 配置建议
[mysqld]
# 默认隔离级别
transaction-isolation = REPEATABLE-READ
# 锁相关配置
innodb_lock_wait_timeout = 50 # 锁等待超时(秒)
innodb_rollback_on_timeout = ON # 超时自动回滚
# MVCC相关配置
innodb_undo_logs = 128 # undo log数量
innodb_purge_threads = 4 # purge线程数
innodb_max_purge_lag = 900000 # 最大purge延迟
实际应用建议
1. 选择合适隔离级别
-- 大多数场景:使用默认的 REPEATABLE READ
-- 高并发读场景:考虑 READ COMMITTED
-- 财务系统:可能需要 SERIALIZABLE
2. 事务设计
-- 保持事务短小
BEGIN;
-- 尽可能少的SQL操作
COMMIT;
-- 按相同顺序访问表,避免死锁
3. 性能优化技巧
-- RC级别可以减少锁冲突
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 合理使用索引,减少锁范围
CREATE INDEX idx_age ON users(age);
SELECT * FROM users WHERE age = 25 FOR UPDATE; -- 只锁age=25的行
-- 分批处理大数据量更新
UPDATE large_table SET status = 1 WHERE id BETWEEN 1 AND 1000;
UPDATE large_table SET status = 1 WHERE id BETWEEN 1001 AND 2000;
| 隔离级别 | MySQL 实现 | 适用场景 | 注意事项 |
|---|---|---|---|
| READ UNCOMMITTED | 直接读取最新数据 | 几乎不用 | 数据一致性无要求 |
| READ COMMITTED | 每次新建 ReadView | 高并发读,可接受不可重复读 | 适合读多写少 |
| REPEATABLE READ | 首次 SELECT 建 ReadView + Next-Key Lock | 默认选择,大部分场景 | 平衡性能与一致性 |
| SERIALIZABLE | 所有读加共享锁 | 强一致性要求,如金融系统 | 性能最差 |
要点:
- MySQL 默认使用 REPEATABLE READ,通过 MVCC + Next-Key Lock 实现
- 隔离级别越高,并发性能越低
- 理解实现原理有助于设计更好的应用和解决并发问题
- 实际选择需权衡数据一致性和系统性能需求
注意:
- mysql 事务隔离级别是指在数据库中,不同事务之间的隔离程度。
- 不同的隔离级别对应不同的并发性能和数据一致性。
- mysql 提供了四种事务隔离级别:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。
- 默认隔离级别是 REPEATABLE READ。
- 如果在默认隔离级别事务中查询数据并且读取,这里需要注意,并行事务间的数据操作是不可读不可见的,只能读取到事务开始时的数据。