MySQL insert into ... select 的锁情况 【转】

死锁场景之insert语句加锁流程分析(二) 死锁场景之insert语句加锁分析(二) 关于mysql有哪些锁?锁的分类有哪些?可查看另外一篇博客:mysql 的锁 在实际情景中的加锁情况分析博客请查看另外一篇博客:数据库操作加锁情况分析 关于数据库的死锁日志如何查看? 请查看另外一篇博客:死锁日志查看 关于数据库的insert导致死锁案例查看:死锁场景之insert语句死锁分析 这篇主要是介绍平时常见的insert语句的加锁情况。 大部分的DBA同行都知道在事务执行insert的时候会申请一把插入意向锁(Insert Intention Lock)。 阅读详情

一、前言

上周遇到一个因insert而引发的死锁问题,其成因比较令人费解。
于是想要了解一下insert加锁机制,但是发现网上介绍的文章比较少且零散,挖掘过程比较忙乱。

本以为只需要系统学习一个较完全的逻辑,但是实际牵扯很多innodb锁相关知识及加锁方式。我好像并没有那么大的能耐,把各种场景的加锁过程一一列举并加之分析;亦没有太多的精力验证网上的言论的准确性。

只好根据现在了解的内容,参考官方文档,说说自己当前的理解。
本文仅供参考,如有误导,概不负责。

二、现场状态

不同的MySQL版本,不同的参数设置,都可能对加锁过程有影响。
分析加锁机制还是应当尽可能多地列举一下关键参数,例如:当前mysql版本、事务隔离级别等。
如下,仅仅只列出个别比较重要的参数。

1.数据库版本

      
1
2
3
4
5
6
      
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.6.27 |
+-----------+

2. 数据库引擎

      
1
2
3
4
5
6
7
8
      
mysql> show variables like '%engine%';
+----------------------------+--------+
| Variable_name | Value |
+----------------------------+--------+
| default_storage_engine | InnoDB |
| default_tmp_storage_engine | InnoDB |
| storage_engine | InnoDB |
+----------------------------+--------+

注:InnoDB支持事务,Myisam不支持事务;InnoDB支持行锁和表锁;Myisam不支持行锁。

3. 事务隔离级别

      
1
2
3
4
5
6
      
mysql> select @@global.tx_isolation, @@session.tx_isolation, @@tx_isolation;
+-----------------------+------------------------+-----------------+
| @@global.tx_isolation | @@session.tx_isolation | @@tx_isolation |
+-----------------------+------------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+------------------------+-----------------+

注:几种事务隔离级别:READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE

4. 查看gap锁开启状态

      
1
2
3
4
5
6
      
mysql> show variables like 'innodb_locks_unsafe_for_binlog';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_locks_unsafe_ for_binlog | OFF |
+--------------------------------+-------+

innodb_locks_unsafe_for_binlog:默认值为0,即启用gap lock。
最主要的作用就是控制innodb是否对gap加锁。
但是,这一设置变更并不影响外键和唯一索引(含主键)对gap进行加锁的需要。
开启innodb_locks_unsafe_for_binlog的REPEATABLE-READ事务隔离级别,很大程度上已经蜕变成了READ-COMMITTED。

参见官方文档[^1]:

By default, the value of innodb_locks_unsafe_for_binlog is 0 (disabled), which means that gap locking is enabled: InnoDB uses next-key locks for searches and index scans. To enable the variable, set it to 1. This causes gap locking to be disabled: InnoDB uses only index-record locks for searches and index scans.

Enabling innodb_locks_unsafe_for_binlog does not disable the use of gap locking for foreign-key constraint checking or duplicate-key checking.

The effect of enabling innodb_locks_unsafe_for_binlog is similar to but not identical to setting the transaction isolation level to READ COMMITTED.

5. 查看自增锁模式

      
1
2
3
4
5
6
      
mysql> show variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1 |
+--------------------------+-------+

