搜索
首页数据库mysql教程MySQL优化index merge引起的死锁怎么解决

背景

生产环境出现死锁流水,通过查看死锁日志,看到造成死锁的是两条一样的update语句(只有where条件中的值不同),

如下:

UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx1' AND `status` = 0;
UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx2' AND `status` = 0;

一开始比较费解,通过大量查询跟学习后,分析出了死锁形成的具体原理,特分享给大家,希望能帮助到遇到同样问题的朋友。

因为MySQL知识点较多,这里对很多名词不进行过多介绍,有兴趣的朋友,可以后续进行专项深入学习。

死锁日志

*** (1) TRANSACTION:
TRANSACTION 791913819, ACTIVE 0 sec starting index read, thread declared inside InnoDB 4999
mysql tables in use 3, locked 3
LOCK WAIT 4 lock struct(s), heap size 1184, 3 row lock(s)
MySQL thread id 462005230, OS thread handle 0x7f55d5da3700, query id 2621313306 x.x.x.x test_user Searching rows for update
UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx1' AND `status` = 0;
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 110 page no 39167 n bits 1056 index `idx_status` of table `test`.`test_table` trx id 791913819 lock_mode X waiting
Record lock, heap no 495 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

*** (2) TRANSACTION:
TRANSACTION 791913818, ACTIVE 0 sec starting index read, thread declared inside InnoDB 4999
mysql tables in use 3, locked 3
5 lock struct(s), heap size 1184, 4 row lock(s)
MySQL thread id 462005231, OS thread handle 0x7f55cee63700, query id 2621313305 x.x.x.x test_user Searching rows for update
UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx2' AND `status` = 0;
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 110 page no 39167 n bits 1056 index `idx_status` of table `test`.`test_table` trx id 791913818 lock_mode X
Record lock, heap no 495 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 110 page no 41569 n bits 88 index `PRIMARY` of table `test`.`test_table` trx id 791913818 lock_mode X locks rec but not gap waiting
Record lock, heap no 14 PHYSICAL RECORD: n_fields 30; compact format; info bits 0

*** WE ROLL BACK TRANSACTION (1)

简要分析下上边的死锁日志:

  • 1、第一块内容(第1行到第9行)中,第6行为事务(1)执行的SQL语句,第7和第8行意思为事务(1)在等待 idx_status 索引上的X锁;

  • 2、第二块内容(第11行到第19行)中,第16行为事务(2)执行的SQL语句,第17和第18行意思为事务(2)持有 idx_status 索引上的X锁;

  • 意思为:事务(2)正在等待在 PRIMARY 索引上获取 X 锁。(but not gap指不是间隙锁)

  • 4、最后一句的意思即为,MySQL将事务(1)进行了回滚操作。

表结构

