Description:
when use index_merge optimizer there will be hold unnecessary row locks,increases the probability of deadlock or blocking
How to repeat:
CREATE TABLE `testsemi` (
`id1` int NOT NULL,
`id2` int NOT NULL,
`name` varchar(10) DEFAULT NULL,
`z` varchar(10) DEFAULT NULL,
`dd` date DEFAULT NULL,
PRIMARY KEY (`id1`,`id2`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=uontf8mb4_0900_ai_ci
;
set transaction_isolation='READ-COMMITTED'
set optimizer_switch='index_merge=on';
insert into testsemi(id1,id2,dd) values(1,1,'2023-05-31');
run next sql 15 times:
insert into testsemi(id1,id2,dd) select id1,id2+(select count(*) from testsemi),dd from testsemi;
update testsemi set name='abc' limit 10;
update testsemi set dd='2023-05-30' limit 5;
select * from testsemi limit 20;
+-----+-----+------+------+------------+
| id1 | id2 | name | z | dd |
+-----+-----+------+------+------------+
| 1 | 1 | abc | NULL | 2023-05-30 |
| 1 | 2 | abc | NULL | 2023-05-30 |
| 1 | 3 | abc | NULL | 2023-05-30 |
| 1 | 4 | abc | NULL | 2023-05-30 |
| 1 | 5 | abc | NULL | 2023-05-30 |
| 1 | 6 | abc | NULL | 2023-05-31 |
| 1 | 7 | abc | NULL | 2023-05-31 |
| 1 | 8 | abc | NULL | 2023-05-31 |
| 1 | 9 | abc | NULL | 2023-05-31 |
| 1 | 10 | abc | NULL | 2023-05-31 |
| 1 | 11 | NULL | NULL | 2023-05-31 |
| 1 | 12 | NULL | NULL | 2023-05-31 |
| 1 | 13 | NULL | NULL | 2023-05-31 |
| 1 | 14 | NULL | NULL | 2023-05-31 |
| 1 | 15 | NULL | NULL | 2023-05-31 |
| 1 | 16 | NULL | NULL | 2023-05-31 |
| 1 | 17 | NULL | NULL | 2023-05-31 |
| 1 | 18 | NULL | NULL | 2023-05-31 |
| 1 | 19 | NULL | NULL | 2023-05-31 |
| 1 | 20 | NULL | NULL | 2023-05-31 |
+-----+-----+------+------+------------+
20 rows in set (0.00 sec)
explain update testsemi set z='test' where id1=1 and name='abc' and dd='2023-05-31';
+----+-------------+----------+------------+-------------+---------------+--------------+---------+------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------------+---------------+--------------+---------+------+------+----------+--------------------------------------------+
| 1 | UPDATE | testsemi | NULL | index_merge | PRIMARY,name | name,PRIMARY | 47,4 | NULL | 5 | 100.00 | Using intersect(name,PRIMARY); Using where |
+----+-------------+----------+------------+-------------+---------------+--------------+---------+------+------+----------+--------------------------------------------+
1 row in set, 1 warning (0.00 sec)
begin;
update testsemi set z='test' where id1=1 and nam='abc' and dd='2023-05-31'
expect for 10 row locks(id2 between 6 and 10 should be release),but there is hold 20 row locks
select * from performance_schema.data_locks;
--------+---------------+-------------+--------------+
| ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+--------------+
| INNODB | 139875127880920:1069:139875139505856 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | NULL | 139875139505856 | TABLE | IX | GRANTED | NULL |
| INNODB | 139875127880920:7:194:2:139875139502800 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139502800 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 1 |
| INNODB | 139875127880920:7:6:2:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 1 |
| INNODB | 139875127880920:7:6:3:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 2 |
| INNODB | 139875127880920:7:6:4:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 3 |
| INNODB | 139875127880920:7:6:5:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 4 |
| INNODB | 139875127880920:7:6:6:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 5 |
| INNODB | 139875127880920:7:6:7:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 6 |
| INNODB | 139875127880920:7:6:8:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 7 |
| INNODB | 139875127880920:7:6:9:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 8 |
| INNODB | 139875127880920:7:6:10:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 9 |
| INNODB | 139875127880920:7:6:11:139875139503144 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | PRIMARY | 139875139503144 | RECORD | X,REC_NOT_GAP | GRANTED | 1, 10 |
| INNODB | 139875127880920:7:12:2:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 2 |
| INNODB | 139875127880920:7:12:3:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 3 |
| INNODB | 139875127880920:7:12:4:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 4 |
| INNODB | 139875127880920:7:12:5:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 5 |
| INNODB | 139875127880920:7:12:6:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 6 |
| INNODB | 139875127880920:7:12:7:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 7 |
| INNODB | 139875127880920:7:12:8:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 8 |
| INNODB | 139875127880920:7:12:9:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 9 |
| INNODB | 139875127880920:7:12:10:139875139503488 | 1519 | 370 | 161 | test | zz1 | NULL | NULL | name | 139875139503488 | RECORD | X,REC_NOT_GAP | GRANTED | 'abc', 1, 10 |
+--------+-----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+--------------+
21 rows in set (0.00 sec)
if set optimizer_switch='index_merge=off' or force index (name), the returned result will be 10 row locks as expected.
begin;
update testsemi set z='test' where id1=1 and name='abc' and dd='2023-05-31'
or
update testsemi force index(name) set z='test' where id1=1 and name='abc' and dd='2023-05-31'
select * from performance_schema.data_locks;
Suggested fix:
release unnecessary locks when use index_merge