innodb_autoinc_lock_mode有3种配置模式:0、1、2,分别对应”传统模式”, “连续模式”, “交错模式”。[^8]
传统模式:涉及auto-increment列的插入语句加的表级AUTO-INC锁,只有插入执行结束后才会释放锁。这是一种兼容MySQL 5.1之前版本的策略。
连续模式:可以事先确定插入行数的语句(包括单行和多行插入),分配连续的确定的auto-increment值;对于插入行数不确定的插入语句,仍加表锁。这种模式下,事务回滚,auto-increment值不会回滚,换句话说,自增列内容会不连续。
交错模式:同一时刻多条SQL语句产生交错的auto-increment值。

由于insert语句常常涉及自增列的加锁过程,会涉及到AUTO-INC Locks加锁过程。
为了分步了解insert加锁过程,本文暂不讨论任何涉及自增列的加锁逻辑。
这一参数设置相关内容可能会出现在我的下一篇文章里。

n. etc

相关的参数配置越详情越好。

三、InnoDB锁类型[^2]

1. 基本锁

基本锁:共享锁(Shared Locks:S锁)与排他锁(Exclusive Locks:X锁)

mysql允许拿到S锁的事务读一行,允许拿到X锁的事务更新或删除一行。
加了S锁的记录,允许其他事务再加S锁,不允许其他事务再加X锁;
加了X锁的记录,不允许其他事务再加S锁或者X锁。

mysql对外提供加这两种锁的语法如下:
加S锁:select…lock in share mode
加X锁:select…for update

2. 意向锁(Intention Locks)

InnoDB为了支持多粒度(表锁与行锁)的锁并存,引入意向锁。
意向锁是表级锁,可分为意向共享锁(IS锁)和意向排他锁(IX锁)。

InnoDB supports multiple granularity locking which permits coexistence of row-level locks and locks on entire tables. To make locking at multiple granularity levels practical, additional types of locks called intention locks are used. Intention locks are table-level locks in InnoDB that indicate which type of lock (shared or exclusive) a transaction will require later for a row in that table. There are two types of intention locks used in InnoDB (assume that transaction T has requested a lock of the indicated type on table t):
Intention shared (IS): Transaction T intends to set S locks on individual rows in table t.
Intention exclusive (IX): Transaction T intends to set X locks on those rows.

事务在请求S锁和X锁前,需要先获得对应的IS、IX锁。

Before a transaction can acquire an S lock on a row in table t, it must first acquire an IS or stronger lock on t. Before a transaction can acquire an X lock on a row, it must first acquire an IX lock on t.

意向锁产生的主要目的是为了处理行锁和表锁之间的冲突,用于表明“某个事务正在某一行上持有了锁,或者准备去持有锁”。

The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.

共享锁、排他锁与意向锁的兼容矩阵如下:

  X IX S IS
X 冲突 冲突 冲突 冲突
IX 冲突 兼容 冲突 兼容
S 冲突 冲突 兼容 兼容
IS 冲突 兼容 兼容 兼容
思考

从官方文档字面意思上看意向锁是表级锁,但是大牛不认为“Intention lock 是表级锁”[^5]?
另外,由于意向锁主要用于解决行锁与表锁间冲突问题,鉴于平时表级操作特别少,在分析加锁过程是否可以不用过多考虑意向锁的问题?

3. 行锁

记录锁(Record Locks)

记录锁, 仅仅锁住索引记录的一行。
单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走任何索引时,那么将会在每一条聚集索引后面加X锁,这个类似于表锁,但原理上和表锁应该是完全不同的。

参见官方文档[^3]:

If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order.

间隙锁(Gap Locks)

区间锁, 仅仅锁住一个索引区间(开区间)。
在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。

next-key锁(Next-Key Locks)

record lock + gap lock, 左开右闭区间。

A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.

By default, InnoDB operates in REPEATABLE READ transaction isolation level and with the innodb_locks_unsafe_for_binlog system variable disabled. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows。

默认情况下,innodb使用next-key locks来锁定记录。
但当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。

插入意向锁(Insert Intention Locks)

Gap Lock中存在一种插入意向锁(Insert Intention Lock),在insert操作时产生。在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。
假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。

An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. 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. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

注:插入意向锁并非意向锁,而是一种特殊的间隙锁。

