参考文献

加锁实战分析

  • 对于唯一键值的锁定,Next-Key Lock降级为Record Lock仅存在于查询所有的唯一索引列.若唯一索引由多个列组成,而查询仅是查找多个唯一索引列中的其中一个,那么查询其实是range类型,而不是point类型,因此InnoDB存储引擎依然会使用Next-Key Lock进行锁定.

    操作序号 SessionA SessionB
    1 begin;
    2 select * from t where id=5 for update;
    3 begin;
    4 insert into t select 4;
    5 commit;#成功,不需要等待
    6 commit;
  • 若是辅助索引,则情况会完全不同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    mysql> show variables like '%transaction_isolation%';
    +-----------------------+-----------------+
    | Variable_name | Value |
    +-----------------------+-----------------+
    | transaction_isolation | REPEATABLE-READ |
    +-----------------------+-----------------+
    1 row in set (0.01 sec)


    create table z(a int ,b int,primary
    key(a),key(b));
    insert into z select 1,1;
    insert into z select 3,1;
    insert into z select 5,3;
    insert into z select 7,6;
    insert into z select 10,8;
    • 表z的列b是辅助索引,若在SessionA中执行下面SQL

      1
      select * from z where b=3 for update;
    • 观察PERFORMANCE_SCHEMA.DATA_LOCKS表信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
      *************************** 1. row ***************************
      ENGINE: INNODB
      ENGINE_LOCK_ID: 4566258352:5769:5039511720
      ENGINE_TRANSACTION_ID: 507987
      THREAD_ID: 55
      EVENT_ID: 143
      OBJECT_SCHEMA: dicom
      OBJECT_NAME: z
      PARTITION_NAME: NULL
      SUBPARTITION_NAME: NULL
      INDEX_NAME: NULL
      OBJECT_INSTANCE_BEGIN: 5039511720
      LOCK_TYPE: TABLE
      LOCK_MODE: IX
      LOCK_STATUS: GRANTED
      LOCK_DATA: NULL
      *************************** 2. row ***************************
      ENGINE: INNODB
      ENGINE_LOCK_ID: 4566258352:4707:5:4:5042258968
      ENGINE_TRANSACTION_ID: 507987
      THREAD_ID: 55
      EVENT_ID: 143
      OBJECT_SCHEMA: dicom
      OBJECT_NAME: z
      PARTITION_NAME: NULL
      SUBPARTITION_NAME: NULL
      INDEX_NAME: b
      OBJECT_INSTANCE_BEGIN: 5042258968
      LOCK_TYPE: RECORD
      LOCK_MODE: X
      LOCK_STATUS: GRANTED
      LOCK_DATA: 3, 5
      *************************** 3. row ***************************
      ENGINE: INNODB
      ENGINE_LOCK_ID: 4566258352:4707:4:4:5042259312
      ENGINE_TRANSACTION_ID: 507987
      THREAD_ID: 55
      EVENT_ID: 143
      OBJECT_SCHEMA: dicom
      OBJECT_NAME: z
      PARTITION_NAME: NULL
      SUBPARTITION_NAME: NULL
      INDEX_NAME: PRIMARY
      OBJECT_INSTANCE_BEGIN: 5042259312
      LOCK_TYPE: RECORD
      LOCK_MODE: X,REC_NOT_GAP
      LOCK_STATUS: GRANTED
      LOCK_DATA: 5
      *************************** 4. row ***************************
      ENGINE: INNODB
      ENGINE_LOCK_ID: 4566258352:4707:5:5:5042259656
      ENGINE_TRANSACTION_ID: 507987
      THREAD_ID: 55
      EVENT_ID: 143
      OBJECT_SCHEMA: dicom
      OBJECT_NAME: z
      PARTITION_NAME: NULL
      SUBPARTITION_NAME: NULL
      INDEX_NAME: b
      OBJECT_INSTANCE_BEGIN: 5042259656
      LOCK_TYPE: RECORD
      LOCK_MODE: X,GAP
      LOCK_STATUS: GRANTED
      LOCK_DATA: 6, 7
      4 rows in set (0.01 sec)
      • 可以看到首先对辅助索引值为3这条记录加上了Next-Key Lock(即锁定范围(1,3]),然后辅助索引对应的主键索引加上了Record Lock(即锁定a=5的聚簇索引),最后对辅助索引值为3的下一个键值加上Gap Lock(即锁定范围(3,6))

