After executing
create table t (id int primary key auto_increment, COL1 int, key idx_a(COL1));
insert into t (COL1) values(5), (10), (11), (13), (20);
-- transaction 1
start transaction;
select * from t where COL1 = 13 for update;
The output of select * from performance_schema.data_locks is:
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| 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 | 140043377180872:1075:140043381460688 | 2368 | 49 | 180 | test | t | NULL | NULL | NULL | 140043381460688 | TABLE | IX | GRANTED | NULL |
| INNODB | 140043377180872:14:5:5:140043381457776 | 2368 | 49 | 180 | test | t | NULL | NULL | idx_a | 140043381457776 | RECORD | X | GRANTED | 13, 4 |
| INNODB | 140043377180872:14:4:5:140043381458120 | 2368 | 49 | 180 | test | t | NULL | NULL | PRIMARY | 140043381458120 | RECORD | X,REC_NOT_GAP | GRANTED | 4 |
| INNODB | 140043377180872:14:5:6:140043381458464 | 2368 | 49 | 180 | test | t | NULL | NULL | idx_a | 140043381458464 | RECORD | X,GAP | GRANTED | 20, 5 |
+--------+----------------------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
Transaction 1 is holding next-key lock ((11, 3), (13, 4)] and gap lock ((13, 4), (20, 5)).
insert into t (COL1) values(10) and insert into t (COL1) values(20) is equal to insert into t (COL1, id) values(10, ?) and ? must be greater than 5, so both (10, ?) and (20, ?) are not in ((11, 3), (13, 4)] or ((13, 4), (20, 5)), that's why they can succeed. insert into t (COL1) values(11) to insert into t (COL1) values(19), they are in ((11, 3), (13, 4)] or ((13, 4), (20, 5)), that's why they are blocked.
An update is like deletion and then insertion. update t set COL1 = 11 where COL1 = 10 will insert (11, 2), (11, 2) is not in ((11, 3), (13, 4)] or ((13, 4), (20, 5)), that's why it succeed. update t set COL1 = 12 where COL1 = 10 to update t set COL1 = 20 where COL1 = 10 will insert (?, 2) and ? is in [12, 20], so (?, 2) is in ((11, 3), (13, 4)] or ((13, 4), (20, 5)), that's why they are blocked. I think update t set a = 21 where a = 10 should be update t set COL1 = 21 where COL1 = 10, it will insert (21, 2), (21, 2) is not in ((11, 3), (13, 4)] or ((13, 4), (20, 5)), that's why it succeed.