4. 行锁的兼容矩阵[^4]

  Gap Insert Intention Record Next-Key
Gap 兼容 兼容 兼容 兼容
Insert Intention 冲突 兼容 兼容 冲突
Record 兼容 兼容 冲突 冲突
Next-Key 兼容 兼容 冲突 冲突

表注:横向是已经持有的锁,纵向是正在请求的锁。

由于S锁和S锁是完全兼容的,因此在判别兼容性时只考虑持有的锁与请求的锁是这三种组合情形:X、S和S、X和X、X。
另外,需要提醒注意的是进行兼容判断也只是针对于加锁涉及的行有交集的情形。

分析兼容矩阵可以得出如下几个结论:

  • INSERT操作之间不会有冲突。
  • GAP,Next-Key会阻止Insert。
  • GAP和Record,Next-Key不会冲突
  • Record和Record、Next-Key之间相互冲突。
  • 已有的Insert锁不阻止任何准备加的锁。

5. 自增锁(AUTO-INC Locks)

AUTO-INC锁是一种特殊的表级锁,发生涉及AUTO_INCREMENT列的事务性插入操作时产生。

官方解释如下[^3]:

An AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values into the table, any other transactions must wait to do their own inserts into that table, so that rows inserted by the first transaction receive consecutive primary key values.

四、insert加锁过程

官方文档[^6]对于insert加锁的描述如下:

INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.

Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. 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. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6 each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock.

简单的insert会在insert的行对应的索引记录上加一个排它锁,这是一个record lock,并没有gap,所以并不会阻塞其他session在gap间隙里插入记录。

不过在insert操作之前,还会加一种锁,官方文档称它为insertion intention gap lock,也就是意向的gap锁。这个意向gap锁的作用就是预示着当多事务并发插入相同的gap空隙时,只要插入的记录不是gap间隙中的相同位置,则无需等待其他session就可完成,这样就使得insert操作无须加真正的gap lock。
假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。

假设发生了一个唯一键冲突错误,那么将会在重复的索引记录上加读锁。当有多个session同时插入相同的行记录时,如果另外一个session已经获得该行的排它锁,那么将会导致死锁。

思考:Insert Intention Locks作用

Insert Intention Locks的引入,我理解是为了提高数据插入的并发能力。
如果没有Insert Intention Locks的话,可能就需要使用Gap Locks来代替。

五、insert死锁场景分析

接下来,带大家看几个与insert相关的死锁场景。

1. duplicate key error引发的死锁

这个场景主要发生在两个以上的事务同时进行唯一键值相同的记录插入操作。

表结构
      
1
2
3
4
5
6
7
8
9
      
CREATE TABLE `aa` (
`id` int( 10) unsigned NOT NULL COMMENT '主键',
`name` varchar( 20) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int( 11) NOT NULL DEFAULT '0' COMMENT '年龄',
`stage` int( 11) NOT NULL DEFAULT '0' COMMENT '关卡数',
PRIMARY KEY ( `id`),
UNIQUE KEY `udx_name` ( `name`),
KEY `idx_stage` ( `stage`)
) ENGINE= InnoDB DEFAULT CHARSET=utf8;
表数据
      
1
2
3
4
5
6
7
8
9
10
      
mysql> select * from aa;
+----+------+-----+-------+
| id | name | age | stage |
+----+------+-----+-------+
| 1 | yst | 11 | 8 |
| 2 | dxj | 7 | 4 |
| 3 | lb | 13 | 7 |
| 4 | zsq | 5 | 7 |
| 5 | lxr | 13 | 4 |
+----+------+-----+-------+
事务执行时序表
T1(36727) T2(36728) T3(36729)
begin; begin; begin;
insert into aa values(6, ‘test’, 12, 3);    
  insert into aa values(6, ‘test’, 12, 3);  
    insert into aa values(6, ‘test’, 12, 3);
rollback;    
    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
  Query OK, 1 row affected (13.10 sec)  

如果T1未rollback,而是commit的话,T2和T3会报唯一键冲突:ERROR 1062 (23000): Duplicate entry ‘6’ for key ‘PRIMARY’

