现象

压测过程中游戏server端出现错误日志:

2020/06/23 12:00:14 [error] 546#0: *24612413 FastCGI sent in stderr: "PHP message: [500] /api/v2/item/ext_exchange SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction, query was: DELETE FROM `UserExpirationItem_p33` WHERE (id IN (106085, 106131, 106144, 106143, 106142, 106141, 106140, 106145, 106138, 106129, 106147, 106130, 106128, 106127, 106132, 106133, 106134, 106084, 106146, 106137, 106125, 106102, 106083, 106148, 104660, 106151, 106152, 106155, 106154, 106153, 106156, 106139, 106157, 106159, 106149, 106168, 106101, 106150, 106103, 196593, 196592, 196591, 196590, 196617, 196602, 196589, 196619, 196595, 196594, 196604, 196596, 196597, 196598, 196599, 196600, 196601, 196603, 196616, 196605, 196606, 196644, 196645, 196625, 196643, 196630, 196620, 196621, 196622, 196623, 196624, 196608, 196626, 196627, 196628, 196629, 196631, 196642, 196633, 196618, 196634, 196635, 196636, 196637, 196638, 196639, 196640, 196641, 196607, 196448, 196609, 196437, 196457, 196458, 196459, 196460, 196461, 196462, 196463, 196464," while reading response header from upstreamt ......"

显然程序出现mysql死锁了。

原因分析

1、 找到死锁更多信息

使用语句:show engine innodb status

上面语句输出内容太多,输出到文件:

mysql -u aetes_dev -h your_ip -p --execute="show engine innodb status \G" > ./mysql.log

more命令查看到如下内容:

*** (1) TRANSACTION:

TRANSACTION 13096646, ACTIVE 9 sec fetching rows

mysql tables in use 1, locked 1

LOCK WAIT 32 lock struct(s), heap size 3520, 3342 row lock(s), undo log entries 6601

MySQL thread id 688878, OS thread handle 140313170929408, query id 46108164 10.80.18.12 aetes_dev updating

DELETE FROM `UserExpirationItem_p04` WHERE (id IN (188838, 188847, 188841, 188842, 188862, 188843, 188844, 188845, 188846, 188849, 188863, 188854, 188856

, 188858, 188859, 188860, 188861, 188850, 188962, 188848, 188840, 188951, 188956, 188865, 188868, 188873, 188910, 188912, 188952, 188953, 188950, 188954,

188957, 188958, 188959, 188960, 188961, 188963, 188864, 188964, 202869, 202839, 202841, 202870, 202864, 202868, 202867, 202866, 202807, 202865, 202815,

202822, 202808, 202809, 202810, 202811, 202812, 202813, 202814, 202816, 202817, 202862, 202863, 202842, 202861, 202848, 202804, 202806, 202871, 202855, 2

02819, 202843, 202844, 202845, 202846, 202847, 202849, 202860, 202850, 202851, 202852, 202853, 202854, 202856, 202840, 202857, 202858, 202859, 202818, 20

3538, 202820, 204251, 204241, 204242, 204243, 204244, 204245, 204246, 204247, 204249, 204233, 204250, 204252, 204239, 204253, 204254, 204255, 204256, 2

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 9016 page no 59 n bits 368 index PRIMARY of table `alice_user_04`.`UserExpirationItem_p04` trx id 13096646 lock_mode X locks rec but not gap waiting

Record lock, heap no 191 PHYSICAL RECORD: n_fields 9; compact format; info bits 0

0: len 8; hex 00000000000323d0; asc # ;;

1: len 6; hex 000000c7d802; asc ;;

2: len 7; hex e3000001fe0110; asc ;;

3: len 4; hex 3b9accc0; asc ; ;;

4: len 4; hex cf34b4c3; asc 4 ;;

5: len 1; hex 00; asc ;;

6: len 5; hex 99a72cde5e; asc , ^;;

7: len 5; hex 99a6aede5e; asc ^;;

8: len 5; hex 99a6aede5e; asc ^;;

*** (2) TRANSACTION:

TRANSACTION 13096962, ACTIVE 6 sec fetching rows

mysql tables in use 1, locked 1

21 lock struct(s), heap size 3520, 3 row lock(s), undo log entries 3301

MySQL thread id 688901, OS thread handle 140313175684864, query id 46108334 10.80.18.12 aetes_dev updating

DELETE FROM `UserExpirationItem_p04` WHERE (id IN (192012, 190613, 191994, 191995, 191996, 192002, 191998, 191983, 191988, 191981, 191975, 192001, 191949

, 191982, 191950, 191971, 191973, 191974, 191997, 191976, 191978, 191979, 191980, 192006, 191993, 191977, 191992, 192254, 191990, 192008, 192009, 192010,

192011, 192204, 192092, 192094, 192197, 192198, 192007, 191999, 191985, 192004, 191987, 191986, 191989, 191984, 191991, 192003, 192000, 192005, 206041,

206082, 206083, 206084, 206085, 206086, 206059, 206057, 206088, 206081, 206032, 206028, 206029, 206030, 206031, 206033, 206034, 206035, 206036, 206037, 2

06038, 206080, 206039, 206068, 206079, 206065, 206042, 206153, 206151, 206027, 206072, 206060, 206061, 206062, 206063, 206064, 206066, 206078, 206067, 20

6069, 206070, 206071, 206073, 206058, 206074, 206075, 206076, 206077, 206040, 206130, 206056, 206143, 206133, 206134, 206136, 206121, 206137, 206138, 2

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 9016 page no 59 n bits 368 index PRIMARY of table `alice_user_04`.`UserExpirationItem_p04` trx id 13096962 lock_mode X locks rec but not gap waiting

说明:

1) 从php错误日志和mysql查询到的数据都说明是执行一个delete from UserExpirationItem_p04 where id in(…)的语句出现死锁了,这里where in后面的数据还比较多,导致没有打印完整。

2) delete where id in后面怎么会有这么多id?

3) id是主键字段,delete 语句实际是1条读加1条写的复合操作,理论上应该是加的行锁(X锁),是否是因为存在重复id导致死锁?

2、 结合代码分析

找到/api/v2/item/ext_exchange具体执行逻辑:

1) 将玩家gacha_point全部兑换掉;测试玩家身上每次登陆会发送一种能兑换ticket_001_01的道具的属性gacha_point 330000点,100点能兑换一个ticket_001_01

