InnoDB死锁分析-案例4-行锁升级next key lock

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sun_ashe/article/details/85220559

一、死锁日志

LATEST DETECTED DEADLOCK
------------------------
2018-12-21 13:34:32 0x7fc92c3be700
*** (1) TRANSACTION:
TRANSACTION 1862, ACTIVE 6 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 172, OS thread handle 140502007543552, query id 184 localhost root statistics
select           ac_no,         usr_no,         cap_typ,         ccy,         ac_typ,         ac_org,         cap_typ_sts,         cre_dt,         ord_seq,         last_ac_bal,         cur_ac_bal,         last_uava_bal,         uava_bal,         od_lmt,         max_bal_lmt,         min_bal_lmt,         bal_tag,         upd_dt,         upd_jrn,         nod_id,         req_id,         tm_smp               from t_acm_ecbl         where  ac_org = '100001' and ac_no = '1200020000000501107' and cap_typ = '1' and ccy = 'CNY' for update
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 28 page no 745 n bits 128 index PRIMARY of table `t2`.`t_acm_ecbl` trx id 1862 lock_mode X locks rec but not gap waiting
Record lock, heap no 37 PHYSICAL RECORD: n_fields 24; compact format; info bits 0
 0: len 19; hex 31323030303230303030303030353031313037; asc 1200020000000501107;;
 1: len 1; hex 31; asc 1;;
 2: len 3; hex 434e59; asc CNY;;
 3: len 6; hex 313030303031; asc 100001;;
 4: len 6; hex 00000000073b; asc      ;;;
 5: len 7; hex 36000001200110; asc 6      ;;
 6: len 9; hex 800000000000000000; asc          ;;
 7: len 3; hex 313230; asc 120;;
 8: len 1; hex 30; asc 0;;
 9: len 8; hex 3230313830393131; asc 20180911;;
 10: len 10; hex 80000000000000000014; asc           ;;
 11: len 9; hex 80000000000000cb00; asc          ;;
 12: len 9; hex 800000000000009905; asc          ;;
 13: len 9; hex 800000000000000000; asc          ;;
 14: len 9; hex 800000000000000000; asc          ;;
 15: len 9; hex 800000000000000000; asc          ;;
 16: len 9; hex 800000000000000000; asc          ;;
 17: len 9; hex 800000000000000000; asc          ;;
 18: len 30; hex 316437386665623265376630343434373735666235396638343937313935; asc 1d78feb2e7f0444775fb59f8497195; (total 32 bytes);
 19: len 8; hex 3230313831323134; asc 20181214;;
 20: len 18; hex 323031383132313430303030353938393937; asc 201812140000598997;;
 21: SQL NULL;
 22: len 18; hex 323031383039313130303030333531363138; asc 201809110000351618;;
 23: len 13; hex 31353434373137383833373035; asc 1544717883705;;

*** (2) TRANSACTION:
TRANSACTION 1861, ACTIVE 10 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 167, OS thread handle 140502007277312, query id 185 localhost root updating
UPDATE t_acm_ecbl
          SET ORD_SEQ = 20
             ,LAST_AC_BAL = 203.00
             ,CUR_AC_BAL=153.05
             ,BAL_TAG='1d78feb2e7f0444775fb59f8497195c5'
             ,UPD_DT='20181214'
             ,UPD_JRN='201812140000598997'
             ,TM_SMP='1544717883705'
        WHERE AC_NO = '1200020000000501107'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 28 page no 745 n bits 128 index PRIMARY of table `t2`.`t_acm_ecbl` trx id 1861 lock_mode X locks rec but not gap