事务锁占用情况

T1 rollback前,各事务锁占用情况:

      
1
2
3
4
5
6
7
8
      
mysql> select * from information_schema.innodb_locks;
+--------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_ type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+--------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
| 36729:24:3:7 | 36729 | S | RECORD | ` test`.`aa` | PRIMARY | 24 | 3 | 7 | 6 |
| 36727:24:3:7 | 36727 | X | RECORD | ` test`.`aa` | PRIMARY | 24 | 3 | 7 | 6 |
| 36728:24:3:7 | 36728 | S | RECORD | ` test`.`aa` | PRIMARY | 24 | 3 | 7 | 6 |
+--------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+

注:mysql有自己的一套规则来决定T2与T3哪个进行回滚,本文不做讨论。

死锁日志
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
      
------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-07-21 19:34:23 700000a3f000
*** (1) TRANSACTION:
TRANSACTION 36728, ACTIVE 199 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1184, 2 row lock(s)
MySQL thread id 13, OS thread handle 0x700000b0b000, query id 590 localhost root update
insert into aa values(6, 'test', 12, 3)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 3 n bits 80 index `PRIMARY` of table ` test`.`aa` trx id 36728 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 36729, ACTIVE 196 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1184, 2 row lock(s)
MySQL thread id 14, OS thread handle 0x700000a3f000, query id 591 localhost root update
insert into aa values(6, 'test', 12, 3)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 24 page no 3 n bits 80 index `PRIMARY` of table ` test`.`aa` trx id 36729 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 3 n bits 80 index `PRIMARY` of table ` test`.`aa` trx id 36729 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
死锁成因

事务T1成功插入记录,并获得索引id=6上的排他记录锁(LOCK_X | LOCK_REC_NOT_GAP)。
紧接着事务T2、T3也开始插入记录,请求排他插入意向锁(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION);但由于发生重复唯一键冲突,各自请求的排他记录锁(LOCK_X | LOCK_REC_NOT_GAP)转成共享记录锁(LOCK_S | LOCK_REC_NOT_GAP)。

T1回滚释放索引id=6上的排他记录锁(LOCK_X | LOCK_REC_NOT_GAP),T2和T3都要请求索引id=6上的排他记录锁(LOCK_X | LOCK_REC_NOT_GAP)。
由于X锁与S锁互斥,T2和T3都等待对方释放S锁。
于是,死锁便产生了。

如果此场景下,只有两个事务T1与T2或者T1与T3,则不会引发如上死锁情况产生。

思考
  • 为什么发现重复主键冲突的时候,要将事务请求的X锁转成S锁?
    (比较牵强的)个人理解,跟插入意向锁类型,也是为了提高插入的并发效率。

  • 插入前请求插入意向锁的作用?
    个人认为,通过兼容矩阵来分析,Insert Intention Locks是为了减少插入时的锁冲突。

2. GAP与Insert Intention冲突引发的死锁

表结构
      
1
2
3
4
5
6
      
CREATE TABLE `t` (
`a` int( 11) NOT NULL,
`b` int( 11) DEFAULT NULL,
PRIMARY KEY ( `a`),
KEY `idx_b` ( `b`)
) ENGINE= InnoDB DEFAULT CHARSET=utf8;
表数据
      
1
2
3
4
5
6
7
8
9
      
mysql> select * from t;
+----+------+
| a | b |
+----+------+
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 11 | 22 |
+----+------+
事务执行时序表
T1(36831) T2(36832)
begin; begin;
select * from t where b = 6 for update;  
  select * from t where b = 8 for update;
insert into t values (4,5);  
  insert into t values (4,5);
  ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
Query OK, 1 row affected (5.45 sec)  
事务锁占用情况

T2 insert前,各事务锁占用情况:

      
1
2
3
4
5
6
7
      
mysql> select * from information_schema.innodb_locks;
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_ type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+
| 36831:25:4:5 | 36831 | X,GAP | RECORD | ` test`.`t` | idx_b | 25 | 4 | 5 | 22, 11 |
| 36832:25:4:5 | 36832 | X,GAP | RECORD | ` test`.`t` | idx_b | 25 | 4 | 5 | 22, 11 |
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+

