Repetable Read下无效索引导致死锁问题的解决

187 阅读2分钟

image.png

背景

Sentry 和 DAS平台报错了,发生了死锁。

业务影响

影响素材的活跃度&热度计算,使用的数据还是昨天的,或者为0。不会出现P0/P1问题。

异常指标

sentry

死锁告警

### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: 

Deadlock found when trying to get lock; try restarting transaction

### The error may exist in URL [jar:file:/root/ROOT.jar!/BOOT-INF/classes!/mapper/MaterialTrackAttrMapper.xml]

### The error may involve defaultParameterMap

### The error occurred while setting parameters

### SQL: /*mycat:schema=t193*/delete from material_track_attr where valid = 1 and core_id in      

问题&分析

事务相关信息

  1. 表结构如下

CREATE TABLE `material_track_attr` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',

  `core_id` bigint(20) NOT NULL COMMENT '素材id',

  `down_num` int(11) NOT NULL DEFAULT '0' COMMENT '下载量',

  `share_num` int(11) NOT NULL DEFAULT '0' COMMENT '分享量',

  `view_num` int(11) NOT NULL DEFAULT '0' COMMENT '浏览量',

  `hot` double(11,5) NOT NULL DEFAULT '0.00000' COMMENT '热度',

  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

  `material_create_time` datetime DEFAULT NULL COMMENT '素材创建时间',

  `create_user` bigint(20) NOT NULL DEFAULT '-1' COMMENT '创建人',

  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

  `update_user` bigint(20) NOT NULL DEFAULT '-1' COMMENT '更新人',

  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除(0:未删除,1:已删除)',

  `valid` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否有效(0:有效,1:无效)',

  `activity` int(11) DEFAULT '0' COMMENT '活跃度',

  `search_factor` double(11,5) DEFAULT '0.00000' COMMENT '搜索因子',

  PRIMARY KEY (`id`) USING BTREE,

  KEY `index_core_id` (`core_id`),

  KEY `idx_valid` (`valid`)

) ENGINE=InnoDB AUTO_INCREMENT=953764 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  1. 索引

主键索引:id

二级索引:index_core_id & idx_valid

这里有个坑,valid字段只有两个值0,1,当初为了优化慢SQL,尝试加上去的

  1. 事务信息

隔离级别:RR

Java层没有加事务,所以没有传播属性

代码逻辑

此处使用了多线程进行操作, 所以可能存在线程竞争问题

materialTrackAttrMapper.saveBatch(materialTrackAttrPOS);

materialTrackAttrMapper.updateValidByIds(calHotChainDTO.getMaterialIds());

materialTrackAttrMapper.deleteByValidTypeAndIds(TrackAttrValidEnum.UN_VALID.getCode(),calHotChainDTO.getMaterialIds());

image.png

DAS监控

第一次死锁

由上图DAS监控可知,session1 和 session2 互相需要对方 id 上的排它锁。

数据

这里有个前提:core_id 的in范围不相互重叠,后面查询日志也正是不重叠。所以跟数据关系没有关系。

使用的索引
  1. Delete
EXPLAIN DELETE 

FROM

        material_track_attr 

WHERE

        valid = 1 

        AND core_id IN (

                358244

        )

只使用了index_core_id索引

possible_keyskey
index_core_id, idx_validindex_core_id
  1. Update
update material_track_attr set valid = !valid where core_id in ( 942 , 943 , 944 , 945 , 946 , 947 , 948 , 949 , 950 , 951)

只使用了index_core_id索引

possible_keyskey
index_core_idindex_core_id

由于delete和update只使用了index_core_id索引,但是他们的数据范围又不一致, 且不存在间隙锁,按道理来讲不会有主键索引上的冲突。

第二次死锁

这个问题的解决是渐进的,新出来的死锁日志中出现了idx_valid索引上的锁。也就是说用到了idx_valid。

如果在idx_valid上加锁了,那么问题就容易解决了。

使用的索引 和 加锁的索引不是一回事!!!!, 这个就能解释通了

image.png

竞争原因

解决办法

除去无效的索引,idx_valid

参考

help.aliyun.com/document_de…