相关系列链接
焱老师带你学习MYSQL系列 第六篇 (MYSQL是如何实现锁的)
焱老师带你学习MYSQL系列 第五篇 (MYSQL事务隔离级别是如何实现的)
焱老师带你学习MYSQL系列 第四篇 (MYSQL优化器详解)
焱老师带你学习MYSQL系列 第三篇 (MYSQL单表访问方法)
焱老师带你学习MYSQL系列 第二篇 (MYSQL 数据结构)
焱老师带你学习MYSQL系列 第一篇 (MYSQL 整体架构)
1 . 锁的数据结构
1 . 1 trx信息
表示这个锁结构是与哪个事务关联的
1 . 2 索引信息
对于行级锁 , 需要记录一下加锁的记录属于哪个索引
1 . 3 表锁 / 行锁信息
1 . 3 . 1 行锁
Space ID
Page Number
n_bits
一条记录对应一个比特 , 表示为当前页面哪一条记录加了锁
1 . 4 type_mode (32比特)
1 . 4 . 1 lock_mode (4比特)
码值如下 :
LOCK_IS : IS锁 (意向共享锁)
LOCK_IX : IX锁 (意向独占锁)
LOCK_S : S锁 (共享锁)
LOCK_X : X锁 (独占锁 意思就是说 加上这个锁 其他的哪怕S锁也不行)
LOCK_AUTO_INC : AUTO-INC锁
IX/IS 意向锁 主要是针对所加的锁是行锁 但也会对表加意向锁 . 这样如果有其他请求需要加表锁 看到有意向锁就不会在加了 需等待行锁以及意向锁释放 .
1 . 4 . 2 lock_type 4比特
LOCK_TABLE 表级锁
LOCK_REC 行级锁
LOCK_ORDINARY next-key锁
LOCK_GAP gap锁
LOCK_REC_NOT_GAP 正经记录锁
LOCK_INSERT_INTENTION 插入意向锁
1 . 4 . 3 lock_wait 1比特
true 当前事务尚未获取到锁
false 当前事务获取锁成功
1 . 5 其他信息 (hash表和链表)
2 . 锁的分类
2 . 1 record锁
(记录锁也称行级锁) 分S/X锁
2 . 2 Gap Lock (间隙锁)
目的 : 为了解决幻读问题
给当前记录加gap锁会不允许给当前记录的前面插入记录
gap锁也分独享 / 独占 , 并不会防止其他事务对这条记录加record锁/gap锁 . 仅仅是防止插入幻影记录
如果到正无穷 , 给当前表最后一条记录所在的页面 supremum(最大页号) 加上gap锁
Next-Key Lock = 当前记录的x锁 + 当前记录的gap锁
2 . 3 Insert Intention Lock (插入意向锁)
当空隙被gap锁锁住不能插入记录时 , 就会给这个锁处于等待状态
2 . 4 隐式锁 (为INSERT使用)
对于聚簇索引
q : 当插入时 比如非自增id作为主键或者id的轻量级锁如何保证不会出现幻读 (比如当插入到一半时 , 迅速的申请此记录的x/s锁可能会出现问题)
a : 当插入的事务 (事务从列记录的trx_id) 活跃时 , 是不能申请此记录的S / X锁的
3 . 加锁过程
3 . 1 delete语句
1 . 先定位到这条记录的位置
2 . 获取这条记录的X锁
3 . 最后执行delete mark操作
3 . 2 insert语句
新插入语句 是不需要加锁的 不过受隐式锁保护 .
3 . 3 update语句
1 . 先定位到这条记录的位置
2 . 获取这条记录的X锁
3 . 4 select语句
1 . 快速在 B+ 树上定位到该扫描区间的第一条记录
2 . 为当前记录加锁
1 . 在不小于可重复读/串行化 会加next - key锁
2 . 在不满足上面提交会加正经记录锁
3 . 判断索引条件下推的条件是否成立 (减少回表)
4 . 执行回表操作 , 并且为聚簇索引记录加对应的锁类型 (一般都是加的正经记录锁)
5 . 判断边界条件是否成立
如果符合 跳到 6
不符合则判断不大于已提交时 , 释放掉该记录上的锁 (如果大于 则先不释放锁) , 并向server层返回查询完毕信息
6 . server层判断其他搜索条件是否成立
如果不满足其他搜索条件 . 则判断不大于已提交时 , 释放掉该记录上的锁 (如果大于 则先不释放锁)
7 . 获取当前链表的下一条记录 并作为新的当前记录 重新走步骤 2
4 死锁
当检测到死锁发生时 , 会选择一个较小的事务进行回滚 .