【MySQL】Deadlock found when trying to get lock; try restarting trans

初始化数据

CREATE TABLE `t_test` (
  `FID` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `FCREATE_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `FMODIFY_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `FUSER_ID` varchar(20) NOT NULL COMMENT '用户id',
  `FLINKMAN_ID` varchar(20) NOT NULL COMMENT '联系人id',
  `FUNREAD_COUNT` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '消息未读数',
  `FLAST_MSG_ID` bigint(20) unsigned DEFAULT NULL COMMENT '最后一条消息id',
  PRIMARY KEY (`FID`) USING BTREE,
  KEY `idx_last_msg_id` (`FLAST_MSG_ID`) USING BTREE
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC COMMENT='测试(会话)';

INSERT INTO t_test(FID, FUSER_ID, FLINKMAN_ID, FUNREAD_COUNT, FLAST_MSG_ID) VALUES(1, 'A', 'B', 0, 1000)
INSERT INTO t_test(FID, FUSER_ID, FLINKMAN_ID, FUNREAD_COUNT, FLAST_MSG_ID) VALUES(2, 'B', 'A', 0, 1000)

在这里插入图片描述

问题重现(简化版)

事务1与事务2并发执行

事务1

-- 时间1:用户A给其联系人B发消息,调用业务接口,开启事务
START transaction ;

-- 时间3:更新会话A-B的最后一条消息
UPDATE t_test SET FLAST_MSG_ID = 1001
WHERE FID = 1;

-- 时间5:更新会话B-A的未读数和最后一条消息
UPDATE t_test SET FUNREAD_COUNT = FUNREAD_COUNT + 1, FLAST_MSG_ID = 1001
WHERE FID = 2;

COMMIT;

在这里插入图片描述

事务2

-- 时间2:用户B给其联系人A发消息,调用业务接口,开启事务
START transaction;

-- 时间4:更新会话B-A的最后一条消息
UPDATE t_test SET FLAST_MSG_ID = 1002
WHERE FID = 2;

-- 时间6:更新会话A-B的未读数和最后一条消息
-- 此时报错:Deadlock found when trying to get lock; try restarting transaction!!!
UPDATE t_test SET FUNREAD_COUNT = FUNREAD_COUNT + 1, FLAST_MSG_ID = 1002
WHERE FID = 1;
	
COMMIT;

在这里插入图片描述

解决

使用 分布式锁+开启本地事务+执行业务逻辑(执行多条sql)+提交本地事务+释放分布式锁,且这个分布式锁的key要设计合理,才能避免死锁。

本示例中每个用户都有类型:客户/经纪人,那么就可以将分布式锁的key设计为场景前缀+客户id+经纪人id。譬如:A用户是客户,B用户是经纪人,那么线程1和线程2都是计算出key=REDISSON:SEND_MSG:A:B,都要先抢夺同一个分布式锁,就不会出现MySQL的死锁了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值