锁
锁类型
-
共享锁和排他锁
行级锁
lock mode S:共享锁(读锁)
lock mode X:排他锁(写锁) -
意向锁
InnoDB支持多粒度锁定,允许行锁和表锁共存。为了使多粒度级别的锁定变得实用,InnoDB
请使用意图锁。意向锁是表级锁,它指示事务稍后对表中的行需要哪种类型的锁(共享或独占)。有两种类型的意图锁:
lock mode IS:意向共享锁
lock mode IX:意向排他锁
意图锁定协议如下:
在事务可以获取表中行的共享锁之前,它必须首先获取IS表上的锁或更强的锁。
在事务可以获取表中行的排他锁之前,它必须首先获取IX 表上的锁。
下面的矩阵总结了表级锁类型的兼容性。
X | IX | S | IS | |
---|---|---|---|---|
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容的 | 冲突 | 兼容的 |
S | 冲突 | 冲突 | 兼容的 | 兼容的 |
IS | 冲突 | 兼容的 | 兼容的 | 兼容的 |
排他锁种类
- 记录锁(RECORD LOCKS)
记录锁是对索引记录的锁。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
阻止任何其他事务插入、更新或删除值为 的t.c1
行10
。
记录锁总是锁定索引记录,即使定义的表没有索引。对于这种情况,InnoDB
创建一个隐藏的聚集索引并将该索引用于记录锁定。请参阅 第 15.6.2.1 节,“聚集索引和二级索引”。
记录锁的事务数据在InnoDB 监视器 输出 SHOW ENGINE INNODB STATUS中 显示类似于以下内容:
RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 8000000a; asc ;;
1: len 6; hex 00000000274f; asc 'O;;
2: len 7; hex b60000019d0110; asc ;
image.png
-
间隙锁(Gap Locks)
-
下一键锁(Next-Key Locks)
lock
相互关系
X锁:不能有S或X锁
S锁:不能有X锁
多线程批量新增死锁场景;
共有10个线程,每个线程一次新增200条数据。
-
场景1
表中有一个主键和一些普通二级索引(非唯一索引).
主键值为雪花算法生成,每次生成的值,一定是表里最大值。
结论:不会死锁 -
场景2
表中有一个主键和一些普通二级索引(非唯一索引).
主键值为其他方式生成,且每次生成的值,随机分布在表中id的任意区间。
结论:主键死锁
-
场景3
表中有一个主键和一个唯一索引
主键值为雪花算法生成,且每次生成的值,一定是表里最大值。
唯一索引字段值为其他方式生成,且每次生成的值,随机分布在表中此字段的任意区间
结论:唯一索引字段死锁 -
原因;结论待验证
某一Thread优先执行insert,由于存在唯一字段,需要校验唯一性,需要先加锁(防止值发生变更),加锁时,如果能找到该索引值,就加记录锁(只锁定此索引),若不能找到,则锁定最近的两个索引区间。
场景2/3,每次insert会有200条数据,最多可能会锁住200个区间,多个事务就可能存在死锁冲突。
场景1,每次insert第一次扫描的区间一定是:表中最大值的索引->无穷大。也就防止了其他事务加锁的可能性。
锁调试
- 查看锁等待情况
select * from innodb_lock_waits;
- 查看相关锁信息
SELECT * FROM innodb_locks;
insert加锁流程
执行 insert 语句,对要操作的页加 RW-X-LATCH,然后判断是否有和插入意向锁冲突的锁,如果有,加插入意向锁,进入锁等待;如果没有,直接写数据,不加任何锁,结束后释放 RW-X-LATCH;并加X锁;
死锁排查语句
select * from innodb_lock_waits;
SELECT * FROM innodb_locks;
SELECT * FROM information_schema.INNODB_TRX;
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
SHOW OPEN TABLES where In_use > 0;
show engine innodb status;