最近线上项目报了一个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.保证事务简短并在一个批处理中,避免出现循环插入死锁问题
 
        这里的场景记录死锁是并发插入多条记录,顺序一样出现的死锁,在并发插入中如果顺序不一样出现死锁的概率会更大。
 

记一次线上MySQL数据库死锁问题的更多相关文章

  1. 记一次线上Mysql数据库 宕机

    从发现问题,到最后解决一共消耗两个半小时(7:30~10:00),报警电话16通,警察坐镇,未完待续 ......

  2. 线上Mysql数据库崩溃事故的原因和处理

    前文提要 承接前文<一次线上Mysql数据库崩溃事故的记录>,在文章中讲到了一次线上数据库崩溃的事件记录,建议两篇文章结合在一起看,不至于摸不着头脑. 由于时间原因,其中只讲了当时的一些经 ...

  3. Spring+SpringMVC+MyBatis+easyUI整合进阶篇(八)线上Mysql数据库崩溃事故的原因和处理

    前文提要 承接前文<一次线上Mysql数据库崩溃事故的记录>,在文章中讲到了一次线上数据库崩溃的事件记录,建议两篇文章结合在一起看,不至于摸不着头脑. 由于时间原因,其中只讲了当时的一些经 ...

  4. 一次线上Mysql数据库崩溃事故的记录

    文章简介 工作这几年,技术栈在不断更新,项目管理心得也增加了不少,写代码的速度也在提升,感觉很欣慰,毕竟是在一直进步,但是过程中也有许许多多的曲折,也踩过了数不尽的坑坑洼洼,从一个连百度都不知道用的萌 ...

  5. Spring+SpringMVC+MyBatis+easyUI整合进阶篇(七)一次线上Mysql数据库崩溃事故的记录

    作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载. 文章简介 工作这几年,技术栈在不断更新,项目管理心得也增加了不少,写 ...

  6. 通过SSH秘钥登录线上MySQL数据库(基于Navicat)

    前言 生产环境的数据库往往需要经过严格的安全限制,所以禁用密码登录,使用秘钥的方式是一种相对安全的登录方式. 原理: 角色: 主机A:其他主机,有访问线上数据库的权限 主机B:线上数据库的主机 主机C ...

  7. 原创 记录一次线上Mysql慢查询问题排查过程

    背景 前段时间收到运维反馈,线上Mysql数据库凌晨时候出现慢查询的报警,并把原始sql发了过来: --去除了业务含义的sql update test_user set a=1 where id=1; ...

  8. 【MySQL】记一次线上重大事故:二狗子竟然把线上数据库删了!!

    写在前面 估计二狗子这几天是大姨夫来了,心情很郁闷,情绪也很低落,工作的时候也有点心不在焉.让他发个版本,结果,一行命令下去把线上的数据库删了!你没听错:是删掉了线上的数据库!运营那边顿时炸了锅:怎么 ...

  9. 记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理

    昨晚我正在床上睡得着着的,突然来了一条短信. 啥,线上MySQL死锁了,我赶紧登录线上系统,查看业务日志. 能清楚看到是这条insert语句发生了死锁. MySQL如果检测到两个事务发生了死锁,会回滚 ...

随机推荐

  1. Hydra 无法爆破SSH 解决办法

    今天测试ssh爆破,发现使用hydra有些问题,windows版本没有协议支持其他的貌似都可以,kali本身也有hydra环境但是也会出现问题,所以就搜了一些资料贴在这里,当然这也是我测试过的,重新编 ...

  2. 在JavaScript文件中读取properties文件的方法

    假设有JavaScript文件叫做:readproperties.js,这个文件需要读取config.properties这个配置文件,步骤如下: 1.  下载插件jquery.i18n.proper ...

  3. Microsoft Office Word 2007 文档结构图突然变小的解决办法

    前记: 一个word文档不知道修改了什么,突然发现文档结构图显示的文字变得非常的小了. 用ctrl+鼠标滚轮只能放大或是缩小行间距,对文字没有什么变化. 解决办法: 1.打开文档结构图 点击视图,勾选 ...

  4. [译]使用scikit-learn进行机器学习的简介(教程1)

    原文:http://www.cnblogs.com/taceywong/p/4568806.html 原文地址:http://scikit-learn.org/stable/tutorial/basi ...

  5. view xml 中的 button 调用web客户端事件

    最近写一个模块 需要 在客户端干点事. 按常规的方法, 应该是写个 客户端模块. 在 客户端 init, start, render 去渲染个按钮出来干事.暂时还不太理解WEB模块如何很好地同服务器端 ...

  6. Java Executor 线程池

  7. 16-spring学习-配置文件操作

    实际使用:配置文件 spring的核心就是一个配置文件.所以只有将表达式应用到配置文件上才会特别有意义. 范例:利用配置文件,编写表达式应用 <bean id="str" c ...

  8. DBCP( 二) DataBase Connection Pool 的使用

    使用DBCP必须用的三个包: commons-dbcp-1.2.1.jar, commons-pool-1.2.jar, commons-collections-3.1.jar. 配置参数. Java ...

  9. powershell执行脚本

    powershell执行脚本   执行powershell脚本有两种方式: 1.通过命令行参数启动脚本 C:\Windows\System32\WindowsPowerShell\v1.0\power ...

  10. 重要:Linux下IDE--KDevelop (用来跟踪调试C++) Ubuntu下QT4开发环境的搭建及初体验

    Linux下安装Qt4有两大问题,一是环境变量,二是IDE(集成开发环境).安装Qt4也有两种方法,一种是apt-get,一种是下载源码包,而后 一种方法已经人证实是最有可能不好使的方法.所以我最终采 ...