-- 准备表
CREATE TABLE `user_1` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`age` int DEFAULT NULL COMMENT '年龄',
`name` varchar(255) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`),
KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
-- 先插入2条基础数据,并提交事务
insert into user_1 values (null, 10, 'aa');
insert into user_1 values (null, 20, 'ee');
注意:
mysql> select * from user_1;
+----+------+------+
| id | age | name |
+----+------+------+
| 1 | 10 | aa |
| 2 | 20 | ee |
+----+------+------+
2 rows in set (0.00 sec)
① 会话1先执行:执行成功
-- step1:update name= 'ee',不提交事务
## 执行成功
update user_1 set age = 21 where name= 'ee';
② 会话2再执行: 执行成功
-- step2:update name= 'aa',不提交事务
## 执行被阻塞
update user_1 set age = 11 where name= 'aa';
③ 会话1再执行:执行被阻塞**
-- step3:插入cc,不提交
insert into user_1 values (null, 15, 'cc');
问题:step3中,此处会话1为什么被阻塞,在等待什么?
④ 会话2再执行:发生死锁**
-- step4:插入cc,不提交
insert into user_1 values (null, 30, 'gg');
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 |
+-------------+-------------+-----------+-----------+------------------+------------+------------+-----------+----------+-----------+
| 5900:33:4:3 | 5900 | X,GAP | RECORD | `mysql`.`user_1` | idx_name | 33 | 4 | 3 | 'ee', 2 |
| 5901:33:4:3 | 5901 | X,GAP | RECORD | `mysql`.`user_1` | idx_name | 33 | 4 | 3 | 'ee', 2 |
+-------------+-------------+-----------+-----------+------------------+------------+------------+-----------+----------+-----------+
2 rows in set, 1 warning (0.00 sec)
mysql> select * from information_schema.INNODB_LOCK_WAITS;
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| 5900 | 5900:33:4:3 | 5901 | 5901:33:4:3 |
+-------------------+-------------------+-----------------+------------------+
1 row in set, 1 warning (0.00 sec)
(由于列比较多,所以改成竖排显示,(字段含义见 8.2))
mysql> select * from information_schema.innodb_trx\G;
*************************** 1. row ***************************
trx_id: 5901
trx_state: RUNNING
trx_started: 2022-08-25 20:06:05
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 5
trx_mysql_thread_id: 3
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 1
trx_lock_structs: 4
trx_lock_memory_bytes: 1136
trx_rows_locked: 3
trx_rows_modified: 1
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 0
trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
trx_id: 5900
trx_state: LOCK WAIT
trx_started: 2022-08-25 20:06:01
trx_requested_lock_id: 5900:33:4:3
trx_wait_started: 2022-08-25 20:06:09
trx_weight: 6
trx_mysql_thread_id: 2
trx_query: /* ApplicationName=DataGrip 2021.1 */ insert into user_1 values (null, 15, 'cc')
trx_operation_state: inserting
trx_tables_in_use: 1
trx_tables_locked: 1
trx_lock_structs: 4
trx_lock_memory_bytes: 1136
trx_rows_locked: 4
trx_rows_modified: 2
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 0
trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.00 sec)
- 会话1主要信息:【trx_id:5900 trx_state: LOCK WAIT trx_started:2022-08-25 20:06:01】
- 会话2主要信息:【trx_id:5901 trx_state: RUNNING trx_started:2022-08-25 20:06:05】
+-------------------+-------------------+-----------------+------------------+
| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |
+-------------------+-------------------+-----------------+------------------+
| 5900 | 5900:33:4:3 | 5901 | 5901:33:4:3 |
+-------------------+-------------------+-----------------+------------------+
问题:会话1为什么要等待会话2释放锁?
+-------------+-------------+-----------+-----------+------------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+-------------+-------------+-----------+-----------+------------------+------------+------------+-----------+----------+-----------+
| 5900:33:4:3 | 5900 | X,GAP | RECORD | `mysql`.`user_1` | idx_name | 33 | 4 | 3 | 'ee', 2 |
| 5901:33:4:3 | 5901 | X,GAP | RECORD | `mysql`.`user_1` | idx_name | 33 | 4 | 3 | 'ee', 2 |
+-------------+-------------+-----------+-----------+------------------+------------+------------+-----------+----------+-----------+
进一步看information_schema.innodb_trx,从上面看到
- 会话1的语句,事务5900在等待锁, 锁定的行数是3【trx_rows_locked: 4 trx_rows_modified: 2】
- 会话2的语句,事务5901是运行状态, 锁定的行数是3【trx_rows_locked: 3 trx_rows_modified: 1】
① 会话1先执行:执行成功
-- step1:update name= 'ee',不提交事务
## 执行成功
update user_1 set age = 21 where name= 'ee';
-- step2:update name= 'aa',不提交事务
## 执行被阻塞
update user_1 set age = 11 where name= 'aa';
-- step3:插入cc,不提交
insert into user_1 values (null, 15, 'cc');
④ 会话2再执行:发生死锁
-- step4:插入cc,不提交
insert into user_1 values (null, 30, 'gg');
时间 | 会话1 | 会话2 |
---|---|---|
T1 | update user_1 set age = 21 where name= ‘ee’; 持有锁(10,20],(20,+∝) | |
T2 | update user_1 set age = 11 where name= ‘aa’; 持有锁(-∝,10],(10,20) | |
T3 | insert into user_1 values (null, 15, ‘cc’);等待会话2的锁(10,20) | |
T4 | insert into user_1 values (null, 30, ‘gg’);等待会话1的锁(20,+∝) |
开启主动死锁检测,默认开启:
innodb_deadlock_detect=on
mysql> show engine innodb status;
2022-08-25 20:06:36 0x4c38 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 57 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 8 srv_active, 0 srv_shutdown, 7898 srv_idle
srv_master_thread log flush and writes: 7906
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 62
OS WAIT ARRAY INFO: signal count 59
RW-shared spins 0, rounds 63, OS waits 31
RW-excl spins 0, rounds 0, OS waits 0
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 63.00 RW-shared, 0.00 RW-excl, 0.00 RW-sx
------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-08-25 20:06:33 0x82a8
*** (1) TRANSACTION:
TRANSACTION 5900, ACTIVE 32 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 2, OS thread handle 32748, query id 312 localhost 127.0.0.1 root update
/* ApplicationName=DataGrip 2021.1 */ insert into user_1 values (null, 15, 'cc')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:❌
RECORD LOCKS space id 33 page no 4 n bits 72 index idx_name of table `mysql`.`user_1` trx id 5900 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 2; hex 6565; asc ee;;
1: len 4; hex 80000002; asc ;;
*** (2) TRANSACTION:
TRANSACTION 5901, ACTIVE 28 sec inserting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 3, OS thread handle 33448, query id 325 localhost 127.0.0.1 root update
/* ApplicationName=DataGrip 2021.1 */ insert into user_1 values (null, 30, 'gg')
*** (2) HOLDS THE LOCK(S):✅
RECORD LOCKS space id 33 page no 4 n bits 72 index idx_name of table `mysql`.`user_1` trx id 5901 lock_mode X locks gap before rec
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 2; hex 6565; asc ee;;
1: len 4; hex 80000002; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:❎
RECORD LOCKS space id 33 page no 4 n bits 72 index idx_name of table `mysql`.`user_1` trx id 5901 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 (1)
------------
TRANSACTIONS
------------
Trx id counter 5907
Purge done for trx's n:o < 5907 undo n:o < 0 state: running but idle
History list length 5
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 284557950381568, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 284557950379824, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 5901, ACTIVE 31 sec
5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 3, OS thread handle 33448, query id 334 localhost 127.0.0.1 root
--------
FILE I/O
--------
I/O thread 0 state: wait Windows aio (insert buffer thread)
I/O thread 1 state: wait Windows aio (log thread)
I/O thread 2 state: wait Windows aio (read thread)
I/O thread 3 state: wait Windows aio (read thread)
I/O thread 4 state: wait Windows aio (read thread)
I/O thread 5 state: wait Windows aio (read thread)
I/O thread 6 state: wait Windows aio (write thread)
I/O thread 7 state: wait Windows aio (write thread)
I/O thread 8 state: wait Windows aio (write thread)
I/O thread 9 state: wait Windows aio (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
429 OS file reads, 198 OS file writes, 130 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.96 writes/s, 0.75 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
0.00 hash searches/s, 0.74 non-hash searches/s
---
LOG
---
Log sequence number 3401095
Log flushed up to 3401095
Pages flushed up to 3401095
Last checkpoint at 3400989
0 pending log flushes, 0 pending chkp writes
87 log i/o's done, 0.46 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137297920
Dictionary memory allocated 105651
Buffer pool size 8192
Free buffers 7765
Database pages 427
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 393, created 34, written 89
0.00 reads/s, 0.00 creates/s, 0.42 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 427, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=15540, Main thread ID=18340, state: sleeping
Number of rows inserted 2, updated 6, deleted 0, read 24
0.02 inserts/s, 0.04 updates/s, 0.00 deletes/s, 0.07 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-08-25 20:06:33 0x82a8
*** (1) TRANSACTION:
-- **事务5900,在执行insert操作,活跃了32秒**
TRANSACTION 5900, ACTIVE 32 sec inserting
-- **该事务使用一个mysql表,有一个锁**
mysql tables in use 1, locked 1
-- **当前有4行记录被锁**
LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
-- **当前事务的线程id、查询id和数据库ip及账号**
MySQL thread id 2, OS thread handle 32748, query id 312 localhost 127.0.0.1 root update
-- **当前事务发生死锁时的执行语句**
/* ApplicationName=DataGrip 2021.1 */ insert into user_1 values (null, 15, 'cc')
-- **当前在等待的锁**
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
-- **索引idx_name的插入意向间隙锁在等待。等待的锁位置是“heap no 3”**
RECORD LOCKS space id 33 page no 4 n bits 72 index idx_name of table `mysql`.`user_1` trx id 5900 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
-- **等待锁的范围是(ee,未知)**
0: len 2; hex 6565; asc ee;;
1: len 4; hex 80000002; asc ;;
*** (2) TRANSACTION:
**事务5900,在执行insert操作,活跃了32秒**
TRANSACTION 5901, ACTIVE 28 sec inserting
-- **该事务使用一个mysql表,有一个锁**
mysql tables in use 1, locked 1
-- **当前有4行记录被锁**
5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
-- **当前事务的线程id、查询id和数据库ip及账号**
MySQL thread id 3, OS thread handle 33448, query id 325 localhost 127.0.0.1 root update
-- **当前事务发生死锁时的执行语句**
/* ApplicationName=DataGrip 2021.1 */ insert into user_1 values (null, 30, 'gg')
-- **当前事务持有的锁**
*** (2) HOLDS THE LOCK(S):
-- **持有的锁是索引idx_name的间隙排他锁。持有的锁位置是“heap no 3”**
RECORD LOCKS space id 33 page no 4 n bits 72 index idx_name of table `mysql`.`user_1` trx id 5901 lock_mode X locks gap before rec
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
-- **持有锁的范围是(ee,未知)**
0: len 2; hex 6565; asc ee;;
1: len 4; hex 80000002; asc ;;
-- **等待的锁**
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
-- **索引idx_name的插入意向间隙锁在等待。等待的锁位置是“heap no 1”**
RECORD LOCKS space id 33 page no 4 n bits 72 index idx_name of table `mysql`.`user_1` trx id 5901 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 (1)
-- 查看数据库版本
select version();
-- 查看默认值
show variables like '%engine%';
-- 查看事务隔离级别
show global variables like 'tx_isolation';
-- 查看是否开启自动提交
show variables like 'autocommit';
-- 查看是否开启间隙锁
show variables like 'innodb_locks_unsafe_for_binlog';
-- 查看锁等待的对应关系
select * from information_schema.INNODB_LOCK_WAITS;
-- 查看innodb当前的事务和锁
select * from information_schema.innodb_trx;
mysql5.6版本后,可以通过下面的命令来进行锁的状态监控
set global innodb_status_output=on;
set global innodb_status_output_locks=on;
-- 开启锁的状态监控后,查看详细的innodeb信息
show engine innodb status;
-- 1、查看是否开启间隙锁:show variables like 'innodb_locks_unsafe_for_binlog';
mysql> show variables like 'innodb_locks_unsafe_for_binlog';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_locks_unsafe_for_binlog | OFF |
+--------------------------------+-------+
-- 注意:默认值为OFF(0),即启用gap lock。
-- 这个参数最主要的作用就是控制innodb是否对gap加锁。
-- 但是,这一设置变更并不影响外键和唯一索引(含主键)对gap进行加锁的需要。
-- 2、关闭间隙锁(gap lock)方法:
-- 在my.cnf里面的[mysqld]添加:
innodb_locks_unsafe_for_binlog = 1
-- 重启MySQL后生效。
Field | Extra |
---|---|
trx_id | 事务ID |
trx_state | 事务状态 |
trx_started | 事务开始时间 |
trx_requested_lock_id | innodb_locks.lock_id |
trx_wait_started | 事务开始等待的时间 |
trx_weight | 事务权重 |
trx_mysql_thread_id | 事务线程ID |
trx_query | 具体SQL语句 |
trx_operation_state | 事务当前操作状态 |
trx_tables_in_use | 事务中有多少个表被使用 |
trx_tables_locked | 事务拥有多少个锁 |
trx_lock_structs | |
trx_lock_memory_bytes | 事务锁住的内存大小(B) |
trx_rows_locked | 事务锁住的行数 |
trx_rows_modified | 事务更改的行数 |
trx_concurrency_tickets | 事务并发票数 |
trx_isolation_level | 事务隔离级别 |
trx_unique_checks | 是否唯一性检查 |
trx_foreign_key_checks | 是否外键检查 |
trx_last_foreign_key_error | 最后的外键错误 |
trx_adaptive_hash_latched | |
trx_adaptive_hash_timeout |
Field | Extra |
---|---|
lock_id | 锁ID |
lock_trx_id | 拥有锁的事务ID |
lock_mode | 锁模式 |
lock_type | 锁类型 |
lock_table | 被锁的表 |
lock_index | 被锁的索引 |
lock_space | 被锁的表空间号 |
lock_page | 被锁的页号 |
lock_rec | 被锁的记录号 |
lock_data | 被锁的数据 |
Field | Extra |
---|---|
requesting_trx_id | 请求锁的事务ID |
requested_lock_id | 请求锁的锁ID |
blocking_trx_id | 当前拥有锁的事务ID |
blocking_lock_id | 当前拥有锁的锁ID |