MySQL 并发更新冗余索引字段导致的死锁

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
日志服务 SLS,月写入数据量 50GB 1个月
简介: 一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 案例分析2.1 业务场景业务开发同学要做业务逻辑迁移,由A表迁移到B表,B表承担所有的类型的业务,他们的业务逻辑是:...

一 前言

死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。

二 案例分析

2.1 业务场景

业务开发同学要做业务逻辑迁移,由A表迁移到B表,B表承担所有的类型的业务,他们的业务逻辑是:上游发task_id列表给下游,下游的业务根据一定的逻辑进行处理,在并发更新时遇到死锁。

注意: 因为B是新表,不一定有对应task_id的值。

2.2 环境说明

MySQL 5.6.24 事务隔离级别为RR

create table  x
(id int not null auto_increment,
 c int not null default  0,
 d int not null default 0,
 primary key(id),
 key idxcd(c,d));
insert into x(c,d) values(1,0),(3,0),(5,0),(7,0),(10,0),(12,0),(14,0),(16,0);

2.3 测试用例

2.4 死锁日志

2018-04-20 23:05:55 0x7f75cdfff700
*** (1) TRANSACTION:
TRANSACTION 2235, ACTIVE 161 sec updating or deleting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 8 row lock(s), undo log entries 1
MySQL thread id 1016626, OS thread handle 140143880890112, query id 4070003 127.0.0.1 root updating
update x set d=1 where c in (5,10)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 44 page no 4 n bits 80 
index idxcd of table `test`.`x` trx id 2235 
lock_mode X locks gap before rec insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 2237, ACTIVE 36 sec updating or deleting, thread declared inside InnoDB 4997
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1
MySQL thread id 1016629, OS thread handle 140143944005376, query id 4070021 127.0.0.1 root updating
update x set d=1 where c=7
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 44 page no 4 n bits 80 
index idxcd of table `test`.`x` trx id 2237 
lock_mode X locks gap before rec
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 44 page no 4 n bits 80 
index idxcd of table `test`.`x` trx id 2237 
lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (2)

2.5 分析死锁日志

首先我们要再次强调insert 插入操作的加锁逻辑。

第一阶段: 唯一性约束检查,先申请LOCK_INSERT_INTENTION

第二阶段: 如果没有唯一键冲突,新数据插入完成之后:LOCK_X + LOCK_REC_NOT_GAP

对于insert操作来说,若发生唯一约束冲突,需要对冲突的唯一索引申请加上S Next-key Lock。如果其他会话中包含已经插入记录的事务没有提交,则申请加锁出现等待,show engine innodb status中的事务列表中会提示 lock mode S waiting 。

从这里会发现,即使是RC事务隔离级别,也同样会存在Next-Key Lock锁,从而阻塞并发。然而,文档没有说明的是,对于检测到冲突的唯一索引,等待线程在获得S Lock之后,还需要对下一个记录进行加锁,在源码中由函数row_ins_scan_sec_index_for_duplicate进行判断.

其次 我们需要了解锁的兼容性矩阵。

从兼容性矩阵我们可以得到如下结论:

INSERT操作之间不会有冲突。GAP,Next-Key会阻塞插入意向锁INSERT_INTENTIONGAP与Record,Next-Key不会冲突Record与Record、Next-Key之间相互冲突。已有的Insert锁不阻止任何准备加的锁。

另外 RR事务隔离级别下,对于通过索引(唯一或者非唯一)更新或者删除不存在的记录,会申请加上gap锁。

当update 更新被索引字段时,相当于删除之后重新插入新的记录,需要重新组织索引节点。

了解上面的基础知识,我们开始对死锁日志进行分析:

T1时刻 开始事务。

T2时刻 sess2 更新c=6的值,但是c=6 不存在,申请加上(5,0)---(7,0)的X gap锁,日志提示:持有idxcd of table test.x trx id 2207 lock_mode X

T3时刻 sess1 更新2行记录c=5,c=10,并且修改d=1,ses1 会申请(5,0),(10,0) 记录之间的Next-key锁,由于sess1需要插入新的记录(5,1)sess2 持有的X GAP 锁范围内,根据锁的兼容性矩阵,GAP,Next-Key会阻塞插入意向锁INSERT_INTENTION,故日志提示:index idxcd of table test.x trx id 2235 lock_mode X locks gap before rec insert intention waiting 。

