背景
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
问题&分析
事务相关信息
-
表结构如下
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
-
索引
主键索引:id
二级索引:index_core_id & idx_valid
这里有个坑,valid字段只有两个值0,1,当初为了优化慢SQL,尝试加上去的
-
事务信息
隔离级别:RR
Java层没有加事务,所以没有传播属性
代码逻辑
此处使用了多线程进行操作, 所以可能存在线程竞争问题
materialTrackAttrMapper.saveBatch(materialTrackAttrPOS);
materialTrackAttrMapper.updateValidByIds(calHotChainDTO.getMaterialIds());
materialTrackAttrMapper.deleteByValidTypeAndIds(TrackAttrValidEnum.UN_VALID.getCode(),calHotChainDTO.getMaterialIds());
DAS监控
第一次死锁
由上图DAS监控可知,session1 和 session2 互相需要对方 id 上的排它锁。
数据
这里有个前提:core_id 的in范围不相互重叠,后面查询日志也正是不重叠。所以跟数据关系没有关系。
使用的索引
- Delete
EXPLAIN DELETE
FROM
material_track_attr
WHERE
valid = 1
AND core_id IN (
358244
)
只使用了index_core_id索引
| possible_keys | key |
|---|---|
| index_core_id, idx_valid | index_core_id |
- 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_keys | key |
|---|---|
| index_core_id | index_core_id |
由于delete和update只使用了index_core_id索引,但是他们的数据范围又不一致, 且不存在间隙锁,按道理来讲不会有主键索引上的冲突。
第二次死锁
这个问题的解决是渐进的,新出来的死锁日志中出现了idx_valid索引上的锁。也就是说用到了idx_valid。
如果在idx_valid上加锁了,那么问题就容易解决了。
使用的索引 和 加锁的索引不是一回事!!!!, 这个就能解释通了
竞争原因
解决办法
除去无效的索引,idx_valid