闽公网安备 35020302035485号
------------------------
LATEST DETECTED DEADLOCK
------------------------
2023-12-11 17:55:14 7f6de3c0a700
*** (1) TRANSACTION:
TRANSACTION 67324219989, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1184, 4 row lock(s), undo log entries 2
MySQL thread id 79690810, OS thread handle 0x7f6de0b4a700, query id 200590503593 x.x.x.x tms_boss_rw update
INSERT INTO delivery_packing_package_info (waybill_code, packing_code, packing_name, packing_type, packing_type_name,
packing_volume, volume_coefficient, packing_specification, packing_unit, packing_number,init_packing_number,
packing_charge,package_code,remark,
real_packing_number,
package_auto_match_flag,
create_user_code,
create_user_name,
create_time,
update_time,
yn)
VALUES
('xxxx00590163185', ...
'2023-12-11 17:55:13.622',
'2023-12-11 17:55:13.622',
1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 341 page no 6195 n bits 480 index `idx_waybill_code` of table `tms_boss`.`delivery_packing_package_info` trx id 67324219989 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 386 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 15; hex 4a444b413030353930313639383937; asc xxxx00590169897;;
1: len 8; hex 800000000005d001; asc ;;
*** (2) TRANSACTION:
TRANSACTION 67324219985, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 1
MySQL thread id 79691432, OS thread handle 0x7f6de3c0a700, query id 200590503599 x.x.x.x tms_boss_rw update
INSERT INTO delivery_packing_package_info (waybill_code, packing_code, packing_name, packing_type, packing_type_name,
packing_volume, volume_coefficient, packing_specification, packing_unit, packing_number,init_packing_number,
packing_charge,package_code,remark,
real_packing_number,
package_auto_match_flag,
create_user_code,
create_user_name,
create_time,
update_time,
yn)
VALUES
('xxxx00590163967', ...
'2023-12-11 17:55:13.822',
'2023-12-11 17:55:13.822',
1)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 341 page no 6195 n bits 480 index `idx_waybill_code` of table `tms_boss`.`delivery_packing_package_info` trx id 67324219985 lock_mode X locks gap before rec
Record lock, heap no 386 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 15; hex 4a444b413030353930313639383937; asc xxxx00590169897;;
1: len 8; hex 800000000005d001; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 341 page no 6195 n bits 480 index `idx_waybill_code` of table `tms_boss`.`delivery_packing_package_info` trx id 67324219985 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 386 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 15; hex 4a444b413030353930313639383937; asc xxxx00590169897;;
1: len 8; hex 800000000005d001; asc ;;
*** WE ROLL BACK TRANSACTION (2)
其中:mysql> show create table `tms_boss`.`delivery_packing_package_info` \G
*************************** 1. row ***************************
Table: delivery_packing_package_info
Create Table: CREATE TABLE `delivery_packing_package_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`waybill_code` varchar(32) DEFAULT NULL COMMENT '运单号',
`packing_code` varchar(32) DEFAULT NULL COMMENT '包装编号',
`packing_name` varchar(32) DEFAULT NULL COMMENT '包装名称',
`packing_type` varchar(32) DEFAULT NULL COMMENT '包装耗材类型',
`packing_type_name` varchar(32) DEFAULT NULL COMMENT '包装耗材类型名称',
`packing_volume` double DEFAULT NULL COMMENT '包装耗材体积',
`volume_coefficient` double DEFAULT NULL COMMENT '包装耗材体积系数',
`packing_specification` varchar(32) DEFAULT NULL COMMENT '包装规格',
`packing_unit` varchar(32) DEFAULT NULL COMMENT '包装耗材单位',
`packing_number` double DEFAULT NULL COMMENT '包装耗材数量',
`init_packing_number` double DEFAULT NULL COMMENT '包装耗材录入数量',
`real_packing_number` int(11) DEFAULT NULL COMMENT '真实耗材数',
`packing_charge` double DEFAULT NULL COMMENT '包装耗材单个价格',
`package_code` varchar(32) DEFAULT NULL COMMENT '包裹号/包裹编号',
`remark` varchar(3000) DEFAULT NULL COMMENT '合打/单打备注',
`package_auto_match_flag` int(11) DEFAULT NULL COMMENT '包裹号自动匹配flag。1-支持自动匹配 2-只支持扫描',
`create_user_code` varchar(32) DEFAULT NULL COMMENT '创建人账号',
`create_user_name` varchar(32) DEFAULT NULL COMMENT '创建人姓名',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`yn` int(11) DEFAULT NULL COMMENT '可用状态: 1 有效, 0无效',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_waybill_code` (`waybill_code`) USING BTREE,
KEY `idx_create_time` (`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=383557 DEFAULT CHARSET=utf8 COMMENT='包裹维度包装耗材信息表'
1 row in set (0.01 sec)
其中:#231211 17:55:14 server id 1683069784 end_log_pos 307231023 CRC32 0x1f48ba3e GTID last_committed=0 sequence_number=0 rbr_only=no
SET @@SESSION.GTID_NEXT= 'da4da38e-d71b-11ec-ad8b-fa163ed04e5b:12597525219'/*!*/;
# at 307231023
#231211 17:55:14 server id 1683069784 end_log_pos 307231086 CRC32 0xcfeee4b9 Query thread_id=79690810 exec_time=0 error_code=0
SET TIMESTAMP=1702288514/*!*/;
SET @@session.pseudo_thread_id=79690810/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=524288/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 307231086
#231211 17:55:14 server id 1683069784 end_log_pos 307231264 CRC32 0xe9813982 Rows_query
# UPDATE delivery_packing_package_info
# SET update_time = '2023-12-11 17:55:14.313', yn = 0
# WHERE waybill_code = 'xxxx00590163185' AND yn = 1
# at 307231264
#231211 17:55:14 server id 1683069784 end_log_pos 307232524 CRC32 0x0cab5bb1 Rows_query
# INSERT INTO delivery_packing_package_info (waybill_code, packing_code, packing_name, packing_type, packing_type_name,
# packing_volume, volume_coefficient, packing_specification, packing_unit, packing_number,init_packing_number,
# packing_charge,package_code,remark,
# real_packing_number,
# package_auto_match_flag,
# create_user_code,
# create_user_name,
# create_time,
# update_time,
# yn)
# VALUES
#
# ('xxxx00590163185', ...
# '2023-12-11 17:55:13.622',
# '2023-12-11 17:55:13.622',
# 1)
# at 307232524
其中:mysql> select id,waybill_code,yn,create_time from `tms_boss`.`delivery_packing_package_info` where waybill_code<='xxxx00590169897' order by waybill_code desc limit 8; +--------+-----------------+------+---------------------+ | id | waybill_code | yn | create_time | +--------+-----------------+------+---------------------+ | 380934 | xxxx00590169897 | 1 | 2023-12-11 17:50:56 | | 380933 | xxxx00590169897 | 1 | 2023-12-11 17:50:56 | | 380930 | xxxx00590169897 | 0 | 2023-12-11 17:50:47 | | 380929 | xxxx00590169897 | 0 | 2023-12-11 17:50:47 | <-- | 382335 | xxxx00590166721 | 1 | 2023-12-12 11:14:18 | | 380963 | xxxx00590163185 | 1 | 2023-12-11 17:55:14 | | 380944 | xxxx00590163185 | 0 | 2023-12-11 17:52:41 | <-- | 382673 | xxxx00590162878 | 1 | 2023-12-12 12:47:19 | +--------+-----------------+------+---------------------+ 8 rows in set (0.00 sec)其中:
mysql> select count(*) from `tms_boss`.`delivery_packing_package_info` where waybill_code='xxxx00590163967'; +----------+ | count(*) | +----------+ | 0 | +----------+ 1 row in set (0.00 sec)其中:
mysql> show create table tb \G
*************************** 1. row ***************************
Table: tb
Create Table: CREATE TABLE `tb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a` int(11) NOT NULL DEFAULT '0',
`b` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_a` (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> insert into tb(id,a,b) values(1,1,1),(5,5,5),(9,9,9);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from tb;
+----+---+---+
| id | a | b |
+----+---+---+
| 1 | 1 | 1 |
| 5 | 5 | 5 |
| 9 | 9 | 9 |
+----+---+---+
3 rows in set (0.00 sec)
其中:主键索引 + 非唯一二级索引| session 1 | session 2 |
|---|---|
| begin; |
|
|
|
begin; |
| update tb set b=4 where a=5; |
|
|
|
update tb set b=5 where a=6; |
|
insert into tb(a,b) values(5,5); Blocked |
|
|
|
insert into tb(a,b) values(6,6); Deadlock found |
死锁复现
------------------------ LATEST DETECTED DEADLOCK ------------------------ 2023-12-12 17:14:16 0x7fdeb5059700 *** (1) TRANSACTION: TRANSACTION 132925, ACTIVE 19 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2 MySQL thread id 70, OS thread handle 140594496775936, query id 9070 127.0.0.1 admin update insert into tb(a,b) values(5,5) *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132925 lock_mode X locks gap before rec insert intention waiting Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000009; asc ;; 1: len 4; hex 80000009; asc ;; *** (2) TRANSACTION: TRANSACTION 132926, ACTIVE 11 sec inserting 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 71, OS thread handle 140594496509696, query id 9071 127.0.0.1 admin update insert into tb(a,b) values(6,6) *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132926 lock_mode X locks gap before rec Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000009; asc ;; 1: len 4; hex 80000009; asc ;; *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132926 lock_mode X locks gap before rec insert intention waiting Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000009; asc ;; 1: len 4; hex 80000009; asc ;; *** WE ROLL BACK TRANSACTION (2)其中:
mysql> insert into tb(id,a,b) values(6,6,6); Query OK, 1 row affected (0.00 sec) mysql> select * from tb; +----+---+---+ | id | a | b | +----+---+---+ | 1 | 1 | 1 | | 5 | 5 | 5 | | 6 | 6 | 6 | | 9 | 9 | 9 | +----+---+---+ 4 rows in set (0.00 sec)操作流程见下表,其中两个事务均更新存在的记录。
| session 1 | session 2 |
|---|---|
| begin; |
|
|
|
begin; |
| update tb set b=4 where a=5; |
|
|
|
update tb set b=5 where a=6; |
|
insert into tb(a,b) values(5,5); Blocked |
|
|
|
insert into tb(a,b) values(6,6); Query OK, 1 row affected (0.00 sec) |
---TRANSACTION 132938, ACTIVE 16 sec 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2 MySQL thread id 71, OS thread handle 140594496509696, query id 9092 127.0.0.1 admin TABLE LOCK table `test_zk`.`tb` trx id 132938 lock mode IX RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132938 lock_mode X Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000006; asc ;; 1: len 4; hex 80000006; asc ;; RECORD LOCKS space id 480 page no 3 n bits 80 index PRIMARY of table `test_zk`.`tb` trx id 132938 lock_mode X locks rec but not gap Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 0: len 4; hex 80000006; asc ;; 1: len 6; hex 00000002074a; asc J;; 2: len 7; hex 7000000024030e; asc p $ ;; 3: len 4; hex 80000006; asc ;; 4: len 4; hex 80000005; asc ;; RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132938 lock_mode X locks gap before rec Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000009; asc ;; 1: len 4; hex 80000009; asc ;; Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000006; asc ;; 1: len 4; hex 80000014; asc ;; ---TRANSACTION 132937, ACTIVE 25 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2 MySQL thread id 70, OS thread handle 140594496775936, query id 9091 127.0.0.1 admin update insert into tb(a,b) values(5,5) Trx read view will not see trx with id >= 132937, sees < 132937 ------- TRX HAS BEEN WAITING 9 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132937 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 80000006; asc ;; 1: len 4; hex 80000006; asc ;; ------------------ TABLE LOCK table `test_zk`.`tb` trx id 132937 lock mode IX RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132937 lock_mode X Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000005; asc ;; 1: len 4; hex 80000005; asc ;; RECORD LOCKS space id 480 page no 3 n bits 80 index PRIMARY of table `test_zk`.`tb` trx id 132937 lock_mode X locks rec but not gap Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 0: len 4; hex 80000005; asc ;; 1: len 6; hex 000000020749; asc I;; 2: len 7; hex 6f000000292dc9; asc o )- ;; 3: len 4; hex 80000005; asc ;; 4: len 4; hex 80000004; asc ;; RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132937 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 80000006; asc ;; 1: len 4; hex 80000006; asc ;; RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132937 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 80000006; asc ;; 1: len 4; hex 80000006; asc ;;其中:
update tb set b=5 where a=6; insert into tb(a,b) values(6,6);给函数lock_rec_add_to_queue设置断点,执行 insert 语句时查看堆栈。

static
void
lock_rec_inherit_to_gap_if_gap_lock(
/*================================*/
const buf_block_t* block, /*!< in: buffer block */
ulint heir_heap_no, /*!< in: heap_no of
record which inherits */
ulint heap_no) /*!< in: heap_no of record
from which inherited;
does NOT reset the locks
on this record */
{
lock_t* lock;
...
// 堆代码 duidaima.com
// 遍历记录上的所有锁
for (lock = lock_rec_get_first(lock_sys->rec_hash, block, heap_no);
lock != NULL;
lock = lock_rec_get_next(heap_no, lock)) {
// 如果不是插入意向锁
if (!lock_rec_get_insert_intention(lock)
&& (heap_no == PAGE_HEAP_NO_SUPREMUM
// 是LOCK_GAP或者NEXT-KEY LOCK(没有设置LOCK_REC_NOT_GAP标记)
|| !lock_rec_get_rec_not_gap(lock))) {
// 给事务增加一个新的锁对象,锁的类型为LOCK_REC | LOCK_GAP
// 所有符合条件的会话都继承了这个新的GAP,避免之前的GAP锁失效
lock_rec_add_to_queue(
LOCK_REC | LOCK_GAP | lock_get_mode(lock),
block, heir_heap_no, lock->index,
lock->trx, FALSE);
}
}
...
}
其中:/*************************************************************//**
Updates the lock table when a new user record is inserted. */
void
lock_update_insert(
/*===============*/
const buf_block_t* block, /*!< in: buffer block containing rec */
const rec_t* rec) /*!< in: the inserted record */
{
ulint receiver_heap_no;
ulint donator_heap_no;
// block->frame 中保存数据页的真实内容
ut_ad(block->frame == page_align(rec));
/* Inherit the gap-locking locks for rec, in gap mode, from the next
record */
// 获取 heap_no
...
receiver_heap_no = rec_get_heap_no_new(rec);
donator_heap_no = rec_get_heap_no_new(
page_rec_get_next_low(rec, TRUE));
...
// heir_heap_no=receiver_heap_no,heap_no=donator_heap_no
lock_rec_inherit_to_gap_if_gap_lock(
block, receiver_heap_no, donator_heap_no);
}
其中: if (!(flags & BTR_NO_LOCKING_FLAG) && inherit) {
lock_update_insert(block, *rec);
}
其中:// 通过 cursor 获取 block block = btr_cur_get_block(cursor); // buf_block_t::frame 中保存 page page = buf_block_get_frame(block); index = cursor->index;inherit 变量在初始化后修改。
ibool inherit = TRUE;
err = lock_rec_insert_check_and_lock(
flags, rec, btr_cur_get_block(cursor),
index, thr, mtr, inherit);
其中:dberr_t
lock_rec_insert_check_and_lock(...){
...
ibool inherit_in = *inherit;
trx_t* trx = thr_get_trx(thr);
// 获取下一条记录
const rec_t* next_rec = page_rec_get_next_const(rec);
ulint heap_no = page_rec_get_heap_no(next_rec);
// 判断下一条记录上是否有锁
lock = lock_rec_get_first(lock_sys->rec_hash, block, heap_no);
// lock_rec_get_first返回 NULL 表示下一个记录上没有锁,因此使用隐式锁
if (lock == NULL) {
if (inherit_in && !dict_index_is_clust(index)) {
/* Update the page max trx id field */
// 直接更新二级索引trx id
// 更新页的最大事务ID
page_update_max_trx_id(block,
buf_block_get_page_zip(block),
trx->id, mtr);
}
// 不需要锁分裂
*inherit = FALSE;
return(DB_SUCCESS);
}
// 可能需要锁分裂
*inherit = TRUE;
/* If another transaction has an explicit lock request which locks
the gap, waiting or granted, on the successor, the insert has to wait.*/
// 下一条记录上有锁,然后判断是否有锁冲突
// 插入意向锁
const ulint type_mode = LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION;
// 判断是否与插入意向锁冲突,也就是检查是否有间隙锁
const lock_t* wait_for = lock_rec_other_has_conflicting(
type_mode, block, heap_no, trx);
// 有冲突时进入锁等待
if (wait_for != NULL) {
RecLock rec_lock(thr, index, block, heap_no, type_mode);
// 如果与插入意向锁有冲突,创建一个插入意向锁,加到事务锁列表中去,插入等待队列中
err = rec_lock.add_to_waitq(wait_for);
} else {
err = DB_SUCCESS;
}
switch (err) {
case DB_SUCCESS_LOCKED_REC:
err = DB_SUCCESS;
/* fall through */
case DB_SUCCESS:
if (!inherit_in || dict_index_is_clust(index)) {
break;
}
/* Update the page max trx id field */
// 如果没有冲突,直接更新页的最大事务ID,然后返回成功
page_update_max_trx_id(
block, buf_block_get_page_zip(block), trx->id, mtr);
default:
/* We only care about the two return values. */
break;
}
...
}
其中:同一个事务持有 gap lock 的前提下插入数据,会发生锁分裂,如果是其他事务持有 gap lock,会发生插入意向锁等待。
RECORD LOCKS space id 480 page no 4 n bits 72 index idx_a of table `test_zk`.`tb` trx id 132938 lock_mode X locks gap before rec Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000009; asc ;; 1: len 4; hex 80000009; asc ;; Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000006; asc ;; 1: len 4; hex 80000014; asc ;;测试过程中数据插入的顺序如下所示。
insert into tb(id,a,b) values(1,1,1),(5,5,5),(9,9,9); insert into tb(id,a,b) values(6,6,6); insert into tb(a,b) values(6,6); // (id,a,b)=(14,6,6)idx_a 索引各记录的 heap_no 值如下所示。
infimum:0 supremum:1 a = 1:2(id = 1) a = 5:3(id = 5) a = 9:4(id = 9) a = 6:5(id = 6) a = 6:6(id = 14)显然 heap_no 的顺序与索引值的顺序不同,那么什么是 heap 和 heap_no?
参考《MySQL 运维内参》:
heap_no 保存在 Record Header 中,占用 13 比特,表示当前记录在页面堆中的相对位置。


heap_no 尽管只是一个逻辑顺序编号,但是每条记录按照主键从小到大的顺序组成一个有序单向链表。