MySQL ( InooDB ) 的锁规则和锁行为

不同隔离级别 / 不同并发模型核心是性能和正确性之间的 Trade Off

锁规则

  1. 索引加锁 -- 锁是设置在索引上,包括 Secondary Index 和 Clustered Index ( 记录本身 );

  2. 按需加锁 -- 访问到的符合条件的索引数据才 ( 都 ) 会上锁;

  3. 锁请求成功当且仅当:或无锁、或当前事务已持有符合的共享/排他锁、或已有锁和申请锁兼容;

  4. 死锁本质是不同事务彼此等待对方的锁,处理:不同的事务按照相同的顺序申请访问/操作数据;

  5. 唯一索引使用间隙锁来保证唯一性,此处间隙锁的使用和事务隔离级别无关;

  6. 若查询条件使用唯一索引且精确匹配,Next-Key Lock 退化为 Record Lock;

锁模式

描述 加锁语句 锁范围
共享锁 (S) 允许持有锁事务读取上锁数据,其他事务可加共享锁,但不能加排他锁 ( 进行数据变更 ) SELECT ... FOR SHARE 行锁或表锁
排他锁 (X) 允许持有锁事务操作上锁数据,阻止其他事务加任何锁 ( 进行数据读取或者数据变更 ) SELECT ... FOR UPDATEUPDATEDELETE 行锁或表锁
意向共享锁 (IS) 示意当前事务正在打算在表的某些行上加共享锁,用于快速判断表级锁冲突,意向锁只会阻塞全表操作 ( 如:LOCK TABLES ... WRITE ) 隐式加锁(行级锁前自动添加) 表锁
意向排他锁 (IX) 示意当前事务正在打算在表的某些行上加排他锁,用于快速判断表级锁冲突,意向锁只会阻塞全表操作(如:LOCK TABLES ... WRITE) 隐式加锁(行级锁前自动添加) 表锁

兼容性

vbnet 复制代码
|    | X        | IX         | S          | IS         | 
| -  | -------- | ---------- | ---------- | ---------- | 
| X  | Conflict | Conflict   | Conflict   | Conflict   | 
| IX | Conflict | Compatible | Conflict   | Compatible | 
| S  | Conflict | Conflict   | Compatible | Compatible | 
| IS | Conflict | Compatible | Compatible | Compatible | 

锁类型

锁模式 描述 加锁语句 锁范围
记录锁 (Record Lock) S, X 对索引记录加锁,以防止其他数据进行插入/更新/删除 唯一索引精确匹配匹配SELECT ... FOR SHARE / IN SHARE MODE ( S 锁 )SELECT ... FOR UPDATE ( X 锁 )INSERT / UPDATE / DELETE ( X 锁 ) 单行记录 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 ;;
间隙锁 (Gap Lock) 不区分 锁定索引记录间隙 ( 锁住不存在的数据 ),防止其他事务插入 / 更新 / 删除数据而造成幻读,间隙锁不区分共享模式和排他模式,不同的事务可以持有相同 ( 或重叠 ) 的间隙锁 ,默认在 REPEATABLE READ 下使用 范围匹配、非唯一索引匹配SELECT ... FOR SHARE / IN SHARE MODE``SELECT ... FOR UPDATE``UPDATE / DELETE 索引间隙 Gap locking is disabled in READ COMMITTED. In this case, gap locking is disabled for searches and index scans and is used only for foreign-key constraint checking and duplicate-key checking.
Next-Key Lock S, X 记录锁 + 间隙锁的组合,锁定记录及前面间隙 前两者组合 记录+间隙 RECORD LOCKS space id 58 page no 3 n bits 72 index PRIMARY of table test.t trx id 10080 lock_mode X Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; 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 ;;
插入意向锁 -- 一种特殊的间隙锁,表示插入意向,不与其他插入意向锁冲突 ( 不同的事务可以持有相同间隙的插入意向锁 ),但与间隙锁冲突 INSERT This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.

可通过SHOW ENGINE INNODB STATUS命令查看锁状态信息

performance_schema.data_locks

performance_schema.data_lock_waits

不同 SQL 锁行为