MySQL中SELECT...FOR UPDATE加锁分析

隔离级别与准备工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.03 sec)

mysql> show create table z;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| z | CREATE TABLE `z` (
`a` int NOT NULL,
`b` int DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

mysql> select * from z;
+---+------+
| a | b |
+---+------+
| 1 | 2 |
| 3 | 3 |
| 5 | 5 |
| 9 | 10 |
+---+------+
4 rows in set (0.00 sec)
  • 明确指定主键并且数据存在,在查询到的数据上加Record Lock

    img

  • 明确指定主键但数据不存在,

    • 条件小于当前索引值最小值,在索引最小值上加Gap Lock

      img

    • 条件在当前索引值区间内,在该区间的右边界索引值上加Gap Lock

      img

    • 条件大于当前索引值最大值,在supremum pseudo-record伪记录上加上Next-Key Lock

      img

  • 不使用主键(不管查询的数据存不存在),加表锁,由下方data_locks表中的信息看出,MySQL对每个主键索引都加上了Next-Key Lock

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)

    mysql> select * from z where b = 2 for update;
    +---+------+
    | a | b |
    +---+------+
    | 1 | 2 |
    +---+------+
    1 row in set (0.00 sec)

    mysql> select * from performance_schema.data_locks\G
    *************************** 1. row ***************************
    ENGINE: INNODB
    ENGINE_LOCK_ID: 5407970096:1065:5518680984
    ENGINE_TRANSACTION_ID: 6973
    THREAD_ID: 48
    EVENT_ID: 125
    OBJECT_SCHEMA: debug
    OBJECT_NAME: z
    PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
    INDEX_NAME: NULL
    OBJECT_INSTANCE_BEGIN: 5518680984
    LOCK_TYPE: TABLE
    LOCK_MODE: IX
    LOCK_STATUS: GRANTED
    LOCK_DATA: NULL
    *************************** 2. row ***************************
    ENGINE: INNODB
    ENGINE_LOCK_ID: 5407970096:4:4:1:5528285720
    ENGINE_TRANSACTION_ID: 6973
    THREAD_ID: 48
    EVENT_ID: 125
    OBJECT_SCHEMA: debug
    OBJECT_NAME: z
    PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
    INDEX_NAME: PRIMARY
    OBJECT_INSTANCE_BEGIN: 5528285720
    LOCK_TYPE: RECORD
    LOCK_MODE: X
    LOCK_STATUS: GRANTED
    LOCK_DATA: supremum pseudo-record
    *************************** 3. row ***************************
    ENGINE: INNODB
    ENGINE_LOCK_ID: 5407970096:4:4:2:5528285720
    ENGINE_TRANSACTION_ID: 6973
    THREAD_ID: 48
    EVENT_ID: 125
    OBJECT_SCHEMA: debug
    OBJECT_NAME: z
    PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
    INDEX_NAME: PRIMARY
    OBJECT_INSTANCE_BEGIN: 5528285720
    LOCK_TYPE: RECORD
    LOCK_MODE: X
    LOCK_STATUS: GRANTED
    LOCK_DATA: 1
    *************************** 4. row ***************************
    ENGINE: INNODB
    ENGINE_LOCK_ID: 5407970096:4:4:4:5528285720
    ENGINE_TRANSACTION_ID: 6973
    THREAD_ID: 48
    EVENT_ID: 125
    OBJECT_SCHEMA: debug
    OBJECT_NAME: z
    PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
    INDEX_NAME: PRIMARY
    OBJECT_INSTANCE_BEGIN: 5528285720
    LOCK_TYPE: RECORD
    LOCK_MODE: X
    LOCK_STATUS: GRANTED
    LOCK_DATA: 3
    *************************** 5. row ***************************
    ENGINE: INNODB
    ENGINE_LOCK_ID: 5407970096:4:4:5:5528285720
    ENGINE_TRANSACTION_ID: 6973
    THREAD_ID: 48
    EVENT_ID: 125
    OBJECT_SCHEMA: debug
    OBJECT_NAME: z
    PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
    INDEX_NAME: PRIMARY
    OBJECT_INSTANCE_BEGIN: 5528285720
    LOCK_TYPE: RECORD
    LOCK_MODE: X
    LOCK_STATUS: GRANTED
    LOCK_DATA: 9
    *************************** 6. row ***************************
    ENGINE: INNODB
    ENGINE_LOCK_ID: 5407970096:4:4:6:5528285720
    ENGINE_TRANSACTION_ID: 6973
    THREAD_ID: 48
    EVENT_ID: 125
    OBJECT_SCHEMA: debug
    OBJECT_NAME: z
    PARTITION_NAME: NULL
    SUBPARTITION_NAME: NULL
    INDEX_NAME: PRIMARY
    OBJECT_INSTANCE_BEGIN: 5528285720
    LOCK_TYPE: RECORD
    LOCK_MODE: X
    LOCK_STATUS: GRANTED
    LOCK_DATA: 5
    6 rows in set (0.01 sec)