2) 添加3300个ticket_001_01道具到玩家背包中,ticket_001_01是不可重叠有过期时间的道具,即对应UserExpirationItem类型;添加前有个上限检查逻辑:计算删除的旧道具数量 = 当前拥有数+这次添加数量-最大上限数,由于上限数是50这里算出需要删除数量是3250,所以相当于delete where in后面有3000多个id,这与日志不完整吻合,解释了问题2)。

这里删除逻辑是:先添加道具,后找到需要删除的道具id,进行批量删除(前面的计算看不懂没问题,重点在这个删除)

批量删除函数:

将所有id放到in后面里中,调用一次delete。

综上逻辑实际发现调用接口都是根据用户id添加自己的道具,删除自己的超出数量道具,并不会出现id重复情况,那为什么会出现死锁呢?猜测原因:

delete语句后面id过多,导致没有使用到索引,直接全表扫描发生表级锁进而导致的死锁?

构造一个3000多id的delete语句,使用explain查看执行计划:

从上图看出扫描行数接近全表数量,基本可以看出是全表扫描了,可以论断猜测。

解决办法

delete where in 效率本身不高,不建议并发使用,2种解决办法:

1) 统一改成delete where id=,保证每次走索引,发生行锁;但是会造成需要执行大量条sql的情况;

2) 将id分批,保证1批where in后面的id数据较少;

显然第2种方法更合适,将in后面的id拆分批次处理(每次100个),最终代码如下:

修改后相同环境重新测试多次没有再出现死锁报错情况,说明死锁问题得以解决。