SQL 行为 范围 兼容 隔离级别影响 备注
SELECT ... FROM 无锁,InnoDB 使用 MVCC 实现一致非锁定读,多个事务可同时读取数据的不同版本,而无需锁数据 ( 意味着不阻塞其它 SQL ) -- 允许 SQL INSERT、UPDATE、DELETE``SELECT FOR UPDATE`` SELECT FOR SHARE ``/ SELECT`` IN SHARE MODE ``SELECT SERIALIZABLE 使用 Next-Key Lock 无锁机制依赖 undo log 和版本链,使用全局事务列表 + 事务大小进行可见性判断。
SELECT ... FOR UPDATE 1. 意向排他锁 ( IX ) 2. 排他记录锁 ( X,唯一索引等值匹配 ) 3. 间隙锁 ( 范围匹配、非唯一索引匹配 ) 匹配行 ( 唯一索引等值匹配 )匹配间隙 ( 范围匹配、非唯一索引匹配 ) 阻塞 SQL INSERT、UPDATE、DELETE``SELECT FOR UPDATE`` SELECT FOR SHARE ``/ SELECT`` IN SHARE MODE 允许 SQL SELECT READ COMMITTED 使用Record Lock``REPEATABLE READ 使用Next-Key Lock 无索引时锁定聚簇索引全表。
SELECT ... FOR SHARE 1. 意向共享锁 ( IS ) 2. 共享记录锁 (S,唯一索引等值匹配 ) 3. 间隙锁 ( 范围匹配、非唯一索引匹配 ) 匹配行 ( 唯一索引等值匹配 )匹配间隙 ( 范围匹配、非唯一索引匹配 ) 阻塞 SQL INSERT、UPDATE、DELETE``SELECT FOR UPDATE允许 SQL SELECT`` SELECT FOR SHARE ``/ SELECT`` IN SHARE MODE READ COMMITTED 使用Record Lock``REPEATABLE READ 使用Next-Key Lock 支持 NOWAITSKIP LOCKED 跳过锁等待。
UPDATE ... WHERE ... 1. 意向排他锁 ( IX ) 2. 排他记录锁 ( X,唯一索引等值匹配 ) 3. 间隙锁 ( 范围匹配、非唯一索引匹配 ) 匹配行 ( 唯一索引等值匹配 )匹配间隙 ( 范围匹配、非唯一索引匹配 ) 阻塞 SQL INSERT、UPDATE、DELETE``SELECT FOR UPDATE`` SELECT FOR SHARE ``/ SELECT`` IN SHARE MODE 允许 SQL SELECT READ COMMITTED 使用Record Lock``REPEATABLE READ 使用Next-Key Lock 若更新后行长度变化,可能触发隐式锁升级。
DELETE FROM ... WHERE ... 1. 意向排他锁 ( IX ) 2. 排他记录锁 ( X,唯一索引等值匹配 ) 3. 间隙锁 ( 范围匹配、非唯一索引匹配 ) 匹配行 ( 唯一索引等值匹配 )匹配间隙 ( 范围匹配、非唯一索引匹配 ) 删除行加排他锁,外键关联时子表行加共享锁 (S)。阻塞 SQL INSERT、UPDATE、DELETE``SELECT FOR UPDATE`` SELECT FOR SHARE ``/ SELECT`` IN SHARE MODE 允许 SQL SELECT READ COMMITTED 使用Record Lock``REPEATABLE READ 使用Next-Key Lock 外键约束下可能触发级联锁。
INSERT 1. 插入意向锁 2. 排他记录锁 ( X )( 成功插入后 ) 3. 共享锁 ( S )( 冲突感知回滚或防止被删 ) 4. 间隙锁 ( 唯一索引插入并发控制 ) 插入行插入间隙 ( INSERT 之间不影响 ) 阻塞 SQL UPDATE、DELETE``SELECT FOR UPDATE`` SELECT FOR SHARE ``/ SELECT`` IN SHARE MODE ``INSERT(插入位置不同不阻塞)允许 SQL SELECT -- 唯一键冲突可能引发死锁。
INSERT ... ON DUPLICATE KEY UPDATE 1. 插入意向锁 2. 排他记录锁 ( X ) ( 成功插入后 ) 3. 排他锁 ( X ) ( 冲突时,控制操作并发 ) 插入行冲突行 同 INSERT -- 可能因共享锁与排他锁交替导致死锁。the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values.
REPLACE 1. 插入意向锁 2. 排他记录锁 ( X )( 成功插入后 ) 3. 共享锁 ( S )( 冲突时,防止记录被删 ) 4. 间隙锁 ( 删除时 ) 插入行冲突间隙 ( 若行存在,先删后插;若不存在,直接插入 ) 同 INSERT INSERT,但删除旧行可能触发间隙锁 可能因"删-插"操作产生更多锁竞争。
INSERT INTO T SELECT ... FROM S 1. 插入意向锁( 表 T ) 2. 排他锁 ( X ) ( 表 T 插入行 ) 3. 共享锁 ( S ) ( 表 S 读取行) 表 T 插入行 + 表 S 读取行 READ COMMITTED 源表 S 无锁快照读REPEATABLE READ / SERIALIZABLE 加共享锁
CREATE TABLE ... SELECT ... 1. 元数据锁 ( MDL ) ( 新表 ) 2. 排他锁 ( X ) ( 新表插入行 ) 3. 共享锁 ( S ) ( 源表读取行 ) 新表插入行 + 源表读取行 INSERT ... SELECT
外键约束操作 1. 共享锁 ( S ) ( 子表关联行 ) 2. 排他锁 ( X ) ( 父表操作行 ) 父表行 + 子表关联行 READ COMMITTED 可能减少间隙锁范围 子表外键字段需建索引,否则全表扫描加锁。
LOCK TABLES ... WRITE / READ 表级锁 ( S / X ) 整表 与隔离级别无关,但会隐式提交事务 慎用!与事务机制冲突,建议用行锁或 FLUSH TABLES WITH READ LOCK 备份。

