这是我参与更文挑战的第5天,活动详情查看:更文挑战
一、前言
解决脏读、不可重复读、幻读问题:基于MVCC机制(即, undo log 版本链条 和 ReadView)
那多个事务更新同一行数据时,是如何避免脏写的?
依靠:锁机制。 依靠此机制,可保证多个事务更新同一行数据的时候串行化执行。
要更新一行数据:必须把它所在的数据页从磁盘文件里读取到 缓存页 里才能更新。
即:数据和关联的锁数据结构都是在内存,操作也在内存。
MySQL 的锁机制跟 Java里的锁机制,思路完全类似的。
模拟事务更新数据流程
模拟步骤:
- 事务A 更新指定数据
- 事务B 更新相同数据
- 事务A 更新完成并提交
- 初识状态:事务A 更新一行数据
事务A 对那行数据加锁,如图:
因为这行数据没有加锁,所以事务A 直接对这个操作进行加锁。
- 事务B 更新这行数据
这时候事务B 也想操作这行数据,发现已经有事务操作并加锁了,事务B 生成一个锁数据结果,并将自己的等待状态设置为 true,如图:
- 事务A 更新完并提交
事务A 更新完并提交后,会把自己的锁给释放掉。 锁一旦释放,就会唤醒其他等待的事务。
如图:
二、锁划分
MySQL 锁有:
- 共享锁
- 独占锁
- 互斥锁
- 表级锁
(1)共享锁 与 独占锁
MySQL 主要:共享锁(Share) 和 独占锁(Exclude)
事务运行的时候,加的是独占锁(Exclue,也叫 X锁)。
-
其他事务要更新这行数据,都要加独占锁并在锁后等待。
-
其他事务可以读取到这行数据,不需要加锁。
依靠的是
MVCC机制,解决频繁加锁互斥。
共享锁(S 锁):执行查询时候加锁
即,在查询语句后面加上 lock in share mode
-- 例如:
SELECT * FROM table LOCK IN SHARE MODE;
加了共享锁,不能再加独占锁(即,他人不能再来更新)。
举个例子:中国土地归人民所有,但不能一人独占一块地。
共享锁和独占锁规律如下:
| 共享锁 | 独占锁 | |
|---|---|---|
| 共享锁 | 不互斥 | 互斥 |
| 独占锁 | 互斥 | 互斥 |
互斥锁:查询数据之后还要执行更新操作,其他事务更新不了这个数据。
-- 例如:
SELECT * FROM table FOR UPDATE;
(2)表级锁
执行 DDL 语句的时候,会默认在表级别加表锁,例如 ALTER TABLE 操作。
表锁,语法如下:
-- 加表级共享锁
LOCK TABLES xxx READ
-- 加表级独占锁
LOCK TABLES xxx WRITE
另外两个情况会加表级锁:
- 如果有事务在表里执行增删改操作,那在行级会加独占锁,同时会在表级加一个意向独占锁
- 如果有事务在表里执行查询操作,那么会在表级加一个意向共享锁
关系如下:
| 独占锁 | 意向独占锁 | 共享锁 | 意向共享锁 | |
|---|---|---|---|---|
| 独占锁 | 互斥 | 互斥 | 互斥 | 互斥 |
| 意向独占锁 | 互斥 | 不互斥 | 互斥 | 不互斥 |
| 共享锁 | 互斥 | 互斥 | 不互斥 | 不互斥 |
| 意向共享锁 | 互斥 | 不互斥 | 不互斥 | 不互斥 |