===============
== CyberJunk ==
===============
Made with 😭

Through an Equality Query on a Record What Locks Will Be Added

Database

In the example table, id is the primary key, create a normal index on field age, and a unique index on name.

mysql> show create table test\G;
Create Table: CREATE TABLE `test` (
  `id` int NOT NULL,
  `age` int DEFAULT NULL,
  `msg` varchar(255) DEFAULT NULL,
  `name` char(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`),
  KEY `idx_age` (`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

mysql> select * from test;  
+----+------+------+------+  
| id | age  | msg  | name |  
+----+------+------+------+  
|  1 |   10 | NULL | a    |  
|  3 |   20 | NULL | NULL |  
|  4 |   21 | NULL | NULL |  
+----+------+------+------+

Observe the locking situation under the RR level.

1. Updating records through the primary key unique index

Check the execution plan; it uses the primary index.

mysql> explain update test set msg='A' where id=1;  
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+  
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |  
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+  
|  1 | UPDATE      | test  | NULL       | range | PRIMARY       | **PRIMARY** | 4       | const |    1 |   100.00 | Using where |  
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+

1.1 If the record exists

Check the lock information; there is an intention lock and a record lock on the primary key (the unique index degrades the next-key lock to a record lock):

mysql> update test set msg='A' where id=1;
Query OK, 1 row affected (0.00 sec)  
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from performance_schema.data_locks\G;  
*************************** 1. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:1115:128238328283648  
ENGINE_TRANSACTION_ID: 28995  
           THREAD_ID: 53  
            EVENT_ID: 48  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: NULL  
OBJECT_INSTANCE_BEGIN: 128238328283648  
           LOCK_TYPE: TABLE  
           LOCK_MODE: IX  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: NULL  
*************************** 2. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:49:4:7:128238328280736  
ENGINE_TRANSACTION_ID: 28995  
           THREAD_ID: 53  
            EVENT_ID: 48  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: PRIMARY  
OBJECT_INSTANCE_BEGIN: 128238328280736  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X,REC_NOT_GAP  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 1  
2 rows in set (0.00 sec)

1.2 if the record does not exist

The unique index degrades the next-key lock to a gap lock.

mysql> update test set msg='A' where id=2;  
Query OK, 0 rows affected (0.00 sec)  
Rows matched: 0  Changed: 0  Warnings: 0

mysql> select * from performance_schema.data_locks\G;  
*************************** 1. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:1115:128238328283648  
ENGINE_TRANSACTION_ID: 28997  
           THREAD_ID: 53  
            EVENT_ID: 52  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: NULL  
OBJECT_INSTANCE_BEGIN: 128238328283648  
           LOCK_TYPE: TABLE  
           LOCK_MODE: IX  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: NULL  
*************************** 2. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:49:4:3:128238328280736  
ENGINE_TRANSACTION_ID: 28997  
           THREAD_ID: 53  
            EVENT_ID: 52  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: PRIMARY  
OBJECT_INSTANCE_BEGIN: 128238328280736  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X,GAP  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 3  
2 rows in set (0.01 sec)

There is a detail to note: if a query like where id=5 makes LOCK_DATA as +∞, LOCK_MODE will only show X.

            LOCK_TYPE: RECORD
            LOCK_MODE: X
          LOCK_STATUS: GRANTED
            LOCK_DATA: supremum pseudo-record

Reference: One thing to note here is that seeing lock_mode X in the log does not necessarily mean this is a Next-key lock, because there is one exception.

1.3 Updating the primary key itself

Unlike in 1.1, where other fields are updated, when updating the primary key itself, all indexes need to be updated.

mysql> update test set id=2 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from performance_schema.data_locks\G;
*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 135950501940440:1117:135950411813584
ENGINE_TRANSACTION_ID: 1396778
            THREAD_ID: 48
             EVENT_ID: 63
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 135950411813584
            LOCK_TYPE: TABLE
            LOCK_MODE: IX
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 135950501940440:51:4:7:135950411810592
ENGINE_TRANSACTION_ID: 1396778
            THREAD_ID: 48
             EVENT_ID: 63
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 135950411810592
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 1
*************************** 3. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 135950501940440:51:5:4:135950411810936
ENGINE_TRANSACTION_ID: 1396778
            THREAD_ID: 48
             EVENT_ID: 63
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 135950411810936
            LOCK_TYPE: RECORD
            LOCK_MODE: X,REC_NOT_GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 'a         ', 1
*************************** 4. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 135950501940440:51:5:4:135950411811280
ENGINE_TRANSACTION_ID: 1396778
            THREAD_ID: 48
             EVENT_ID: 63
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 135950411811280
            LOCK_TYPE: RECORD
            LOCK_MODE: S,GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 'a         ', 1
*************************** 5. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 135950501940440:51:5:5:135950411811280
ENGINE_TRANSACTION_ID: 1396778
            THREAD_ID: 48
             EVENT_ID: 63
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 135950411811280
            LOCK_TYPE: RECORD
            LOCK_MODE: S,GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 'a         ', 2
*************************** 6. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 135950501940440:51:5:1:135950411811624
ENGINE_TRANSACTION_ID: 1396778
            THREAD_ID: 48
             EVENT_ID: 63
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 135950411811624
            LOCK_TYPE: RECORD
            LOCK_MODE: S
          LOCK_STATUS: GRANTED
            LOCK_DATA: supremum pseudo-record
6 rows in set (0.01 sec)

MySQL updates by deleting first and then inserting, so it is also necessary to be aware of the potential deadlocks that may occur during these two stages. ref

2. Updating records through a unique non-primary key index

Check the execution plan; it uses the name index.

  
mysql> explain update test set msg='A' where name='a';  
+----+-------------+-------+------------+-------+-------------  
--+------+---------+-------+------+----------+-------------+  
| id | select_type | table | partitions | type  | possible_key  
s | key  | key_len | ref   | rows | filtered | Extra       |  
+----+-------------+-------+------------+-------+-------------  
--+------+---------+-------+------+----------+-------------+  
|  1 | UPDATE      | test  | NULL       | range | name          
 | name | 41      | const |    1 |   100.00 | Using where |  
+----+-------------+-------+------------+-------+-------------  
--+------+---------+-------+------+----------+-------------+  
1 row in set, 1 warning (0.00 sec)

2.1 Record exists

Compared to situation 1.1, there is one more lock: a record lock on the name index.

mysql> update test set msg='A' where name='a';     
Query OK, 0 rows affected (0.00 sec)  
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from performance_schema.data_locks\G;  
*************************** 1. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:1116:128238328283648  
ENGINE_TRANSACTION_ID: 29064  
           THREAD_ID: 53  
            EVENT_ID: 153  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: NULL  
OBJECT_INSTANCE_BEGIN: 128238328283648  
           LOCK_TYPE: TABLE  
           LOCK_MODE: IX  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: NULL  
*************************** 2. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:50:5:5:128238328280736  
ENGINE_TRANSACTION_ID: 29064  
           THREAD_ID: 53  
            EVENT_ID: 153  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: name  
OBJECT_INSTANCE_BEGIN: 128238328280736  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X,REC_NOT_GAP  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 'a         ', 1  
*************************** 3. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:50:4:7:128238328281080  
ENGINE_TRANSACTION_ID: 29064  
           THREAD_ID: 53  
            EVENT_ID: 153  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: PRIMARY  
OBJECT_INSTANCE_BEGIN: 128238328281080  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X,REC_NOT_GAP  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 1  
3 rows in set (0.01 sec)

2.2 Record does not exist

Trying to find a record with name=0. A gap lock is added on the unique name index. This situation is the same as in 1.2.

mysql> update test set msg='A' where name='0';  
Query OK, 0 rows affected (0.00 sec)  
Rows matched: 0  Changed: 0  Warnings: 0

mysql> select * from performance_schema.data_locks\G;  
*************************** 1. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:1116:128238328283648  
ENGINE_TRANSACTION_ID: 29061  
           THREAD_ID: 53  
            EVENT_ID: 140  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: NULL  
OBJECT_INSTANCE_BEGIN: 128238328283648  
           LOCK_TYPE: TABLE  
           LOCK_MODE: IX  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: NULL  
*************************** 2. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:50:5:5:128238328280736  
ENGINE_TRANSACTION_ID: 29061  
           THREAD_ID: 53  
            EVENT_ID: 140  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: name  
OBJECT_INSTANCE_BEGIN: 128238328280736  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X,GAP  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 'a         ', 1  
2 rows in set (0.00 sec)

3. Updating records through a non-unique index

mysql> explain update test set msg="A" where age=10;  
+----+-------------+-------+------------+-------+-------------  
--+---------+---------+-------+------+----------+-------------  
+  
| id | select_type | table | partitions | type  | possible_key  
s | key     | key_len | ref   | rows | filtered | Extra         
|  
+----+-------------+-------+------------+-------+-------------  
--+---------+---------+-------+------+----------+-------------  
+  
|  1 | UPDATE      | test  | NULL       | range | idx_age       
 | idx_age | 5       | const |    1 |   100.00 | Using where  
|  
+----+-------------+-------+------------+-------+-------------  
--+---------+---------+-------+------+----------+-------------  
+  
1 row in set, 1 warning (0.00 sec)

3.1 Record exists

There are a total of four locks: the intention lock and the record lock on the primary key (row 1,3), row 2 is the next-key lock on the found record, and row 4 is the gap lock before the first mismatched record.

mysql> update test set msg="A" where age=10;  
Query OK, 0 rows affected (0.01 sec)  
Rows matched: 1  Changed: 0  Warnings: 0

mysql> select * from performance_schema.data_locks\G;  
*************************** 1. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:1117:128238328283648  
ENGINE_TRANSACTION_ID: 29089  
           THREAD_ID: 53  
            EVENT_ID: 168  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: NULL  
OBJECT_INSTANCE_BEGIN: 128238328283648  
           LOCK_TYPE: TABLE  
           LOCK_MODE: IX  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: NULL  
*************************** 2. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:51:6:2:128238328280736  
ENGINE_TRANSACTION_ID: 29089  
           THREAD_ID: 53  
            EVENT_ID: 168  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: idx_age  
OBJECT_INSTANCE_BEGIN: 128238328280736  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 10, 1  
*************************** 3. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:51:4:2:128238328281080  
ENGINE_TRANSACTION_ID: 29089  
           THREAD_ID: 53  
            EVENT_ID: 168  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: PRIMARY  
OBJECT_INSTANCE_BEGIN: 128238328281080  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X,REC_NOT_GAP  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 1  
*************************** 4. row ***************************  
              ENGINE: INNODB  
      ENGINE_LOCK_ID: 128238397428520:51:6:3:128238328281424  
ENGINE_TRANSACTION_ID: 29089  
           THREAD_ID: 53  
            EVENT_ID: 168  
       OBJECT_SCHEMA: t  
         OBJECT_NAME: test  
      PARTITION_NAME: NULL  
   SUBPARTITION_NAME: NULL  
          INDEX_NAME: idx_age  
OBJECT_INSTANCE_BEGIN: 128238328281424  
           LOCK_TYPE: RECORD  
           LOCK_MODE: X,GAP  
         LOCK_STATUS: GRANTED  
           LOCK_DATA: 20, 3  
4 rows in set (0.00 sec)

3.2 Record does not exist

Only the gap lock on that index.

mysql> update test set msg="A" where age=15;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

mysql> select * from performance_schema.data_locks\G;
*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 128238397428520:1117:128238328283648
ENGINE_TRANSACTION_ID: 29090
            THREAD_ID: 53
             EVENT_ID: 172
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 128238328283648
            LOCK_TYPE: TABLE
            LOCK_MODE: IX
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 128238397428520:51:6:3:128238328280736
ENGINE_TRANSACTION_ID: 29090
            THREAD_ID: 53
             EVENT_ID: 172
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: idx_age
OBJECT_INSTANCE_BEGIN: 128238328280736
            LOCK_TYPE: RECORD
            LOCK_MODE: X,GAP
          LOCK_STATUS: GRANTED
            LOCK_DATA: 20, 3
2 rows in set (0.00 sec)

4. Updating records without using an index

The whole table is scanned.

mysql> explain update test set age=age+100 where msg is NULL;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | UPDATE      | test  | NULL       | index | NULL          | PRIMARY | 4       | NULL |    3 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

Locks all rows in the table:

mysql> select * from performance_schema.data_locks\G;
*************************** 1. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 128238397428520:1117:128238328283648
ENGINE_TRANSACTION_ID: 29093
            THREAD_ID: 53
             EVENT_ID: 186
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 128238328283648
            LOCK_TYPE: TABLE
            LOCK_MODE: IX
          LOCK_STATUS: GRANTED
            LOCK_DATA: NULL
*************************** 2. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 128238397428520:51:4:1:128238328280736
ENGINE_TRANSACTION_ID: 29093
            THREAD_ID: 53
             EVENT_ID: 186
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 128238328280736
            LOCK_TYPE: RECORD
            LOCK_MODE: X
          LOCK_STATUS: GRANTED
            LOCK_DATA: supremum pseudo-record
*************************** 3. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 128238397428520:51:4:2:128238328280736
ENGINE_TRANSACTION_ID: 29093
            THREAD_ID: 53
             EVENT_ID: 186
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 128238328280736
            LOCK_TYPE: RECORD
            LOCK_MODE: X
          LOCK_STATUS: GRANTED
            LOCK_DATA: 1
*************************** 4. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 128238397428520:51:4:5:128238328280736
ENGINE_TRANSACTION_ID: 29093
            THREAD_ID: 53
             EVENT_ID: 186
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 128238328280736
            LOCK_TYPE: RECORD
            LOCK_MODE: X
          LOCK_STATUS: GRANTED
            LOCK_DATA: 3
*************************** 5. row ***************************
               ENGINE: INNODB
       ENGINE_LOCK_ID: 128238397428520:51:4:6:128238328280736
ENGINE_TRANSACTION_ID: 29093
            THREAD_ID: 53
             EVENT_ID: 186
        OBJECT_SCHEMA: t
          OBJECT_NAME: test
       PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
           INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 128238328280736
            LOCK_TYPE: RECORD
            LOCK_MODE: X
          LOCK_STATUS: GRANTED
            LOCK_DATA: 4
5 rows in set (0.00 sec)