Record lock, heap no 37 PHYSICAL RECORD: n_fields 24; compact format; info bits 0
 0: len 19; hex 31323030303230303030303030353031313037; asc 1200020000000501107;;
 1: len 1; hex 31; asc 1;;
 2: len 3; hex 434e59; asc CNY;;
 3: len 6; hex 313030303031; asc 100001;;
 4: len 6; hex 00000000073b; asc      ;;;
 5: len 7; hex 36000001200110; asc 6      ;;
 6: len 9; hex 800000000000000000; asc          ;;
 7: len 3; hex 313230; asc 120;;
 8: len 1; hex 30; asc 0;;
 9: len 8; hex 3230313830393131; asc 20180911;;
 10: len 10; hex 80000000000000000014; asc           ;;
 11: len 9; hex 80000000000000cb00; asc          ;;
 12: len 9; hex 800000000000009905; asc          ;;
 13: len 9; hex 800000000000000000; asc          ;;
 14: len 9; hex 800000000000000000; asc          ;;
 15: len 9; hex 800000000000000000; asc          ;;
 16: len 9; hex 800000000000000000; asc          ;;
 17: len 9; hex 800000000000000000; asc          ;;
 18: len 30; hex 316437386665623265376630343434373735666235396638343937313935; asc 1d78feb2e7f0444775fb59f8497195; (total 32 bytes);
 19: len 8; hex 3230313831323134; asc 20181214;;
 20: len 18; hex 323031383132313430303030353938393937; asc 201812140000598997;;
 21: SQL NULL;
 22: len 18; hex 323031383039313130303030333531363138; asc 201809110000351618;;
 23: len 13; hex 31353434373137383833373035; asc 1544717883705;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 28 page no 745 n bits 128 index PRIMARY of table `t2`.`t_acm_ecbl` trx id 1861 lock_mode X waiting
Record lock, heap no 37 PHYSICAL RECORD: n_fields 24; compact format; info bits 0
 0: len 19; hex 31323030303230303030303030353031313037; asc 1200020000000501107;;
 1: len 1; hex 31; asc 1;;
 2: len 3; hex 434e59; asc CNY;;
 3: len 6; hex 313030303031; asc 100001;;
 4: len 6; hex 00000000073b; asc      ;;;
 5: len 7; hex 36000001200110; asc 6      ;;
 6: len 9; hex 800000000000000000; asc          ;;
 7: len 3; hex 313230; asc 120;;
 8: len 1; hex 30; asc 0;;
 9: len 8; hex 3230313830393131; asc 20180911;;
 10: len 10; hex 80000000000000000014; asc           ;;
 11: len 9; hex 80000000000000cb00; asc          ;;
 12: len 9; hex 800000000000009905; asc          ;;
 13: len 9; hex 800000000000000000; asc          ;;
 14: len 9; hex 800000000000000000; asc          ;;
 15: len 9; hex 800000000000000000; asc          ;;
 16: len 9; hex 800000000000000000; asc          ;;
 17: len 9; hex 800000000000000000; asc          ;;
 18: len 30; hex 316437386665623265376630343434373735666235396638343937313935; asc 1d78feb2e7f0444775fb59f8497195; (total 32 bytes);
 19: len 8; hex 3230313831323134; asc 20181214;;
 20: len 18; hex 323031383132313430303030353938393937; asc 201812140000598997;;
 21: SQL NULL;
 22: len 18; hex 323031383039313130303030333531363138; asc 201809110000351618;;
 23: len 13; hex 31353434373137383833373035; asc 1544717883705;;

*** WE ROLL BACK TRANSACTION (1)

二、表结构

mysql> show create table t_acm_ecbl\G
*************************** 1. row ***************************
       Table: t_acm_ecbl