T4时刻 sess2 更新c=7对应d=1,同样相当于insert (7,1) 在(5,0),(10,0)之间。根据兼容矩阵GAP,Next-Key会阻塞插入意向锁INSERT_INTENTION 故日志提示: index idxcd of table test.x trx id 2237 lock_mode X locks gap before rec insert intention waiting 。

至此,sess2 持有gap锁阻塞sess1 插入(5,1),sess1持有Next-key 阻塞sess2 插入(7,1),循环等待出现死锁。

2.6 解决方法

其实解决方法比较简单 把组合索引 idxcd(c,d) 中的d去掉,改为idxc(c),避免GAP/Next-key 阻塞插入意向锁INSERT_INTENTION 即可,开发同学18:10分左右修改索引之后,业务不再出现死锁。

三 小结

本文的死锁算是蛮有意思的一个案例:并发多个update更新二级索引列,相当于索引节点重新组织,更新等于删除加插入,在死锁日志出现了插入意向锁。第一次猜想的时候还以为有 多个insert 操作,但是实际上只有更新动作。

最后想说关于解决死锁问题的思路:

具备扎实的锁相关的基础知识。

单单根据死锁日志其实比较难以判断具体的sql执行情况,需要和开发同学沟通好(当然开发也要提提供完整的业务逻辑),理清业务执行sql的逻辑,然后去模拟测试。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
3天前
|
缓存 关系型数据库 MySQL
MySQL并发支撑底层Buffer Pool机制详解
【10月更文挑战第18天】在数据库系统中,磁盘IO操作是性能瓶颈之一。为了提高数据访问速度,减少磁盘IO,MySQL引入了缓存机制。其中,Buffer Pool是InnoDB存储引擎中用于缓存磁盘上的数据页和索引页的内存区域。通过缓存频繁访问的数据和索引,Buffer Pool能够显著提高数据库的读写性能。
19 2
|
7天前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
39 3
Mysql(4)—数据库索引
|
1天前
|
存储 关系型数据库 MySQL
如何在MySQL中进行索引的创建和管理?
【10月更文挑战第16天】如何在MySQL中进行索引的创建和管理?
10 1
|
1天前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
5 1
|
2天前
|
监控 关系型数据库 MySQL
mysql8索引优化
综上所述,深入理解和有效实施这些索引优化策略,是解锁MySQL 8.0数据库高性能查询的关键。
9 0
|
6天前
|
SQL 关系型数据库 MySQL
美团面试:mysql 索引失效?怎么解决? (重点知识,建议收藏,读10遍+)
本文详细解析了MySQL索引失效的多种场景及解决方法,包括破坏最左匹配原则、索引覆盖原则、前缀匹配原则、`ORDER BY`排序不当、`OR`关键字使用不当、索引列上有计算或函数、使用`NOT IN`和`NOT EXISTS`不当、列的比对等。通过实例演示和`EXPLAIN`命令分析,帮助读者深入理解索引失效的原因,并提供相应的优化建议。文章还推荐了《尼恩Java面试宝典》等资源,助力面试者提升技术水平,顺利通过面试。
|
9天前
|
关系型数据库 MySQL 数据库
深入浅出MySQL索引优化:提升数据库性能的关键
在这个数据驱动的时代,数据库性能的优劣直接关系到应用的响应速度和用户体验。MySQL作为广泛使用的数据库之一,其索引优化是提升查询性能的关键。本文将带你一探MySQL索引的内部机制,分析索引的类型及其适用场景,并通过实际案例演示如何诊断和优化索引,以实现数据库性能的飞跃。
|
9天前
|
监控 关系型数据库 MySQL
一次彻底讲清如何处理mysql 的死锁问题
【10月更文挑战第16天】本文详细介绍了如何处理 MySQL 中的死锁问题,涵盖死锁的概念、原因、检测方法及解决策略,强调通过优化事务设计、调整数据库参数、手动处理和预防措施等手段,有效减少死锁,提升数据库性能与稳定性。
|
11天前
|
SQL 存储 关系型数据库
MySQL新增字段/索引会不会锁表?
MySQL新增字段/索引会不会锁表?
|
9天前
|
存储 SQL 关系型数据库
Mysql学习笔记(二):数据库命令行代码总结
这篇文章是关于MySQL数据库命令行操作的总结,包括登录、退出、查看时间与版本、数据库和数据表的基本操作(如创建、删除、查看)、数据的增删改查等。它还涉及了如何通过SQL语句进行条件查询、模糊查询、范围查询和限制查询,以及如何进行表结构的修改。这些内容对于初学者来说非常实用,是学习MySQL数据库管理的基础。
43 6