MySQL简单 SQL 的加锁分析

  • MySQL版本

    1
    2
    3
    4
    5
    6
    7
    mysql> select version();
    +--------------+
    | version() |
    +--------------+
    | 8.0.27-debug |
    +--------------+
    1 row in set (0.01 sec)
  • RR与RC下进行对比

  • 前置准备

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     CREATE TABLE `s` (
    `id` int NOT NULL AUTO_INCREMENT,
    `no` varchar(10) NOT NULL,
    `name` varchar(64) NOT NULL,
    `age` int NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `no` (`no`),
    Key `name` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

    INSERT INTO `s` (`id`, `no`, `name`, `age`) VALUES (15, 'S0001', 'Bob', 25);
    INSERT INTO `s` (`id`, `no`, `name`, `age`) VALUES (18, 'S0002', 'Alice', 24);
    INSERT INTO `s` (`id`, `no`, `name`, `age`) VALUES (20, 'S0004', 'Jim', 24);
    INSERT INTO `s` (`id`, `no`, `name`, `age`) VALUES (30, 'S0005', 'Eric', 23);
    INSERT INTO `s` (`id`, `no`, `name`, `age`) VALUES (37, 'S0006', 'Tom', 22);
    INSERT INTO `s` (`id`, `no`, `name`, `age`) VALUES (49, 'S0008', 'Tom', 25);
    INSERT INTO `s` (`id`, `no`, `name`, `age`) VALUES (50, 'S0017', 'Rose', 23);
聚簇索引,查询命中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update s set age = 20 where id = 15;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204643680:1066:4771072680
ENGINE_TRANSACTION_ID: 7468
THREAD_ID: 51
EVENT_ID: 24
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4771072680
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204643680:5:4:2:4773632024
ENGINE_TRANSACTION_ID: 7468
THREAD_ID: 51
EVENT_ID: 24
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 4773632024
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
2 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204644656:1066:4771073768
ENGINE_TRANSACTION_ID: 7470
THREAD_ID: 52
EVENT_ID: 23
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4771073768
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204644656:5:4:2:4773636632
ENGINE_TRANSACTION_ID: 7470
THREAD_ID: 52
EVENT_ID: 23
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 4773636632
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
2 rows in set (0.01 sec)
  • 结论: 在使用聚簇索引且查询命中情况下,RR与RC都在聚簇索引上加上了Record Locks
聚簇索引,查询未命中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
mysql> begin;
Query OK, 0 rows affected (0.01 sec)

mysql> update s set age = 20 where id = 16;
Query OK, 0 rows affected (0.01 sec)
Rows matched: 0 Changed: 0 Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204643680:1066:4771072680
ENGINE_TRANSACTION_ID: 7471
THREAD_ID: 51
EVENT_ID: 28
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4771072680
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204643680:5:4:3:4773632024
ENGINE_TRANSACTION_ID: 7471
THREAD_ID: 51
EVENT_ID: 28
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 4773632024
LOCK_TYPE: RECORD
LOCK_MODE: X,GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 18
2 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204644656:1066:4771073768
ENGINE_TRANSACTION_ID: 7472
THREAD_ID: 52
EVENT_ID: 27
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4771073768
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
1 row in set (0.01 sec)
  • 结论,在使用聚簇索引,但查询未命中的情况下
    • RR下,加上了Gap Locks(区间(15,18))
    • RC不加锁
二级唯一索引,查询命中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update s set age = 20 where no = 'S0001';
Query OK, 0 rows affected (0.01 sec)
Rows matched: 1 Changed: 0 Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)