Create Table: CREATE TABLE `t_acm_ecbl` (
  `AC_NO` varchar(32) NOT NULLL,
  `USR_NO` decimal(20,0) DEFAULT NULL,
  `CAP_TYP` varchar(1) NOT NULL,
  `CCY` varchar(3) NOT NULL,
  `AC_TYP` varchar(3) NOT NULL ,
  `AC_ORG` varchar(10) NOT NULL ,
  `CAP_TYP_STS` varchar(1) NOT NULL DEFAULT ' ',
  PRIMARY KEY (`AC_NO`,`CAP_TYP`,`CCY`,`AC_ORG`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

联合主键 PRIMARY KEY (AC_NO,CAP_TYP,CCY,AC_ORG)

三、死锁分析

事务1862等待space id 28 page no 745 heap no 37这行数据的行锁,锁类型(lock_mode X locks rec but not gap waiting)

事务1861持有space id 28 page no 745 heap no 37这行数据的行锁,锁类型(lock_mode X locks rec but not gap)

事务1861等待 space id 28 page no 745 heap no 37 这行数据的行锁+gap锁(lock_mode X waiting),触发死锁检测,回滚1862的select for update。

为什么会出现这种场景呢?需要对事务中(已经和RD确认事务中包含两条sql,一条select for update,一条update)的两条语句的加锁过程进行分析

如下

  • select for update
    对于如下sql
select  * from t_acm_ecbl         where  ac_org = '100001' and ac_no = '1200020000000501107' and cap_typ = '1' and ccy = 'CNY' for update

事务1861执行如上sql的加锁过程为

2018-12-21T13:34:22.873954+08:00 167 [Note] InnoDB: trx_id: 1861 create a record lock and add it to lock hash table,
space_id: 28
page_no: 745
heap_no: 37
n_bits: 128
primary key: 1
is record lock: 1
is waiting: 0
is gap: 0
is record not gap: 1
is insert intention: 0
lock_mode: 3  (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE)

如果同时另外一个线程进行相同的select操作,加锁过程为
事务1862执行select for update

2018-12-21T13:34:26.379141+08:00 172 [Note] InnoDB: trx_id: 1862 create a record lock and add it to lock hash table,
space_id: 28     ibd no
page_no: 745     数据页no
heap_no: 37      在数据页中的heap no
n_bits: 128
primary key: 1   主键索引
is record lock: 1 行锁
is waiting: 1     处于加锁等待状态,因为事务1861持有此行数据的x锁,x兼容
is gap: 0
is record not gap: 1  只锁记录,不锁gap
is insert intention: 0
lock_mode: 3  (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE)
  • update语句
    如下
UPDATE t_acm_ecbl
          SET ORD_SEQ = 20
        WHERE AC_NO = '1200020000000501107'

由于此update语句的where条件中只包含了主键索引的第一个字段,所以呢,加锁范围被放大了。可以这样来想象这个范围。

扫描二维码关注公众号,回复: 4613772 查看本文章

对于联合主键的如下值
(3,3,3),(4,2,3),(5,3,2)
如果where 条件中只包含了col_1 = 4, 那么在主键索引中,可以插入(4,,)的间隙为(3,3,3)-(5,3,2)。可以看如下的加锁过程

事务1861 执行此update的加锁过程如下
第一:

2018-12-21T13:34:32.260018+08:00 167 [Note] InnoDB: trx_id: 1861 create a record lock and add it to lock hash table,
space_id: 28
page_no: 745
heap_no: 37
n_bits: 128
primary key: 1
is record lock: 1   //行锁
is waiting: 1  //等待状态,因为锁队列中存在1862在等待
is gap: 0     //非gap锁
is record not gap: 0   //非只锁记录,不锁间隙,则是next key(记录+间隙)
is insert intention: 0
lock_mode: 3  (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE)

此时已经引发了死锁检测机制,回滚掉了事务1862.

第二,1861需要再加锁,其实就是对于下一行记录的间隙锁,因为这个间隙可以插入符合where条件中的值。

2018-12-21T13:34:32.260171+08:00 167 [Note] InnoDB: trx_id: 1861 create a record lock and add it to lock hash table,
space_id: 28
page_no: 745
heap_no: 38
n_bits: 128
primary key: 1
is record lock: 1
is waiting: 0
is gap: 1
is record not gap: 0
is insert intention: 0
lock_mode: 3  (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE)

分析完毕。

四、解决办法

非常简单,修改事务中的update语句,where条件中要包含所有的主键值。

猜你喜欢

转载自blog.csdn.net/sun_ashe/article/details/85220559