CREATE TABLE `test_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`trans_id` varchar(21) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_trans_id` (`trans_id`) USING BTREE,
KEY `idx_status` (`status`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

通过表结构可以看出,trans_id 列上有一个唯一索引uniq_trans_id status 列上有一个普通索引idx_status ,id列为主键索引 PRIMARY

InnoDB引擎中有两种索引:

  • 聚簇索引: 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据。

  • 辅助索引: 辅助索引叶子节点存储的是主键值,也就是聚簇索引的键值。

主键索引 PRIMARY 就是聚簇索引,叶子节点中会保存行数据。uniq_trans_id 索引和idx_status 索引为辅助索引,叶子节点中保存的是主键值,也就是id列值。

当我们通过辅助索引查找行数据时,先通过辅助索引找到主键id,再通过主键索引进行二次查找(也叫回表),最终找到行数据。

执行计划

MySQL优化index merge引起的死锁怎么解决

通过看执行计划,可以发现,update语句用到了索引合并,也就是这条语句既用到了 uniq_trans_id 索引,又用到了 idx_status 索引,Using intersect(uniq_trans_id,idx_status)的意思是通过两个索引获取交集。

为什么会用 index_merge(索引合并)

MySQL5.0之前,一个表一次只能使用一个索引,无法同时使用多个索引分别进行条件扫描。但是从5.1开始,引入了 index merge 优化技术,对同一个表可以使用多个索引分别进行条件扫描。

如执行计划中的语句:

UPDATE test_table SET `status` = 1 WHERE `trans_id` = '38' AND `status` = 0 ;

MySQL会根据 trans_id = ‘38’这个条件,利用 uniq_trans_id 索引找到叶子节点中保存的id值;同时会根据 status = 0这个条件,利用 idx_status 索引找到叶子节点中保存的id值;然后将找到的两组id值取交集,最终通过交集后的id回表,也就是通过 PRIMARY 索引找到叶子节点中保存的行数据。

这里可能很多人会有疑问了,uniq_trans_id 已经是一个唯一索引了,通过这个索引最终只能找到最多一条数据,那MySQL优化器为啥还要用两个索引取交集,再回表进行查询呢,这样不是多了一次 idx_status 索引查找的过程么。我们来分析一下这两种情况执行过程。

第一种 只用uniq_trans_id索引 :

  • 根据 trans_id = ‘38’查询条件,利用uniq_trans_id 索引找到叶子节点中保存的id值;

  • 通过找到的id值,利用PRIMARY索引找到叶子节点中保存的行数据;

  • 再通过 status = 0 条件对找到的行数据进行过滤。

第二种 用到索引合并 Using intersect(uniq_trans_id,idx_status)

  • 根据 trans_id = ‘38’ 查询条件,利用 uniq_trans_id 索引找到叶子节点中保存的id值;

  • 根据 status = 0 查询条件,利用 idx_status 索引找到叶子节点中保存的id值;

  • 将1/2中找到的id值取交集,然后利用PRIMARY索引找到叶子节点中保存的行数据

上边两种情况,主要区别在于,第一种是先通过一个索引把数据找到后,再用其它查询条件进行过滤;第二种是先通过两个索引查出的id值取交集,如果取交集后还存在id值,则再去回表将数据取出来。

当优化器认为第二种情况执行成本比第一种要小时,就会出现索引合并。(生产环境流水表中 status = 0 的数据非常少,这也是优化器考虑用第二种情况的原因之一)。

为什么用了 index_merge 就死锁了

MySQL优化index merge引起的死锁怎么解决

上面简要画了一下两个update事务加锁的过程,从图中可以看到,在idx_status 索引和 PRIMARY (聚簇索引) 上都存在重合交叉的部分,这样就为死锁造成了条件。

如,当遇到以下时序时,就会出现死锁:

MySQL优化index merge引起的死锁怎么解决

事务1等待事务2释放锁,事务2等待事务1释放锁,这样就造成了死锁。

MySQL检测到死锁后,会自动回滚代价更低的那个事务,如上边的时序图中,事务1持有的锁比事务2少,则MySQL就将事务1进行了回滚。

解决方案

一、从代码层面

  • where 查询条件中,只传 trans_id ,将数据查询出来后,在代码层面判断 status 状态是否为0;

  • 使用 force index(uniq_trans_id) 强制查询语句使用 uniq_trans_id 索引;

  • where 查询条件后边直接用 id 字段,通过主键去更新。

二、从MySQL层面

  • 删除 idx_status 索引或者建一个包含这俩列的联合索引;

  • 将MySQL优化器的index merge优化关闭。

以上是MySQL优化index merge引起的死锁怎么解决的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:亿速云。如有侵权,请联系admin@php.cn删除
mysql:blob和其他无-SQL存储,有什么区别?mysql:blob和其他无-SQL存储,有什么区别?May 13, 2025 am 12:14 AM

mysql'sblobissuitableForStoringBinaryDataWithInareLationalDatabase,而alenosqloptionslikemongodb,redis和calablesolutionsoluntionsoluntionsoluntionsolundortionsolunsolunsstructureddata.blobobobsimplobissimplobisslowderperformandperformanceperformancewithlararengelitiate;

mySQL添加用户:语法,选项和安全性最佳实践mySQL添加用户:语法,选项和安全性最佳实践May 13, 2025 am 12:12 AM

toaddauserinmysql,使用:createUser'username'@'host'Indessify'password'; there'showtodoitsecurely:1)choosethehostcarecarefullytocon trolaccess.2)setResourcelimitswithoptionslikemax_queries_per_hour.3)usestrong,iniquepasswords.4)Enforcessl/tlsconnectionswith

MySQL:如何避免字符串数据类型常见错误?MySQL:如何避免字符串数据类型常见错误?May 13, 2025 am 12:09 AM

toAvoidCommonMistakeswithStringDatatatPesInMysQl,CloseStringTypenuances,chosethirtightType,andManageEngencodingAndCollat​​ionsEttingsefectery.1)usecharforfixed lengengters lengengtings,varchar forbariaible lengength,varchariable length,andtext/blobforlabforlargerdata.2 seterters seterters seterters seterters

mySQL:字符串数据类型和枚举?mySQL:字符串数据类型和枚举?May 13, 2025 am 12:05 AM

mysqloffersechar,varchar,text,and denumforstringdata.usecharforfixed Lengttrings,varcharerforvariable长度,文本forlarger文本,andenumforenforcingDataAntegrityWithaEtofValues。

mysql blob:如何优化斑点请求mysql blob:如何优化斑点请求May 13, 2025 am 12:03 AM

优化MySQLBLOB请求可以通过以下策略:1.减少BLOB查询频率,使用独立请求或延迟加载;2.选择合适的BLOB类型(如TINYBLOB);3.将BLOB数据分离到单独表中;4.在应用层压缩BLOB数据;5.对BLOB元数据建立索引。这些方法结合实际应用中的监控、缓存和数据分片,可以有效提升性能。

将用户添加到MySQL:完整的教程将用户添加到MySQL:完整的教程May 12, 2025 am 12:14 AM

掌握添加MySQL用户的方法对于数据库管理员和开发者至关重要,因为它确保数据库的安全性和访问控制。1)使用CREATEUSER命令创建新用户,2)通过GRANT命令分配权限,3)使用FLUSHPRIVILEGES确保权限生效,4)定期审计和清理用户账户以维护性能和安全。

掌握mySQL字符串数据类型:varchar vs.文本与char掌握mySQL字符串数据类型:varchar vs.文本与charMay 12, 2025 am 12:12 AM

chosecharforfixed-lengthdata,varcharforvariable-lengthdata,andtextforlargetextfield.1)chariseffity forconsistent-lengthdatalikecodes.2)varcharsuitsvariable-lengthdatalikenames,ballancingflexibilitibility andperformance.3)

MySQL:字符串数据类型和索引:最佳实践MySQL:字符串数据类型和索引:最佳实践May 12, 2025 am 12:11 AM

在MySQL中处理字符串数据类型和索引的最佳实践包括:1)选择合适的字符串类型,如CHAR用于固定长度,VARCHAR用于可变长度,TEXT用于大文本;2)谨慎索引,避免过度索引,针对常用查询创建索引;3)使用前缀索引和全文索引优化长字符串搜索;4)定期监控和优化索引,保持索引小巧高效。通过这些方法,可以在读取和写入性能之间取得平衡,提升数据库效率。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器