replace into导致的死锁

2020-11-26  本文已影响0人  快点给我想个名
需求

每小时统计一遍所有用户的某几项数据,一天当中此用户只有一条记录。对于有数据的用户保存数据库,否则放弃。

数据库 mysql
事务隔离级别 rr

表结构大体如下:

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `time_key` int(11) NOT NULL,
  `doctor_id` varchar(50) COLLATE utf8mb4_bin NOT NULL,
  `ask_count` int(11) NOT NULL DEFAULT '0',
  `cancel_count` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uix_user_time_key_doctor_id` (`time_key`,`doctor_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

唯一索引 time_key+doctor_id

实现方式

1.对于所有的用户分页查询
2.通过CompletableFuture异步查询用户各项数据

CompletableFuture<DataTransferObject<Map<String,Integer>>> askFuture = CompletableFuture.supplyAsync(() -> askService.getAskCount(askStatisDto));
CompletableFuture<DataTransferObject<Map<String,Integer>>> cancelFuture = CompletableFuture.supplyAsync(() -> askService.getCancelCount(askStatisDto));

3.之后整合数据,异步保存

CompletableFuture.allOf(askFuture ,cancelFuture).whenCompleteAsync((aVoid, throwable) -> {
    //存库
}

4.数据库保存语句用的是 replace into 表名

问题

死锁

问题复现

1.执行第一条语句

BEGIN;

REPLACE INTO user (time_key,doctor_id,ask_count,cancel_count) values(20190523,'333',1,1);

2.执行第二条语句

BEGIN;

REPLACE INTO user (time_key,doctor_id,ask_count,cancel_count) values(20190523,'999',6,6)

3.出现死锁,命令查看

死锁.png
这里死锁是因为从(-∞,插入数据]直接加了间隙锁
详见:https://www.dazhuanlan.com/2019/10/21/5dadd2ac43e9c/ 下一个键锁(Next-Key Locks)

所以现在的问题找到了。最终的解决办法是改成了同步。

上一篇 下一篇

猜你喜欢

热点阅读