死锁日志
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
      
------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-07-28 12:28:34 700000a3f000
*** (1) TRANSACTION:
TRANSACTION 36831, ACTIVE 17 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
MySQL thread id 38, OS thread handle 0x700000b0b000, query id 953 localhost root update
insert into t values (4,5)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 4 n bits 72 index `idx_b` of table `test`.`t` trx id 36831 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000016; asc ;;
1: len 4; hex 8000000b; asc ;;
*** (2) TRANSACTION:
TRANSACTION 36832, ACTIVE 13 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 360, 2 row lock(s)
MySQL thread id 39, OS thread handle 0x700000a3f000, query id 954 localhost root update
insert into t values (4,5)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 25 page no 4 n bits 72 index `idx_b` of table `test`.`t` trx id 36832 lock_mode X locks gap before rec
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000016; asc ;;
1: len 4; hex 8000000b; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` trx id 36832 lock mode S locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000004; asc ;;
1: len 6; hex 000000008fdf; asc ;;
2: len 7; hex 8d000001d00110; asc ;;
3: len 4; hex 80000005; asc ;;
*** WE ROLL BACK TRANSACTION (2)
死锁成因

事务T1执行查询语句,在索引b=6上加排他Next-key锁(LOCK_X | LOCK_ORDINARY),会锁住idx_b索引范围(4, 22)。
事务T2执行查询语句,在索引b=8上加排他Next-key锁(LOCK_X | LOCK_ORDINARY),会锁住idx_b索引范围(4, 22)。由于请求的GAP与已持有的GAP是兼容的,因此,事务T2在idx_b索引范围(4, 22)也能加锁成功。

事务T1执行插入语句,会先加排他Insert Intention锁。由于请求的Insert Intention锁与已有的GAP锁不兼容,则事务T1等待T2释放GAP锁。
事务T2执行插入语句,也会等待T1释放GAP锁。
于是,死锁便产生了。

注:LOCK_ORDINARY拥有LOCK_GAP一部分特性。

思考:Insert Intention锁在加哪级索引上?

这个排他锁加在PK上,还是二级索引上?

六、课后思考

  1. 无主键的加锁过程
    无PK时,会创建一个隐式聚簇索引。加锁在这个隐式聚簇索引会有什么不同?

  2. 复合索引加锁过程

  3. 多条件(where condition)加锁过程
  4. 隐式锁与显式锁,隐式锁什么情况下会转换成显式锁

  5. 如果插入意向锁不阻止任何锁,这个锁还有必要存在吗?
    目前看到的作用是,通过加锁的方式来唤醒等待线程。
    但这并不意味着,被唤醒后可以直接做插入操作了。需要再次判断是否有锁冲突。

七、补充知识

1. 查看事务隔离级别

SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;

2. 设置隔离级别

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
例如:set session transaction isolation level read uncommitted;

3. 查看auto_increment机制模式

show variables like ‘innodb_autoinc_lock_mode’;

4. 查看表状态

show table status like ‘plan_branch’\G;
show table status from test like ‘plan_branch’\G;

5. 查看SQL性能

show profiles
show profile for query 1;

6. 查看当前最新事务ID

每开启一个新事务,记录当前最新事务的id,可用于后续死锁分析。
show engine innodb status\G;

7. 查看事务锁等待状态情况

select from information_schema.innodb_locks;
select 
from information_schema.innodb_lock_waits;
select * from information_schema.innodb_trx;

8. 查看innodb状态(包含最近的死锁日志)

show engine innodb status;

八、参考文档

[^1]: InnoDB Startup Options and System Variables
[^2]: InnoDB Locking
[^3]: Clustered and Secondary Indexes
[^4]: [MySQL] gap lock/next-key lock浅析
[^5]: Intention Lock是否表级锁
[^6]: Locks Set by Different SQL Statements in InnoDB

[^8]: AUTO_INCREMENT lock Handing in InnoDB
[^9]: 深入理解innodb的锁(record,gap,Next-Key lock)

[^11]: The INFORMATION_SCHEMA INNODB_LOCKS Table
[^12]: The INFORMATION_SCHEMA INNODB_TRX Table
[^13]: mysql innodb插入意向锁

Oracle中的INSERT语句是否会锁表? 总结来说,Oracle中的INSERT语句通常不会锁定整个表,而是对插入的数据行进行锁定。然而,需要注意的是,在某些情况下,Oracle数据库可能会选择锁定整个表。例如,在某些特殊的并发操作和事务隔离级别下,Oracle可能会使用更高级别的锁定,以确保数据的一致性和完整性。INSERT语句并不会锁定整个表,而是对插入的数据行进行锁定,以确保数据的一致性。在Oracle数据库中,执行INSERT语句时,是否会对表进行锁定是取决于数据库的隔离级别和具体的操作情况的。 阅读详情

相关推荐

mysql INSERT INTO时的锁

操作,也只有成功提交的操作才会对表产生影响。而且,在事务内部,其他线程只能查看事务开始前的数据状态,无法看到正在进行的中间状态。将不会触发,因为这将导致一个错误,因为自增主键值已经存在。在这种情况下,你需要先检查记录是否存在,如果不存在,再执行插入操作。提交事务)来确保在事务执行过程中,只有持有事务的线程可以对表进行写操作。操作时直接锁定整个表,确保在同一时间只有一个线程可以对表进行写操作。如果表中有一个自增的主键,并且你尝试插入一个已经存在的值,那么。是唯一的,并且已经存在,那么。

ithongchou的博客 1787

mysql insert锁机制

一、前言 上周遇到一个因insert而引发的死锁问题,其成因比较令人费解。 于是想要了解一下insert加锁机制,但是发现网上介绍的文章比较少且零散,挖掘过程比较忙乱。 本以为只需要系统学习一个较完全的逻辑,但是实际牵扯很多innodb锁相关知识及加锁方式。我好像并没有那么大的能耐,把各种场景的加锁过程一一列举并加之分析;亦没有太多的精力验证网上的言论的准确性。 只好根据现在了

zhanghongzheng3213的专栏 2万+

【MySQL】MySQL Insert into select 大量锁表导致无法插入

1.概述 原文:因用了Insert into select语句,同事被开除了! Insert into select 请慎用,同事因为使用了 Insert into select 语句引发了重大生产事故,最后被开除。 某天 xxx 接到一个需求,需要将表 A 的数据迁移到表 B 中去做一个备份。他本想通过程序先查询查出来然后批量插入,但 xxx 觉得这样有点慢,需要耗费大量的网络 I/O,决定采取别的方法进行实现。 通过在某度的海洋里遨游,他发现了可以使用 insert into select 实现,这.

九师兄 8790

分享insert into select遇到的死锁问题(项目实战)

本篇分享分享insert into select遇到的死锁问题!欢迎打卡、沟通、交流!

末尾卡片和博主交流学习! 7696

HackRF实战:用Portapack扩展板玩转GPS信号模拟(附固件刷写教程)

本文详细介绍了如何使用HackRF与Portapack扩展板进行GPS信号模拟,包括固件刷写、信号生成与发射配置等实战操作。通过专业工具和优化技巧,读者可以掌握软件无线电的高级应用,解决手机定位难题,同时强调安全合规使用的重要性。

weixin_28717431的博客 362

华为2024机考真题集合

2.中位数是指把一组数据从小到大排列,最中间的那个数,如果这组数据的个数是奇数,那最中间那个就是中位数,如果这组数据的个数为偶数,那就把中间的两个数之和除以 2,所得的结果就是中位数。在实际考试中会随机抽三道题目进行考察,其中两道100分卷题和一道200分卷题,总共400分。幼儿园组织活动,老师布置了一个任务:每个小朋友去了解与自己同一个小区的小朋友还有几个。3.查找整型数组中元素的众数并组成一个新的数组,求新数组的中位数。1.众数是指一组数据中出现次数量多的那个数,众数可以是多个。

qq_25360769的博客 5093

【MySQL】 insert into语句几种方式

MySQL中添加或插入语句(Insert)的几种使用方式 1.INSERT 简写方式插入数据(不推荐) 1.先看一下表中有那些数据 2.*使用Inset into 表名 values(值1,值2)进行插入,并对查看插入数据是否成功*   注意:insert这种简写的方式虽然非常简单,但是Values后面的值必须和表中的类顺序对应,且类型要保持一直,即使表中某一个列不需要值也必须赋值为null,比如我们的主键id设置的是递增实际上是不用设置值的,但是使用这种方式必须赋值为null   不推荐的原因

a6458360的博客 7万+

Axure rp9 中继器新建表格

Axure rp9 中继器新建表格

weixin_49295490的博客 1652

MySQL的insert into select 引发锁表

上周五HaC我要上线,有一个脚本需要执行,执行前需要备份一个表。运维大佬:“这个表的备份为什么要这么久,,??”1秒过去了……2秒过去了……期间运营反馈系统出现大量订单超时情况。大佬找到...

好好学java 2937

Homebrew 必备软件清单:我的 macOS 开发环境搭建全记录

本文详细介绍了如何使用Homebrew在macOS上搭建高效的开发环境,包括基础配置、核心开发工具链、高效编码工具集等30个必备工具。通过Homebrew这一macOS缺失的包管理器,开发者可以一键安装命令行工具和GUI应用,大幅提升工作效率。文章还提供了日常维护命令和疑难问题解决方案,适合从新手到资深开发者的所有用户。

weixin_28836507的博客 353

ChatGPT Web转API工具:原理、部署与集成实战

在AI应用开发中,API(应用程序编程接口)是实现系统间通信和数据交换的核心技术,它通过标准化的请求响应机制,将复杂功能封装为可编程的服务。其工作原理基于HTTP协议,通过定义清晰的端点、方法和数据格式,使不同平台的应用能够高效集成AI能力。这一技术价值在于极大降低了开发门槛,促进了自动化流程和智能功能的快速落地,广泛应用于智能客服、内容生成和创意辅助等场景。本文聚焦于如何通过浏览器自动化技术,将ChatGPT的Web界面能力转化为标准API服务,其中涉及Playwright无头浏览器操作和会话Cookie

weixin_42533910的博客 1761

mysql insert into会锁表_MySQL的insert into select 引发锁表

上周五HaC我要上线,有一个脚本需要执行,执行前需要备份一个表。运维大佬:“这个表的备份为什么要这么久,,??”1秒过去了......2秒过去了......期间运营反馈系统出现大量订单超时情况。大佬找到我,问:“你怎么备份的?”我:“insert into select * from 呀!”大佬:“??你是不是不想混了?”又是被大佬嫌弃的一天,为了不卷铺盖走人,我决定去学习一下表备份的常见方法。...

weixin_39688687的博客 1756

EPLAN二次开发实战指南:从API接口到定制化界面

本文为EPLAN二次开发实战指南,详细介绍了从搭建C#开发环境、理解API接口到创建定制化插件的完整流程。通过实战案例,如批量修改设备属性和设计交互式界面,帮助电气工程师利用EPLAN二次开发实现工作流程自动化与深度定制,从而显著提升设计效率。

weixin_29246195的博客 804

insert会锁表吗mysql_MySQL的insert into select 引发锁表

又是被大佬嫌弃的一天,为了不卷铺盖走人,我决定去学习一下表备份的常见方法。MySQL一般我们在生产上备份数据通常会用到 这两种方法:INSERT INTO SELECTCREATE TABLE AS SELECT注:本文仅针对MySQL innodb引擎,事务是可重复读RR,数据库版本为5.51.INSERT INTO SELECTinsert into Table2(field1,field2,...

weixin_33050129的博客 4222

把 SAP BTP Destination 写进 MTA:一段 com.sap.application.content 配置背后的部署编排学

MTA内容模块解析:SAP BTP中的声明式部署机制 本文详细解析了mta.yaml中com.sap.application.content模块在SAP BTP云环境中的作用。该模块通过声明式配置实现内容投递,无需编译或部署代码。核心功能包括: Destination创建:通过content-target:true标识将配置投递到Destination服务 HTML5应用部署:将静态资源投递到HTML5 Application Repository 安全集成:配置XSUAA服务实现OAuth2用户令牌交换

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 165

使用Verilog实现QR分解——嵌入式 [设计与代码]

通过Verilog的硬件描述语言,我们可以将QR分解算法实现为硬件电路,并在嵌入式系统中高效地运行。本文将介绍如何使用Verilog设计并实现QR分解算法,该算法用于将一个矩阵分解为正交矩阵Q和上三角矩阵R。具体而言,QR分解通过对矩阵的列进行正交化处理,得到正交矩阵Q,并将对应的正交化结果存储在上三角矩阵R中。该算法通过将矩阵分解为两个部分,正交矩阵Q和上三角矩阵R,实现了对矩阵的分解和变换。模块,该模块接收待分解矩阵A,并输出正交矩阵Q和上三角矩阵R。下面给出了使用Verilog实现QR分解的源代码。

CodeMaven的博客 620

MySQL中 INSERT INTO SELECT FROM 的使用

MySQL中 INSERT INTO SELECT FROM 的使用 使用场景 通过一条sql语句实现从多个表中组合字段然后插入到另外的一个新表中。 1.两表之间,从一张表中查出字段的值插入到另一张表中对应的字段 INSERT INTO tab1(field1,field2) SELECT field1,field2 FROM tab2; 2.三表之间,从两张表中查出字段的值插入到另一张表中对应...

qq_20732077的博客 1万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

SQL(8):INSERT INTO SELECT与SELECT INTO,选数据出来,放到另一个表中

INSERT INTO SELECT与SELECT INTO,选数据出来,放到另一个表中

在下_诸葛的博客 6799

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

在 MySQL 中使用 Insert Into Select

是 MySQL 中一个非常实用的数据操作语句,允许将数据从一个表插入到另一个表中。通过使用,可以实现数据迁移、汇总和备份等操作。在实际应用中,需要确保列的匹配、考虑性能和使用事务控制。掌握这些技术可以帮助您更高效地管理 MySQL 数据库中的数据。

网络技术联盟站 4864

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

SELECT INTO 和 INSERT INTO SELECT 两种表复制语句

第一句:SELECT * INTO [ToTable] FROM [FromTable] 第二句:INSERT INTO [ToTable] ([fild_One],[fild_Two]) SELECT [fild_One], 8 FROM [FromTable] 以上两句都是将 [FromTable] 的数据插入到 [ToTable],但两句又有区别的: 第一句(SELEC

u012467492的专栏 9686

MySQL中 insert into select和create table的区别 已经复制表的方法

MySQL中 insert into select和create table的区别 MySQL一般我们在生产上备份数据通常会用到 这两种方法: INSERT INTO SELECT CREATE TABLE AS SELECT 本文仅针对MySQL innodb引擎,事务是可重复读RR 1.INSERT INTO SELECT insert into Table2(field1,field2,...

HaC 的博客 2669

mysql5.7修改字段类型会不会锁表_MySQL的insert into select 引发锁表

又是被大佬嫌弃的一天,为了不卷铺盖走人,我决定去学习一下表备份的常见方法。MySQL一般我们在生产上备份数据通常会用到 这两种方法:INSERT INTO SELECTCREATE TABLE AS SELECT注:本文仅针对MySQL innodb引擎,事务是可重复读RR,数据库版本为5.51.INSERT INTO SELECTinsert into Table2(field1,field2,...

weixin_29888579的博客 1766

MySQL——INSERT INTO

INSERT INTO语句:INSERT [LOW_PRIORITY | DELAYED] [IGNORE][INTO] tbl_name [(col_name,...)]VALUES (expression,...),(...),...INSERT INTO SELECT语句:INSERT [LOW_PRIORITY | DELAYED] [IGNORE][INTO] tbl_n

冷秋魂的专栏 1万+
上一篇: 未排序数组中累加和为给定值的最长子数组
下一篇: MySQL详解--锁
大树叶
博客等级 码龄12年 1171粉丝 664原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值