一、索引合并的诞生
MySQL的索引合并是一种查询优化技术,它允许优化器使用多个索引来改善对单个表的查询性能。在这种情况下,MySQL将使用多个索引来检索行,然后通过行ID合并结果。
- MySQL5.0之前,一个表一次只能使用一个索引,无法同时使用多个索引分别进行条件扫描。
- MySQL5.1开始,引入了 index merge 优化技术,对同一个表可以使用多个索引分别进行条件扫描。
二、索引合并的问题
虽然这个特性可以提高某些查询的效率,但它也可能带来一些问题,包括性能下降和死锁。
- 性能问题:在某些情况下,索引合并可能不如使用单个复合索引高效。这是因为索引合并涉及多个索引查找和行ID的合并,这可能比直接通过一个更适合的索引进行查找更耗时。
- 查询优化器的选择:MySQL的查询优化器可能错误地选择了索引合并,而不是单个更有效的索引。这种选择可能导致查询性能下降。
- 资源消耗:索引合并可能导致更高的CPU和内存消耗,尤其是在处理大量数据时。
- 锁竞争:在使用事务的数据库中,索引合并可能增加锁竞争,特别是在高并发环境下。
三、索引合并死锁原因
- 复杂的锁定模式:当使用索引合并时,MySQL可能会锁定多个索引涉及的行。这可能导致比单个索引使用时更复杂的锁定模式。
- 锁定顺序不一致:不同的事务可能以不同的顺序获取多个索引上的锁,增加了死锁的风险。
- 增加的行锁数量:索引合并可能涉及更多的行锁,尤其是在范围查询或多个索引覆盖大量行时。
四、索引合并死锁举例
table表中有两个索引uniq_trans_id 和 idx_status。通过SQL更新字段 update table set status = 1 where status = 0 and trans_id in (xxx1, xxx2);
产生死锁的流程如下:
五、索引合并死锁解决
一、从代码层面解决
- where 查询条件中,只传 trans_id ,将数据查询出来后,在代码层面判断 status 状态是否为0;
- where 查询条件后边直接用 id 字段,通过主键去更新。
二、从MySQL层面解决
- 删除 idx_status 索引或者建一个包含这俩列的联合索引;
- 将MySQL优化器的index merge优化关闭。
- 使用 force index(uniq_trans_id) 强制查询语句使用 uniq_trans_id 索引;