-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204643680:1066:4771072680
ENGINE_TRANSACTION_ID: 7473
THREAD_ID: 51
EVENT_ID: 32
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4771072680
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204643680:5:5:2:4773632024
ENGINE_TRANSACTION_ID: 7473
THREAD_ID: 51
EVENT_ID: 32
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 4773632024
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0001', 15
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204643680:5:4:2:4773632376
ENGINE_TRANSACTION_ID: 7473
THREAD_ID: 51
EVENT_ID: 32
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 4773632376
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
3 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204644656:1066:4771073768
ENGINE_TRANSACTION_ID: 7474
THREAD_ID: 52
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 4771073768
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204644656:5:5:2:4773636632
ENGINE_TRANSACTION_ID: 7474
THREAD_ID: 52
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 4773636632
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0001', 15
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 5204644656:5:4:2:4773636984
ENGINE_TRANSACTION_ID: 7474
THREAD_ID: 52
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 4773636984
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
3 rows in set (0.01 sec)
  • 结论: 在使用二级唯一索引且查询命中情况下,RR和RC都在二级索引上加上了Record Locks以及二级索引对应的聚簇索引上加上了Record Locks
二级唯一索引,查询未命中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update s set age = 20 where no ='S1001';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:1066:5587885048
ENGINE_TRANSACTION_ID: 8458
THREAD_ID: 48
EVENT_ID: 19
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587885048
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:5:5:1:5578610200
ENGINE_TRANSACTION_ID: 8458
THREAD_ID: 48
EVENT_ID: 19
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: supremum pseudo-record
2 rows in set (0.00 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875850544:1066:5587886136
ENGINE_TRANSACTION_ID: 8459
THREAD_ID: 49
EVENT_ID: 19
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587886136
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
1 row in set (0.00 sec)
  • 结论: 在使用二级唯一索引,查询未命中情况下

    • RR下会取决于未命中的条件的值与当前现有的索引的值进行比较后落在那个区间,从而判断加Gap Locks还是Next-Key Locks

      • 条件的值在当前现有的索引的值的(-∞,max)区间内,加Gap Locks

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        mysql> begin;
        Query OK, 0 rows affected (0.01 sec)
        -- update s set age = 20 where no ='-10000000'; 落在(-∞,'S0001')上,在S0001加Gap Locks
        -- update s set age = 20 where no =''; 落在(-∞,'S0001')上,在S0001加Gap Locks
        -- update s set age = 20 where no ='S0003'; 落在('S0002','S0004')上,在S0004加Gap Locks
        -- 落在('S0008','S00017')上,在S00017加Gap Locks 锁定 "S0011" 所在的索引区间
        mysql> update s set age = 20 where no ='S0011';
        Query OK, 0 rows affected (0.00 sec)
        Rows matched: 0 Changed: 0 Warnings: 0

      • 条件的值超出当前现有索引的值的最大值,在supremum pseudo-record上加Next-Key Locks

        1
        2
        3
        4
        5
        6
        mysql> begin;
        Query OK, 0 rows affected (0.01 sec)
        -- 落在('S00017',+∞]上,在`supremum pseudo-record`上加`Next-Key Locks`
        mysql> update s set age = 20 where no ='S0020';
        Query OK, 0 rows affected (0.00 sec)
        Rows matched: 0 Changed: 0 Warnings: 0
    • RC下不加锁

二级非唯一索引,查询命中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update s set age=20 where name ='Tom';
Query OK, 2 rows affected (0.01 sec)
Rows matched: 2 Changed: 2 Warnings: 0

-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:1067:5587885048
ENGINE_TRANSACTION_ID: 8537
THREAD_ID: 48
EVENT_ID: 85
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587885048
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:6:1:5578610200
ENGINE_TRANSACTION_ID: 8537
THREAD_ID: 48
EVENT_ID: 85
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: supremum pseudo-record
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:6:6:5578610200
ENGINE_TRANSACTION_ID: 8537
THREAD_ID: 48
EVENT_ID: 85
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 'Tom', 37
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:6:7:5578610200
ENGINE_TRANSACTION_ID: 8537
THREAD_ID: 48
EVENT_ID: 85
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 'Tom', 49
*************************** 5. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:6:5578610552
ENGINE_TRANSACTION_ID: 8537
THREAD_ID: 48
EVENT_ID: 85
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610552
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 37
*************************** 6. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:7:5578610552
ENGINE_TRANSACTION_ID: 8537
THREAD_ID: 48
EVENT_ID: 85
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610552
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 49
6 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:1067:5587887224
ENGINE_TRANSACTION_ID: 8539
THREAD_ID: 51
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587887224
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:6:6:5578619416
ENGINE_TRANSACTION_ID: 8539
THREAD_ID: 51
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'Tom', 37
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:6:7:5578619416
ENGINE_TRANSACTION_ID: 8539
THREAD_ID: 51
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'Tom', 49
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:6:5578619768
ENGINE_TRANSACTION_ID: 8539
THREAD_ID: 51
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619768
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 37
*************************** 5. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:7:5578619768
ENGINE_TRANSACTION_ID: 8539
THREAD_ID: 51
EVENT_ID: 31
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619768
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 49
5 rows in set (0.01 sec)
  • 结论,在使用二级非唯一索引,查询命中情况下
    • RR下,会在命中的二级非唯一索引上加上Next-Key Locks,在二级非唯一索引对应的聚簇索引上加上Record Locks
      • 若查询条件在二级非唯一索引(-∞,max)区间内,则会在不符合条件的下一个二级非唯一索引上加GAP Locks
        • 关于"在不符合条件的下一个二级非唯一索引上加GAP Locks"的原因猜想:因为索引是不唯一的,防止其他事务中插入和当前索引相同的值,因此将下一个二级非唯一索引也加上了GAP Locks
      • 若查询条件恰好等于二级非唯一索引区间的最大值(MAX),则会在supremum pseudo-record加上Next-Key Locks
    • RC下,会在命中的二级非唯一索引以及对应的聚簇索引上都加上Record Locks
二级非唯一索引,查询未命中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update s set age=20 where name ='Lin';
Query OK, 0 rows affected (0.01 sec)
Rows matched: 0 Changed: 0 Warnings: 0

-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:1067:5587885048
ENGINE_TRANSACTION_ID: 8540
THREAD_ID: 48
EVENT_ID: 89
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587885048
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:6:8:5578610200
ENGINE_TRANSACTION_ID: 8540
THREAD_ID: 48
EVENT_ID: 89
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: name
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X,GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'Rose', 50
2 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:1067:5587887224
ENGINE_TRANSACTION_ID: 8541
THREAD_ID: 51
EVENT_ID: 35
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587887224
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
1 row in set (0.01 sec)
  • 结论: 在使用二级非唯一索引,查询未命中情况下,
    • RR下,如果未命中的条件的值在当前现有的索引值的区间(-∞,max)内,则会在不满足条件的下一个二级非唯一索引加 Gap Locks.否则(即在区间(max,+∞))会在 supremum pseudo-record 上加 Next-Key Locks 以避免幻读现象的发生.
    • RC下不会在索引上加锁.
无索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
mysql> begin;
Query OK, 0 rows affected (0.01 sec)

mysql> update s set name = 'Ju' where age = 25;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
-- 观察加锁情况

-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:1067:5587885048
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587885048
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:1:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: supremum pseudo-record
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:2:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 15
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:3:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 18
*************************** 5. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:4:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 20
*************************** 6. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:5:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 30
*************************** 7. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:6:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 37
*************************** 8. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:7:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 49
*************************** 9. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:8:5578610200
ENGINE_TRANSACTION_ID: 8542
THREAD_ID: 48
EVENT_ID: 93
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 50
9 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:1067:5587887224
ENGINE_TRANSACTION_ID: 8544
THREAD_ID: 51
EVENT_ID: 39
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587887224
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:2:5578619416
ENGINE_TRANSACTION_ID: 8544
THREAD_ID: 51
EVENT_ID: 39
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
2 rows in set (0.01 sec)
  • 结论: 在不使用索引的情况下
    • RR下,
      • 如果查询命中,则会在所有的聚簇索引上加 Next-Key Locks,即锁表;
      • 如果查询未命中,会对不满足条件的所有记录加Next-Key Locks和对应的聚簇索引加Record Locks
      • 若执行的是更新操作,则会锁表.
    • RC下,只会在命中的记录的聚簇索引上Record Locks,不命中则不加锁

在没有索引的时候,只能走聚簇索引,对表中的记录进行全表扫描.在 RC 隔离级别下会给所有记录加行锁,在 RR 隔离级别下,不仅会给所有记录加行锁,所有聚簇索引和聚簇索引之间还会加上 GAP 锁 即锁表.

语句 update s set name = 'Ju' where age = 25; 满足条件的虽然只有 1 条记录,但是聚簇索引上所有的记录,都被加上了 X 锁.那么,为什么不是只在满足条件的记录上加锁呢?这是由于 MySQL 的实现决定的.如果一个条件无法通过索引快速过滤,那么存储引擎层面就会将所有记录加锁后返回,然后由 MySQL Server 层进行过滤,因此也就把所有的记录都锁上了.

不过在实际的实现中,MySQL 有一些改进,如果是 RC 隔离级别,在 MySQL Server 过滤条件发现不满足后,会调用 unlock_row 方法,把不满足条件的记录锁释放掉(违背了 2PL 的约束).这样做可以保证最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的.如果是 RR 隔离级别,一般情况下 MySQL 是不能这样优化的,除非设置了 innodb_locks_unsafe_for_binlog 参数,这时也会提前释放锁,并且不加 GAP 锁,这就是所谓的 semi-consistent read.

聚簇索引,范围查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update s set age = 22 where id <= 20;
Query OK, 3 rows affected (0.01 sec)
Rows matched: 3 Changed: 3 Warnings: 0
-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:1067:5587885048
ENGINE_TRANSACTION_ID: 8554
THREAD_ID: 48
EVENT_ID: 109
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587885048
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:2:5578610200
ENGINE_TRANSACTION_ID: 8554
THREAD_ID: 48
EVENT_ID: 109
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 15
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:3:5578610200
ENGINE_TRANSACTION_ID: 8554
THREAD_ID: 48
EVENT_ID: 109
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 18
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:4:5578610200
ENGINE_TRANSACTION_ID: 8554
THREAD_ID: 48
EVENT_ID: 109
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 20
4 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:1067:5587887224
ENGINE_TRANSACTION_ID: 8558
THREAD_ID: 51
EVENT_ID: 48
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587887224
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:2:5578619416
ENGINE_TRANSACTION_ID: 8558
THREAD_ID: 51
EVENT_ID: 48
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:3:5578619416
ENGINE_TRANSACTION_ID: 8558
THREAD_ID: 51
EVENT_ID: 48
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 18
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:4:5578619416
ENGINE_TRANSACTION_ID: 8558
THREAD_ID: 51
EVENT_ID: 48
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 20
4 rows in set (0.01 sec)
  • 在使用聚簇索引,范围查询情况下,
    • RR下会对符合范围查询的聚簇索引加上Next-Key Locks
    • RC下会对符合范围查询的聚簇索引加上Record Locks
二级唯一索引,范围查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update s set age =age+1 where no <= 'S0002';
Query OK, 2 rows affected (0.01 sec)
Rows matched: 2 Changed: 2 Warnings: 0
-- 观察加锁情况
-- session A RR
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:1067:5587885048
ENGINE_TRANSACTION_ID: 8566
THREAD_ID: 48
EVENT_ID: 125
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587885048
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:5:2:5578610200
ENGINE_TRANSACTION_ID: 8566
THREAD_ID: 48
EVENT_ID: 125
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0001', 15
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:5:3:5578610200
ENGINE_TRANSACTION_ID: 8566
THREAD_ID: 48
EVENT_ID: 125
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0002', 18
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:5:4:5578610200
ENGINE_TRANSACTION_ID: 8566
THREAD_ID: 48
EVENT_ID: 125
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578610200
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0004', 20
*************************** 5. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:2:5578610552
ENGINE_TRANSACTION_ID: 8566
THREAD_ID: 48
EVENT_ID: 125
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610552
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
*************************** 6. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:3:5578610552
ENGINE_TRANSACTION_ID: 8566
THREAD_ID: 48
EVENT_ID: 125
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610552
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 18
*************************** 7. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875849568:6:4:4:5578610552
ENGINE_TRANSACTION_ID: 8566
THREAD_ID: 48
EVENT_ID: 125
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578610552
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 20
7 rows in set (0.01 sec)

-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:1067:5587887224
ENGINE_TRANSACTION_ID: 8562
THREAD_ID: 51
EVENT_ID: 52
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587887224
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:5:2:5578619416
ENGINE_TRANSACTION_ID: 8562
THREAD_ID: 51
EVENT_ID: 52
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0001', 15
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:5:3:5578619416
ENGINE_TRANSACTION_ID: 8562
THREAD_ID: 51
EVENT_ID: 52
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0002', 18
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:2:5578619768
ENGINE_TRANSACTION_ID: 8562
THREAD_ID: 51
EVENT_ID: 52
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619768
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
*************************** 5. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:3:5578619768
ENGINE_TRANSACTION_ID: 8562
THREAD_ID: 51
EVENT_ID: 52
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619768
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 18
5 rows in set (0.01 sec)
-- session B RC
mysql> select * from PERFORMANCE_SCHEMA.DATA_LOCKS\G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:1067:5587887224
ENGINE_TRANSACTION_ID: 8568
THREAD_ID: 51
EVENT_ID: 57
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 5587887224
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:5:2:5578619416
ENGINE_TRANSACTION_ID: 8568
THREAD_ID: 51
EVENT_ID: 57
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0001', 15
*************************** 3. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:5:3:5578619416
ENGINE_TRANSACTION_ID: 8568
THREAD_ID: 51
EVENT_ID: 57
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: no
OBJECT_INSTANCE_BEGIN: 5578619416
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 'S0002', 18
*************************** 4. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:2:5578619768
ENGINE_TRANSACTION_ID: 8568
THREAD_ID: 51
EVENT_ID: 57
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619768
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 15
*************************** 5. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 4875851520:6:4:3:5578619768
ENGINE_TRANSACTION_ID: 8568
THREAD_ID: 51
EVENT_ID: 57
OBJECT_SCHEMA: debug
OBJECT_NAME: s
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 5578619768
LOCK_TYPE: RECORD
LOCK_MODE: X,REC_NOT_GAP
LOCK_STATUS: GRANTED
LOCK_DATA: 18
5 rows in set (0.01 sec)
  • 结论: 在使用二级索引范围查询情况下
    • RR下会对满足范围查询的二级索引加上Next-Key Locks以及对应的聚簇索引加上Record Locks外,还会在不满足条件的第一个二级索引上加上Next-Key Locks以及对应的聚簇索引上加上Record Locks
      • 即上述信息中满足条件的为(‘S0001’,‘S0002’),对应的聚簇索引为(15,18),不满足条件的第一个则为(‘S0004’)对应的聚簇索引为(20)
    • RC下会对满足范围查询的二级索引以及对应的聚簇索引都加上Record Locks

总结

聚簇索引,查询命中

  • 在使用聚簇索引且查询命中情况下,RR和RC都会在聚簇索引上加上 Record Locks,锁定的是符合查询条件的行.
  • 如果查询语句使用了 FOR UPDATELOCK IN SHARE MODE 语句,则会额外加上 Next-Key Locks,同时会在聚簇索引的 supremum pseudo-record 上加上 Next-Key Locks,避免幻读的发生.

聚簇索引,查询未命中

  • 结论,在使用聚簇索引,但查询未命中的情况下
    • RR下,如果表中不存在符合查询条件的记录,那么会在聚簇索引上加上 Gap Locks,以此避免其他事务往数据表中插入数据时,产生幻读的情况.
    • RC下,不会在聚簇索引上加锁.

二级唯一索引,查询命中

  • 结论: 在使用二级唯一索引且查询命中情况下
    • RR和RC都会在二级唯一索引上加上 Record Locks 和对应的聚簇索引上的 Record Locks.
    • 如果查询语句使用了 FOR UPDATELOCK IN SHARE MODE 语句,则会额外加上相应的 Next-Key Locks 以及在聚簇索引的 supremum pseudo-record 上加上 Next-Key Locks,以此避免幻读的发生.

二级唯一索引,查询未命中

  • 结论: 在使用二级唯一索引,查询未命中情况下

    • RR下会取决于未命中的条件的值与当前现有的索引的值进行比较后落在那个区间,从而判断加Gap Locks还是Next-Key Locks

      • 如果未命中的条件的值在当前现有的索引值的区间 (-∞,max) 内,则会在二级唯一索引和对应的聚簇索引上加 Gap Locks

      • 条件的值超出当前现有索引的值的最大值,在supremum pseudo-record上加Next-Key Locks,以避免幻读的出现

    • RC下不加锁

二级非唯一索引,查询命中

  • 结论,在使用二级非唯一索引,查询命中情况下
      • RR下,会在命中的二级非唯一索引上加上Next-Key Locks,在二级非唯一索引对应的聚簇索引上加上Record Locks
        • 若查询条件在二级非唯一索引(-∞,max)区间内,则会在不符合条件的下一个二级非唯一索引上加GAP Locks
          • 关于"在不符合条件的下一个二级非唯一索引上加GAP Locks"的原因猜想:因为索引是不唯一的,防止其他事务中插入和当前索引相同的值(为防止发生幻读),因此将下一个二级非唯一索引也加上了GAP Locks
        • 若查询条件恰好等于二级非唯一索引区间的最大值(MAX),则会在supremum pseudo-record加上Next-Key Locks
    • RC下,会在命中的二级非唯一索引以及对应的聚簇索引上都加上Record Locks

二级非唯一索引,查询未命中

  • 结论: 在使用二级非唯一索引,查询未命中情况下,
    • RR下,如果未命中的条件的值在当前现有的索引值的区间(-∞,max)内,则会在不满足条件的下一个二级非唯一索引加 Gap Locks.否则(即在区间(max,+∞))会在 supremum pseudo-record 上加 Next-Key Locks 以避免幻读现象的发生.
    • RC下不会在索引上加锁.

无索引

  • 结论: 在不使用索引的情况下
    • RR下
      • 如果查询命中,则会在所有的聚簇索引上加 Next-Key Locks,即锁表;
      • 如果查询未命中,会对不满足条件的所有记录加Next-Key Locks和对应的聚簇索引加Record Locks
      • 若执行的是更新操作,则会锁表.
    • RC下
      • 如果查询命中,则只会在命中的记录的聚簇索引上加上 Record Locks
      • 如果查询未命中,则不会在聚簇索引上加锁.

聚簇索引,范围查询

  • 在使用聚簇索引,范围查询情况下,
    • RR下会对符合范围查询的聚簇索引加上Next-Key Locks
    • RC下会对符合范围查询的聚簇索引加上Record Locks

二级唯一索引,范围查询

  • 结论: 在使用二级索引范围查询情况下
    • RR下会对满足范围查询的二级索引加上Next-Key Locks以及对应的聚簇索引加上Record Locks外,还会在不满足条件的第一个二级索引上加上Next-Key Locks以及对应的聚簇索引上加上Record Locks
    • RC下会对满足范围查询的二级索引以及对应的聚簇索引都加上Record Locks