首页 技术 正文
技术 2022年11月21日
0 收藏 592 点赞 4,445 浏览 3881 个字

        最近线上项目报了一个MySQL死锁(DealLock)错误,虽说对业务上是没有什么影响的,由于自己对数据库锁这块了解不是很多,之前也没怎么的在线上碰到过。这次刚好遇到了,便在此记录一下。 

  • 出现死锁问题背景

        项目层面:报错的项目做的是一个批量下单的动作,会同时写入多条订单数据,代码之前写的是一个事务中一个循环一条一条insert到数据库(至于为啥没用批量插入就不追究了,历史原因了)。        数据库层面:一张test表(非线上真实表),比较重要的是有一个 type 和 name的唯一索引。 事务隔离级别: read commited

CREATE TABLE `test` (
`id` bigint(11) NOT NULL ,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`type` tinyint(4) NULL DEFAULT NULL ,
`uid` int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
UNIQUE INDEX `uniq_type_name` (`type`, `name`) USING BTREE
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT
;

    出现死锁的情况是批量下单的接口,外部重复请求了两次,两次相隔10毫秒。    两次请求执行的sql(一次请求会执行三条insert)除了主键id不一样,其他都是一样的:如下

insert into test(id, name, type, uid) values(1, "DT590", 3,  1001);
insert into test(id, name, type, uid) values(2, "DT589", 3, 1001);
insert into test(id, name, type, uid) values(3, "DT588", 3, 1001);

 报错的死锁日志:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2018-06-21 10:51:03 2b16deb03700
*** (1) TRANSACTION:
TRANSACTION 1905650677, ACTIVE 0.001 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1
LOCK BLOCKING MySQL thread id: 16983306 block 34208692
MySQL thread id 34208692, OS thread handle 0x2b2203b0b700, query id 9093982364 172.24.18.106 app_redcliffc update
INSERT INTO `test` (id, name, type, uid) VALUES (4, 'DT590', 3, 1001)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 138 page no 16492 n bits 408 index `uniq_type_name` of table `db`.`test` trx id 1905650677 lock mode S waiting
Record lock, heap no 341 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000048; asc H;;
1: len 10; hex 44543631393230363835; asc DT61920685;;
2: len 8; hex 0461116807c09a00; asc a h ;;*** (2) TRANSACTION:
TRANSACTION 1905650675, ACTIVE 0.004 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 2
MySQL thread id 16983306, OS thread handle 0x2b16deb03700, query id 9093982366 172.24.18.105 app_redcliffc update
INSERT INTO `test` (id, name, type, uid) VALUES (2, 'DT589', 3, 1001)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 138 page no 16492 n bits 408 index `uniq_type_name` of table `db`.`test` trx id 1905650675 lock_mode X locks rec but not gap
Record lock, heap no 341 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000048; asc H;;
1: len 10; hex 44543631393230363835; asc DT61920685;;
2: len 8; hex 0461116807c09a00; asc a h ;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 138 page no 16492 n bits 408 index `uniq_type_name` of table `db`.`test` trx id 1905650675 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 341 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 4; hex 80000048; asc H;;
1: len 10; hex 44543631393230363835; asc DT61920685;;
2: len 8; hex 0461116807c09a00; asc a h ;;*** WE ROLL BACK TRANSACTION (1)
  • 问题分析

   死锁日志分析

session1   session2
insert into test(id, name, type, uid) values(1, “DT590”, 3,  1001); 事务一先到,先插入第一条记录DT590,成功  
insert into test(id, name, type, uid) values(2, “DT589”, 3,  1001); 事务一继续插入第二天DT589记录,这个时候事务二请求到了,开始插入第一条记录DT590,然后就报出死锁,事务二回滚,事务一成功执行 insert into test(id, name, type, uid) values(1, “DT590”, 3,  1001);

  

session1 持锁 session2 持锁
insert into test(id, name, type, uid) values(1, “DT590”, 3,  1001); 插入一条数据库中没有的记录,对DT590这条记录加了一个x锁    
insert into test(id, name, type, uid) values(2, “DT589”, 3,  1001); 这时事务一插入DT589时候,发现这条记录已经有了一个gap lock(DT589这条记录刚好被事务二插DT590时候申请的gap lock包含了),会先申请一个insert intention waiting插入意向锁,这个锁和事务二持有gap lock互斥,发生死锁。事务一在等事务二释放这条记录gap lock, 事务二在等事务一释放DT590 X锁 insert into test(id, name, type, uid) values(1, “DT590”, 3,  1001); 事务二插入有唯一索引DT590这条记录,发现这条记录上已经有了x锁,所以会申请一个该条记录的s锁和gap lock

 

  • 相关一些锁知识

        InnoDB锁细分为如下几种子类型:                  record lock(RK)  锁直接加在索引记录上面,锁住的是key                 gap lock(GK)  间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况                 next key lock(NK)  行锁和间隙锁组合起来就叫Next-Key Lock                 insert intention lock(IK)  如果插入前,该间隙已经由gap锁,那么Insert会申请插入意向锁。因为了避免幻读,当其他事务持有该间隙的间隔锁,插入意向锁就会被阻塞(不用直接用gap锁,是因为gap锁不互斥)     insert中对唯一索引的加锁逻辑 : 先做唯一索引冲突检测,如果存在目标行,会先对目标行加S NK, 

  • 总结

        1.保证事务简短并在一个批处理中,避免出现循环插入死锁问题         这里的场景记录死锁是并发插入多条记录,顺序一样出现的死锁,在并发插入中如果顺序不一样出现死锁的概率会更大。 

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,953
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,478
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,290
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,107
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,739
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,773