不同隔离级别 / 不同并发模型核心是性能和正确性之间的 Trade Off
锁规则
-
索引加锁 -- 锁是设置在索引上,包括 Secondary Index 和 Clustered Index ( 记录本身 );
-
按需加锁 -- 访问到的符合条件的索引数据才 ( 都 ) 会上锁;
-
锁请求成功当且仅当:或无锁、或当前事务已持有符合的共享/排他锁、或已有锁和申请锁兼容;
-
死锁本质是不同事务彼此等待对方的锁,处理:不同的事务按照相同的顺序申请访问/操作数据;
-
唯一索引使用间隙锁来保证唯一性,此处间隙锁的使用和事务隔离级别无关;
-
若查询条件使用唯一索引且精确匹配,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 |
支持 NOWAIT 或 SKIP 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.