mysql自动合并索引(index merge)查询导致死锁问题

algorain

mysql自动合并索引(index merge)查询导致死锁问题 虽然在生产环境上加了分布式锁,但还是会出现某一个事务未结束,而下一个事务进入来修改数据,这时就会陷入等待,最后等待超时,事务进行了回滚,在运行几个月后第一次出现这种情况,发生死锁的是两条update语句,当sql语句的where语句中使用两个索引时,mysql的优化器可能会对这两个索引进行合并,使用explain分析会显示Using intersect(index1,index2); 表示将index1和index2合并来查询。该表中只有index1,index2两个索引。

1
UPDATE a SET a.data = a.data - 1  WHERE  index1_id = xxx AND index2_id = xxx

首先出现这种情况是由于前一个事务一直没有释放所占用的资源,导致第二个事务无法获取资源,但其实两个事务之间在业务上没有关系,也不应该出现冲突,通过explain分析在type字段出现index_merge,表示对全表进行了查询,可能就是在将index1和index2索引取交集的时候锁定了index2的表,导致其他事务无法进行。 可以使用show engine innodb status\G;来查看最近一次的死锁信息。 至于mysql优化器为什么要合并索引,优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句。在数据库里面,扫描行数是影响执行代价的因素之一。扫描的行数越少,意味着访问磁盘数据的次数越少,消耗的 CPU 资源越少,扫描行数并不是唯一的判断标准,优化器还会结合是否使用临时表、是否排序等因素进行综合判断。我们可以通过explain看一下扫描行数。 执行原SQL语句,扫描行数为1,因为优化器觉得通过两个索引可以唯一确定数据

1
2
3
4
5
+----+-------------+----------+------------+-------------+---------------------------------+---------------------------------+---------+------+------+----------+---------------------------------------------------------------+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
+----+-------------+----------+------------+-------------+---------------------------------+---------------------------------+---------+------+------+----------+---------------------------------------------------------------+
1 UPDATE a NULL index_merge index1,index2 index1,index2 4,4 NULL 1 100.00 Using intersect(index1,index2); Using where
+----+-------------+----------+------------+-------------+---------------------------------+---------------------------------+---------+------+------+----------+---------------------------------------------------------------+

使用force index() 来分别指定索引,先使用force index(index1) ,扫描行数为3

1
2
3
4
5
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+
1 UPDATE recharge NULL range index1 index1 4 const 3 100.00 Using where
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+

使用force index(index2) ,扫描行数为114

1
2
3
4
5
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+
1 UPDATE recharge NULL range index2 index2 4 const 114 100.00 Using where
+----+-------------+----------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+

所以优化器合并两个索引是有道理的,但没想到会造成其他事务的阻塞

解决方案:

  1. 在SQL语句使用force index()来指定要使用的索引,但在实际开发中不太方便
  2. 将优化器的index merge优化关闭
  3. 删除掉index2或者index1的索引,这样也可以解决问题,让优化器只能使用一个索引
  4. 添加一个联合索引,包含index1+index2的,优化器会直接使用这个索引

添加合理的索引是最合适的办法。

  • Title: mysql自动合并索引(index merge)查询导致死锁问题
  • Author: algorain
  • Created at: 2021-03-17 16:30:56
  • Updated at: 2023-05-14 21:39:50
  • Link: http://www.rain1024.com/2021/03/17/mysql自动合并索引index-merge查询导致死锁问题/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments
On this page
mysql自动合并索引(index merge)查询导致死锁问题