MySQL 5.7.26
表 a 中的字段 b_id 外键关联 表 b 中的 id
create table a
(
id int auto_increment
primary key,
name varchar(64) null,
age int null,
b_id int null,
constraint a_b_id_fk
foreign key (b_id) references b (id)
);
[
{
"id": 2,
"name": "hsh",
"age": 24,
"b_id": 3
}
]
create table b
(
id int auto_increment
primary key,
name varchar(64) null,
time bigint null
);
[
{
"id": 3,
"name": "h",
"time": 330
}
]
通过主、外键更新数据来产生排它锁,阻塞和死锁。
步骤 |
事务1 id=56389 |
事务2 id=56390 |
1 |
update a set name='hsh_1' where id = 2; |
|
2 |
update a set age=19 where b_id = 3; |
|
3 |
update a set name='hsh_2' where b_id = 3; |
当执行完第2步时,事务2阻塞,此时事务与锁的状态为:
select *
from information_schema.INNODB_TRX trx
left join information_schema.INNODB_LOCKS locks on trx.trx_id = locks.lock_trx_id;
[
{
"trx_id": "56389",
"trx_state": "RUNNING",
"trx_started": "2022-08-20 16:07:43",
"trx_requested_lock_id": null,
"trx_wait_started": null,
"trx_weight": 3,
"trx_mysql_thread_id": 12,
"trx_query": null,
"trx_operation_state": null,
"trx_tables_in_use": 0,
"trx_tables_locked": 1,
"trx_lock_structs": 2,
"trx_lock_memory_bytes": 1136,
"trx_rows_locked": 1,
"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,
"lock_id": "56389:227:3:9",
"lock_trx_id": "56389",
"lock_mode": "X",
"lock_type": "RECORD",
"lock_table": "`dps`.`a`",
"lock_index": "PRIMARY",
"lock_space": 227,
"lock_page": 3,
"lock_rec": 9,
"lock_data": "2"
},
{
"trx_id": "56390",
"trx_state": "LOCK WAIT",
"trx_started": "2022-08-20 16:07:55",
"trx_requested_lock_id": "56390:227:3:9",
"trx_wait_started": "2022-08-20 16:07:55",
"trx_weight": 3,
"trx_mysql_thread_id": 13,
"trx_query": "/* ApplicationName=DataGrip 2022.2 */ update a set age=19 where b_id=3",
"trx_operation_state": "starting index read",
"trx_tables_in_use": 1,
"trx_tables_locked": 1,
"trx_lock_structs": 3,
"trx_lock_memory_bytes": 1136,
"trx_rows_locked": 2,
"trx_rows_modified": 0,
"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,
"lock_id": "56390:227:3:9",
"lock_trx_id": "56390",
"lock_mode": "X",
"lock_type": "RECORD",
"lock_table": "`dps`.`a`",
"lock_index": "PRIMARY",
"lock_space": 227,
"lock_page": 3,
"lock_rec": 9,
"lock_data": "2"
}
]
select * from information_schema.INNODB_LOCK_WAITS;
[
{
"requesting_trx_id": "56390",
"requested_lock_id": "56390:227:3:9",
"blocking_trx_id": "56389",
"blocking_lock_id": "56389:227:3:9"
}
]
可以看出事务2当前处于锁等待状态,也就是阻塞状态。事务2当前正在请求的锁id为 56390:227:3:9。
事务1对应的锁ID为56389:227:3:9,这两个其实是同一把锁,都是添加在 表空间为227页号为3行记录是9的行上,是加在主键索引上。
再执行步骤3,产生死锁,事务2被kill掉。
[40001][1213] Deadlock found when trying to get lock; try restarting transaction。
死锁日志会在innodb status中记录。
show engine innodb status;
此语句会展示innodb监控信息,包含事务,文件IO,死锁,线程,内存和缓冲池等信息。
INNODB MONITOR OUTPUT
Per second averages calculated from the last 20 seconds
BACKGROUND THREAD
SEMAPHORES
LATEST DETECTED DEADLOCK
TRANSACTIONS
FILE I/O
INSERT BUFFER AND ADAPTIVE HASH INDEX
LOG
BUFFER POOL AND MEMORY
ROW OPERATIONS
下面只展示死锁相关信息:
LATEST DETECTED DEADLOCK
------------------------
2022-08-20 16:08:43 0x17e0
*** (1) TRANSACTION:
TRANSACTION 56390, ACTIVE 48 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 13, OS thread handle 3788, query id 1277 BOGON 192.168.0.101 root updating
/* ApplicationName=DataGrip 2022.2 */ update a set age=19 where b_id=3
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 227 page no 3 n bits 80 index PRIMARY of table `dps`.`a` trx id 56390 lock_mode X locks rec but not gap waiting
Record lock, heap no 9 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00000000dc45; asc E;;
2: len 7; hex 300000017018b8; asc 0 p ;;
3: len 5; hex 6873685f31; asc hsh_1;;
4: len 4; hex 80000018; asc ;;
5: len 4; hex 80000003; asc ;;
*** (2) TRANSACTION:
TRANSACTION 56389, ACTIVE 60 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 12, OS thread handle 6112, query id 1321 BOGON 192.168.0.101 root updating
/* ApplicationName=DataGrip 2022.2 */ update a set name='hsh_2' where b_id=3
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 227 page no 3 n bits 80 index PRIMARY of table `dps`.`a` trx id 56389 lock_mode X locks rec but not gap
Record lock, heap no 9 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000002; asc ;;
1: len 6; hex 00000000dc45; asc E;;
2: len 7; hex 300000017018b8; asc 0 p ;;
3: len 5; hex 6873685f31; asc hsh_1;;
4: len 4; hex 80000018; asc ;;
5: len 4; hex 80000003; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 227 page no 4 n bits 72 index a_b_id_fk of table `dps`.`a` trx id 56389 lock_mode X waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000003; asc ;;
1: len 4; hex 80000002; asc ;;
*** WE ROLL BACK TRANSACTION (1)
当事务2执行更新时,时通过外键更新的,所以不仅会请求 227:3:9 这把锁,同时也会在 227:4:6 这个外键索引上加锁。
当事务1再通过外键更新时,就请求 227:4:6 这把外键锁,此时事务2持有这把锁,由于事务1持有事务2请求的锁,
所以事务2无法释放持有的外键锁,死锁就会产生。这是MySQL检测到了死锁,就会kill掉事务2。
按如下语句执行事务:
步骤 |
事务1 id=56395 |
事务2 id=56396 |
1 |
update a set name='hsh_2' where b_id = 3; |
|
2 |
update a set age=19 where b_id = 3; |
[
{
"trx_id": "56395",
"trx_state": "RUNNING",
"trx_started": "2022-08-20 17:05:16",
"trx_requested_lock_id": null,
"trx_wait_started": null,
"trx_weight": 4,
"trx_mysql_thread_id": 12,
"trx_query": null,
"trx_operation_state": null,
"trx_tables_in_use": 0,
"trx_tables_locked": 1,
"trx_lock_structs": 3,
"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,
"lock_id": "56395:227:4:6",
"lock_trx_id": "56395",
"lock_mode": "X",
"lock_type": "RECORD",
"lock_table": "`dps`.`a`",
"lock_index": "a_b_id_fk",
"lock_space": 227,
"lock_page": 4,
"lock_rec": 6,
"lock_data": "3, 2"
},
{
"trx_id": "56396",
"trx_state": "LOCK WAIT",
"trx_started": "2022-08-20 17:05:33",
"trx_requested_lock_id": "56396:227:4:6",
"trx_wait_started": "2022-08-20 17:05:33",
"trx_weight": 2,
"trx_mysql_thread_id": 13,
"trx_query": "/* ApplicationName=DataGrip 2022.2 */ update a\nset age=19\nwhere b_id = 3",
"trx_operation_state": "starting index read",
"trx_tables_in_use": 1,
"trx_tables_locked": 1,
"trx_lock_structs": 2,
"trx_lock_memory_bytes": 1136,
"trx_rows_locked": 1,
"trx_rows_modified": 0,
"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,
"lock_id": "56396:227:4:6",
"lock_trx_id": "56396",
"lock_mode": "X",
"lock_type": "RECORD",
"lock_table": "`dps`.`a`",
"lock_index": "a_b_id_fk",
"lock_space": 227,
"lock_page": 4,
"lock_rec": 6,
"lock_data": "3, 2"
}
]
从"lock_index": "a_b_id_fk" 可以看出此时的锁是外键锁,
锁的记录为 227:4:6 上的外键锁。
事务与锁 表中的具体字段说明,参见
information_schema.INNODB_TRX表详解_CaptHua的博客-CSDN博客_information_schema.innodb_trx
INNODB_LOCKS与INNODB_LOCK_WAITS表_CaptHua的博客-CSDN博客