MySQL update use index merge(Using intersect) increase chances for deadlock

昨天一同事发现线上系统在并发更新的时候出现了死锁,通过排查定位于update更新使用了两个索引导致,死锁信息如下:

*** (1) TRANSACTION:
TRANSACTION 29285454235, ACTIVE 0.001 sec fetching rows
mysql tables in use 3, locked 3
LOCK WAIT 6 lock struct(s), heap size 1184, 4 row lock(s)
MySQL thread id 6641616, OS thread handle 0x2b165c4b1700, query id 28190427937 10.103.180.86 test_ebs Searching rows for update
UPDATE test SET is_deleted = 1 WHERE group_id = 1332577 and test_id = 4580605
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 132 page no 37122 n bits 352 index `PRIMARY` of table `testdriver`.`test` trx id 29285454235 lock_mode X locks rec but not gap waiting
Record lock, heap no 179 PHYSICAL RECORD: n_fields 8; compact format; info bits 0
*** (2) TRANSACTION:
TRANSACTION 29285454237, ACTIVE 0.001 sec fetching rows, thread declared inside InnoDB 4980
mysql tables in use 3, locked 3
5 lock struct(s), heap size 1184, 3 row lock(s)
MySQL thread id 6639213, OS thread handle 0x2b1694cc2700, query id 28190427939 10.103.180.113 test_ebs Searching rows for update
UPDATE test SET is_deleted = 1 WHERE group_id = 1332577 and test_id = 4212859
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 132 page no 37122 n bits 352 index `PRIMARY` of table `testdriver`.`test` trx id 29285454237 lock_mode X locks rec but not gap
Record lock, heap no 179 PHYSICAL RECORD: n_fields 8; compact format;

表结构:
CREATE TABLE `test` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘主键’,
`test_id` bigint(20) DEFAULT NULL,
`group_id` bigint(20) DEFAULT NULL COMMENT ‘Id,对应test_group.id’,
`gmt_created` datetime DEFAULT NULL COMMENT ‘创建时间’,
`gmt_modified` datetime DEFAULT NULL COMMENT ‘修改时间’,
`is_deleted` tinyint(4) DEFAULT ‘0’ COMMENT ‘删除。’,
PRIMARY KEY (`id`),
KEY `idx_testid` (`test_id`),
KEY `idx_groupid` (`group_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7429111 ;

SQL执行计划:
mysql>explain UPDATE test SET is_deleted = 1 WHERE group_id = 1332577 and test_id = 4212859
+—-+————-+—————+————-+———————–+———————–+———+—–+——+———
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+—————+————-+———————–+———————–+———+—–+——+———
| 1 | SIMPLE | test | index_merge | idx_testid,idx_groupid | idx_testid,idx_groupid | 9,9 | | 1 | Using intersect(idx_testid,idx_groupid); Using where; Using temporary |
+—-+————-+—————+————-+———————–+———————–+———+—–+——+———

所以第一个事务先根据group_id索引,已经锁住primary id,然后再根据test_id索引,锁定primary id;
第二个事务先根据test_id索引,已经锁住primary id,然后再根据group_id索引,去锁primary id;
所以这样并发更新就可能出现死索引。

MySQL官方也已经确认了此bug:https://bugs.mysql.com/bug.php?id=77209

解决方法有两种:

第一、添加test_id+group_id的组合索引,这样就可以避免掉index merge;

第二、将优化器的index merge优化关闭;

建议选择第一种方法来避免此问题的发生。

时间: 2024-09-27 17:14:08

MySQL update use index merge(Using intersect) increase chances for deadlock的相关文章

MySQL优化器:index merge介绍

1. 什么是index merge MySQL优化器如果发现可以使用多个索引查找后的交集/并集定位数据,那么MySQL优化器就会尝试index merge这类访问方式.index merge主要分为两大类,多个索引交集访问(intersections),多个索引并集访问,当然这两类还可以组合出更为复杂的方式,例如多个交集后做并集. 1.1 index merge的限制:range优先 MySQL在5.6.7之前,使用index merge有一个重要的前提条件:没有range可以使用.这个限制降低

index merge的数据结构和成本评估

1. 概述:index merge的数据结构 index merge的主要数据结构仍然是存放在SEL_TREE中: class SEL_TREE :public Sql_alloc { ... List<SEL_IMERGE> merges; ... }; 在merges这个list中存放了所有可能的index merge.本文将从几个案例,来看看SEL_TREE/SEL_IMERGE如何代表一个index merge访问方式.本文将不再重复介绍SEL_ARG/SEL_TREE的Range相关

【MySQL】性能优化之 index merge (1)

一 序言介绍 MySQL 5.0 版本之前,每条个表在查询时 只能使用一个索引,有些不知道此功能限制的开发总是在一个表上创建很多单独列的索引,以便当where条件中含有这些列是能够走上索引.但是这样并不是一个好方法,或者是"好心办坏事",索引能够提供查询速度,但是也能给日常维护和IUD 操作带来维护成本. MySQL 5.0 和之后的版本推出了一个新特性---索引合并优化(Index merge optimization),它让MySQL可以在查询中对一个表使用多个索引,对它们同时扫描

index merge的补充说明

1. 为什么需要考虑Intersection 考虑如下查询: SELECT COUNT(*) FROM t1 WHERE key1=1 AND key2=1; 优化器可以考虑使用索引key1或者key2进行REF/Range访问,如果使用key1,那么key2=1则作为过滤条件.另外,优化器还会考虑使用Intersection,即同时使用索引key1和key2.这样做可能的好处是: (a) 如果两次索引扫描后做交集,如果最后ROWID很少,则回表次数大大减少 (b) 如果扫描这两个索引能是覆盖扫

Oracle中Index Merge与and_equal 的变迁

and_equal是Oracle支持的一种特定操作,可以将多个单列索引进行合并(Index Merge)输出查询结果. 同时and_equal可以通过Hints来进行强制,最少指定两个索引,最多指定5个. 以下的执行计划是常见的and_equal执行方式: SQL> select /*+ and_equal(t1 iu ii) */  username,password from t1 where username='EYGLE' and user_id=58; USERNAME        

mysql update 多表实例

  mysql update 多表实例讲解 我们先来看如下实例: 实例一: update tab1 set tab1.商品尺寸 = (select tab2.商品尺寸 from tab2 where tab2.商品颜色 = tab1.商品颜色) where tabl1.商品颜色 in (select tab2.商品颜色 from tab2) 继续如下实例: UPDATE CMS_Document A,CMS_Template B SET A.RuleType2=B.RuleType WHERE

mysql update select用法实例

  mysql update select用法实例 应该使用inner join,即: UPDATE friends INNER JOIN users ON friends.friendid=users.userid SET friends.friendname=users.username MySQL是通过临时表来实现FROM子句里面的嵌套查询,那么把嵌套查询装进另外一个嵌套查询里,可使FROM子句查询和保存都是在临时表里进行,然后间接地在外围查询被引用. 我们来看如下sql语句: updat

mysql update使用子查询

今天我像以前操作Oracle写了一个update sql: update device_user a set a.scene_id=null where a.id not in(select min(t.id) from device_user t group by t.device_id); 根据子查询的结果,更新表中的一个字段. 在mysql数据库中执行后报错: Error Code: 1093. You can't specify target table 'a' for update i

mysql update操作 出现incorrect datetime value

问题描述 mysql update操作 出现incorrect datetime value update iportal_wx.inviterelationshipcycle as A, (select * from iportal_wx.register where register_ctime<current_timestamp() AND register_ctime>ADDDATE(current_timestamp(),-1)) as B set A.IsAttentionHNLL