MYSQL事务原理分析(三)
目录
一. 事务
1. 事物的定义和必要性
2. 事务的基本语句
3. ACID 特性
4. Undo Log 与 Redo Log
Undo Log(回滚日志)
Redo Log(重做日志)
二. 事务隔离级别
1. 并发问题与隔离级别的关系
2. 四种隔离级别
3. 隔离级别操作命令
三. MVCC(多版本并发控制)(⭐⭐⭐⭐⭐)
1. MVCC 核心原理
2. Read View
可见性判断:
3. 不同隔离级别下 MVCC 行为
4. 快照读 VS 当前读
四.MySQL 锁机制
1. 锁的粒度
2. 常见锁类型
全局锁
表级锁
行级锁
3. 锁算法
记录锁(Record Lock)
间隙锁(Gap Lock)
临键锁(Next-Key Lock)
五.死锁
1.死锁的概念与原因
2. 如何避免死锁
一. 事务
1. 事物的定义和必要性
在数据库多用户高并发访问场景中,多个连接同时操作数据可能导致数据不一致。
事务(Transaction)是数据库管理系统提供的一种机制,用于确保一组数据库操作要么全部成功执行,要么全部失败回滚,从而保证数据的完整性和一致性。
2. 事务的基本语句
MySQL 提供以下事务控制语句:
--显式开启事务
START TRANSTACTION; --提交事务,持久化所有更改
COMMIT;--回滚事务,撤销未提交的修改
ROLLBACK;--创建保存点
SAVEPOINT identifier;--删除保存点
RELEASE SAVEPOINT identifier;--回滚到指定保存点
ROLLBACK TO SAVEPOINT identifier;
3. ACID 特性
特性 | 含义 | 实现机制 |
原子性(Atomicity) | 事务中所有操作要么全部完成,要么全部不完成,不可分割 | 通过 Undo log 实现回滚能力 |
一致性(Consistency) | 事务执行前后,数据库的完整性约束没有被破坏,数据保持一致 | 依赖约束,外键等规则强制实现 |
隔离性(Isolation) | 多个事务并发执行时,事物之间不互相影响 | 通过 MVCC 和锁机制实现 |
持久性(Durability) | 一旦事务提交,其对数据库的修改应该永久保存,即使系统崩溃 | 基于 Redo log 实现崩溃恢复 |
4. Undo Log 与 Redo Log
Undo Log(回滚日志)
- 作用:记录数据修改前的状态,用于事务回滚或 MVCC 的快照读
- 特点:InnoDB 引擎特有,保证事务原子性
Redo Log(重做日志)
- 作用:记录事务对数据页的修改,确保事务持久化
- 特点:顺序写入,高效持久化,崩溃回复时用于重建数据
二. 事务隔离级别
1. 并发问题与隔离级别的关系
在多事务并发执行时,若没有适当的隔离机制,可能会出现以下问题:
- 脏读:一个事务读取了另一个未提交事务修改的数据
- 不可重复读:同一事务多次读取同一数据返回不同结果
- 幻读:同一事务中,两次查询的结果集不一致
2. 四种隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(READ UNCOMMITED) | 可能 | 可能 | 可能 |
读已提交(READ COMMITED) | 不可能 | 可能 | 可能 |
可重复读(REPEATABLE READ)(MYSQL默认) | 不可能 | 不可能 | 可能 |
可串行化(SERIALIZABLE) | 不可能 | 不可能 | 不可能 |
3. 隔离级别操作命令
-- 查看当前会话隔离级别
SELECT @@tx_isolation;-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;-- 设置全局隔离级别(需重启或新连接生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
三. MVCC(多版本并发控制)(⭐⭐⭐⭐⭐)
1. MVCC 核心原理
MVCC 是 InnoDB 实现高并发的关键技术,通过保存数据的多个版本,使得读写操作可以并发执行而互不阻塞,核心机制包括:
- Undo Log :存储数据的历史版本
- Read View :事务执行期间的快照,用于判断可见性
Undo Log 会随着事务提交后被清除(并不是永久保存)
2. Read View
属性 | 作用 |
m_ids | 当前活跃的事务ID集合(还未提交的事务) |
min_trx_id | 当前活跃事务中最小的事务ID |
max_trx_id | 下一个即将分配的事务ID(并不一定是最大的事务ID) |
creat_trx_id | 每一行数据的创建事务ID(trx_id字段) |
可见性判断:
- trx_id < min_trx_id:说明该记录在创建read view 之前已经提交,所以对当前事务可见;
- trx_id >= max_trx_id:说明该记录是在创建read view 之后启动事务生成的,所以对当前事务不可见
- min_trx_id <= trx_id < max_trx_id:此时需要判断是否在 m_ids 集合中;
- 在列表中;生成该版本记录的事务仍处于活跃状态,该版本记录对当前事务不可见;
- 不在列表中;生成该版本记录的事务已经提交,该版本记录对当前事务可见;
3. 不同隔离级别下 MVCC 行为
隔离级别 | Read View 创建时机 | 说明 |
READ COMMITED(读已提交) | 每次查询时创建新的 Read View | 可以读取其他事务已提交的修改 |
REPEATABLE READ(可重复读) | 事务开始时创建唯一的 Read View | 事务内查询结果保持一致 |
4. 快照读 VS 当前读
类型 | 说明 | 案例 |
快照读(Snapshot Read) | 非锁定读,走MVCC快照 | SELECT * FROM table WHERE id = 1; |
当前读(Current Read) | 加锁读,拿最新版本 | SELECT * FROM table WHERE id = 1 FOR UPDATE; |
四.MySQL 锁机制
1. 锁的粒度
MySQL 支持多种锁粒度
- 表级锁:对整张表加锁,开销小但并发度低
- 页级锁:对数据页加锁(主要用于 MyISAM 索引缓存,InnoDB 不使用)
- 行级锁:对记录加锁,开销大但并发度高(InnoDB 支持)
2. 常见锁类型
全局锁
-- 全局只读锁(用于一致性备份)
FLUSH TABLES WITH READ LOCK;-- 全局写锁(用于表结构变更)
LOCK TABLES table_name WRITE;
- 全局锁会锁定整个数据库,其他事务无法进行读写操作。
表级锁
- 意向共享锁(IS):事务打算对表中某些记录加共享锁
- 意向排他锁(IX):事务打算对表中某些记录加排他锁
意向锁不会阻塞其他意向锁,但会与表级的共享或排他锁发生冲突
行级锁
- 共享锁(S Lock):允许其他事务读取同一行,但阻止写入
- 排他锁(X Lock):阻止其他事务获取任何类型的锁
InnoDB 使用MVCC + 行锁结合实现高并发控制
3. 锁算法
InnoDB 主要实现了三种行锁算法
记录锁(Record Lock)
锁定单个索引记录,防止其他事务修改或删除。
SELECT * FROM user WHERE id = 5 FOR UPDATE;//若 id 有索引,这条语句会对 id=5 的那一条记录加记录锁
间隙锁(Gap Lock)
锁定索引记录之间的间隙,防止插入新纪录,避免幻读
SELECT * FROM user WHERE id < 5 FOR UPDATE;//会锁住 (5, ∞) 范围的间隙,阻止插入 id > 5 的新记录。
临键锁(Next-Key Lock)
记录锁与间隙锁的组合,锁定记录及其前面的间隙,是InnoDB 默认的行锁算法
SELECT * FROM user WHERE id BETWEEN 5 AND 10 FOR UPDATE;
五.死锁
1.死锁的概念与原因
死锁是指两个或多个事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。常见场景包括:
- 相反加锁顺序:事务 A 锁定资源 X 并请求资源 Y,同时事务 B 锁定资源 Y 并请求资源 X
- 锁冲突:高并发环境下,多个事务频繁争夺相同资源
2. 如何避免死锁
方法 | 说明 |
统一加锁顺序 | 用一致的顺序加锁,避免环形等待 |
控制事务粒度与范围 | 缩小每个事务锁定的数据范围和执行时间 |
减少范围查询 | 减少 BRTWEEN/</> 查询,避免间隙锁干扰 |
合理设置索引 | 避免全表扫描导致不必要的行锁或表锁 |
设置死锁重试机制 | 捕获错误码 1213,程序自动重试一次事务操作 |
适当降低隔离级别 | 如使用 Read Committed 替代 Repeatable Read(锁冲突) |