死锁

唯一 索引 导致的 死锁

Case 1:插入冲突后回滚(会话1插入后回滚)

会话1 会话2 会话3 锁状态 结果
1 START TRANSACTION;``INSERT (1); - - 会话1对 i=1排他锁 (X) 会话1持有 X 锁。
2 - START TRANSACTION;``INSERT (1); - 会话2因唯一键冲突,尝试对 i=1共享锁 (S) ,但被会话1的 X 锁阻塞,进入等待队列。 会话2等待 S 锁。
3 - - START TRANSACTION;``INSERT (1); 会话3因唯一键冲突,尝试对 i=1共享锁 (S) ,同样被会话1的 X 锁阻塞,进入等待队列。 会话3等待 S 锁。
4 ROLLBACK; - - 会话1释放 X 锁,会话2和会话3的 S 锁请求被批准,各自持有 S 锁。 会话2和会话3均持有 S 锁。
5 - 尝试升级 S 锁为 X 锁以插入数据。 尝试升级 S 锁为 X 锁以插入数据。 会话2需等待会话3释放 S 锁才能升级为 X 锁,反之同理,形成 死锁 死锁 发生 ( MySQL 检测到后回滚一个事务 )。

The first operation by session 1 acquires an exclusive lock for the row. The operations by sessions 2 and 3 both result in a duplicate-key error and they both request a shared lock for the row. When session 1 rolls back, it releases its exclusive lock on the row and the queued shared lock requests for sessions 2 and 3 are granted. At this point, sessions 2 and 3 deadlock: Neither can acquire an exclusive lock for the row because of the shared lock held by the other.

Case 2:删除后插入冲突(会话1删除后提交)

会话1 会话2 会话3 锁状态与行为 结果
1 START TRANSACTION;``DELETE (1); - - 会话1对 i=1排他锁 (X) 会话1持有 X 锁。
2 - START TRANSACTION;``INSERT (1); - 会话2因唯一键冲突(假设原表中已有 i=1),尝试对 i=1共享锁 (S) ,被会话1的 X 锁阻塞。 会话2等待 S 锁。
3 - - START TRANSACTION;``INSERT (1); 会话3因唯一键冲突,同样尝试加 共享锁 (S) ,被会话1的 X 锁阻塞。 会话3等待 S 锁。
4 COMMIT; - - 会话1释放 X 锁,会话2和会话3的 S 锁请求被批准,各自持有 S 锁。 会话2和会话3均持有 S 锁。
5 - 尝试升级 S 锁为 X 锁以插入数据。 尝试升级 S 锁为 X 锁以插入数据。 会话2需等待会话3释放 S 锁才能升级为 X 锁,反之同理,形成 死锁 死锁 发生 ( MySQL 检测到后回滚一个事务 )。

The first operation by session 1 acquires an exclusive lock for the row. The operations by sessions 2 and 3 both result in a duplicate-key error and they both request a shared lock for the row. When session 1 commits, it releases its exclusive lock on the row and the queued shared lock requests for sessions 2 and 3 are granted. At this point, sessions 2 and 3 deadlock: Neither can acquire an exclusive lock for the row because of the shared lock held by the other.

索引顺序导致的死锁

juejin.cn/post/732229...

相关推荐
金金金__9 分钟前
MySQL ZIP安装教程:从解压到启动
mysql
小句26 分钟前
MySQL索引
数据库·mysql
战族狼魂1 小时前
Excel 连接阿里云 RDS MySQL
mysql·阿里云·云计算·excel
九转苍翎2 小时前
全面解析MySQL(5)——“索引、事务、JDBC”三大核心
mysql
winfield8213 小时前
间隙锁(Gap Lock)
数据库·mysql
ghhgy5314 小时前
Windows已经安装了一个MySQL8,通过修改配置文件的端口号跑2个或多个Mysql服务方法,并注册为系统服务
windows·mysql
CANI_PLUS14 小时前
ESP32将DHT11温湿度传感器采集的数据上传到XAMPP的MySQL数据库
android·数据库·mysql
tanxiaomi15 小时前
学习分库分表的前置知识:高可用系统架构理论与实践
java·mysql·spring cloud·系统架构·springboot
Runing_WoNiu19 小时前
mysql 索引失效分析
数据库·mysql