mysql delete in死锁_delete where in导致的死锁问题相关推荐

  1. mysql deadlock 展示_MySQL DeadLock -- 二级索引导致的死锁

    MySQL Deadlock 复现步骤 建表SQL CREATE TABLE `t_wms_order_item` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, ...

  2. mysql 表死锁_MySQL Innodb表导致死锁日志情况分析与归纳

    案例描述在定时脚本运行过程中,发现当备份表格的sql语句与删除该表部分数据的sql语句同时运行时,mysql会检测出死锁,并打印出日志. 两个sql语句如下:(1)insert into backup ...

  3. MySQL 5.6.35 索引优化导致的死锁案例解析

    一.背景 随着公司业务的发展,商品库存从商品中心独立出来成为一个独立的系统,承接主站商品库存校验.订单库存扣减.售后库存释放等业务.在上线之前我们对于核心接口进行了压测,压测过程中出现了 MySQL ...

  4. 线上MySQL死锁分析——索引设置不当导致的死锁

    文章目录 1. 背景 2. MySQL InnoDB的锁机制 2.1 MySQL中的锁类型 2.2 行锁的加锁规则 2.3 死锁检测机制 3. 本文案例分析 3.1 分析InnoDB status日志 ...

  5. update和delete操作忘加where条件导致全表更新的处理方法

    在数据库日常维护中,开发人员是最让人头痛的,很多时候都会由于SQL语句写的有问题导致服务器出问题,导致资源耗尽.最危险的操作就是在做DML操作的时候忘加where条件,导致全表更新,这是作为运维或者D ...

  6. lua mysql 死锁_使用 mysqladmin debug 查看死锁信息

    使用 mysqladmin debug 查看死锁信息 mysqladmin -S /mysql/data/mysql.sock debug 然后在error日志中,会看到: 11 lock struc ...

  7. MySQL DELETE 删除语句加锁分析

    MySQL DELETE 删除语句加锁分析 Posted on 2017-09-24 by Harvey Leave a comment  MySQL http://www.fordba.com/lo ...

  8. mysql rename时长_MySQL · BUG分析 · Rename table 死锁分析

    背景 InnoDB buffer pool中的page管理牵涉到两个链表,一个是lru链表,一个是flush 脏块链表,由于数据库的特性: 脏块的刷新,是异步操作: page存在两个版本,一个是ibd ...

  9. DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2

    本文介绍使用Windbg去验证<DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子>中的结论,调试对象是文中刚开始那个例子.(转载请指明出于breakso ...

  10. 进程死锁的危害、导致原因和解决方法

    在多个程序同时执行的情况下,多个进程可能出现竞争一定数量的资源.若某个进程申请资源,且此时资源不可用,那么该进行将进入等待状态.如果所申请的资源被其他等待进程占有,那么该等待进程有可能永远处于等待状态 ...

最新文章

  1. 模拟器不全屏_iOS 14实测GBA游戏模拟器下载
  2. html model 属性值,如何将Model属性编辑为HTML
  3. Android EventBus使用与思路总结
  4. keras保存和载入模型继续训练
  5. 使用Redis Stream来做消息队列和在Asp.Net Core中的实现
  6. 为旗下硬件产品服务,LG推出基于SLAM技术的3D摄像头
  7. GZNT模版文件说明
  8. (转) IOS8 设置TableView Separatorinset 分割线从边框顶端开始
  9. [20171124]xxd与通配符.txt
  10. 逻辑推理的分类:演绎推理(详解)、归纳推理、类比推理
  11. 【引文74 引文114】基于区块链的联邦学习的激励机制设计
  12. android如何截屏快捷键,安卓手机怎么截图 各大品牌快捷键截屏大集合
  13. mysql常见关键字的用法_MySQL 常用关键字用法详解
  14. HTML+CSS ---- 背景图片
  15. 史上最强最逼真的游戏
  16. 西安交通大学大学计算机考试题,西安交通大学17年3月课程考试《计算机应用基础》作业考核试题...
  17. Java(13)- 抽象类
  18. 一个高性能无锁非阻塞链表队列
  19. dhcp租约(dhcp租约时间可设置为永久吗)
  20. 惠州技校那间有读计算机网络的,惠州有哪些公办职校中专学校

热门文章

  1. 【Kotlin学习之旅】Kotlin实现101个C#的LINQ示例,让你领略一下Kotlin代码的魅力
  2. Pyhton计算一元二次方程的根
  3. android 373dpi对应的布局,[荣耀6X BLN-AL10] EMUI5.0 B373 自定义DPI 来电闪光 接听 录音 核心控制 性能调节 游戏模式 稳定精简顺畅等...
  4. 当电脑内存比较小的时候,小于4G,安装32bit还是64bit呢
  5. Python之路(一)
  6. markdown编辑器示范
  7. ACER-4738ZG 拆机改散热
  8. 无设备云控系统(ipad协议)
  9. 服务器虚拟化 lpar,HMC与VIOS对新LPAR提供存储与网络虚拟化的支持
  10. ESP8266连接手机