1. 程式人生 > >MySQL 間隙鎖

MySQL 間隙鎖

死鎖分析 des ola ast star del PE ant for

一.根據案例二:不同索引加鎖順序的問題,模擬重現死鎖(詳細操作步驟)

  • 1.RR級別下,更新操作默認會加行級鎖,行級鎖會對索引加鎖
  • 2.如果更新語句使用多個索引,行級鎖會先鎖定普通索引,再鎖定聚簇索引
  • 3.如果兩個SQL用到了不同的普通索引,或者一個用了,另外一個沒用
  • 4.會導致這兩個SQL加行級鎖的順序不一致,形成多個事物之間X鎖的循環等待,形成死鎖
1.1表結構
root@slave01 22:28:  [sqltest]> create table user_info (id int primary key,username varchar(20), status tinyint,key(username));
Query OK, 0 rows affected (0.03 sec)
#
root@slave01 22:54:  [sqltest]> show create table user_info;
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table     | Create Table                                                                                                                                                                                                            |
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| user_info | CREATE TABLE `user_info` (
  `id` int(11) NOT NULL,
  `username` varchar(20) DEFAULT NULL,
  `status` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
#
insert into user_info values(1,‘yzw1‘,1);
insert into user_info values(3,‘yzw3‘,0);
insert into user_info values(5,‘yzw5‘,1);
1.2執行計劃
root@master 23:15:  [sqltest]> desc update user_info set status=1 where username=‘yzw3‘;
+----+-------------+-----------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key      | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
|  1 | UPDATE      | user_info | NULL       | range | username      | username | 63      | const |    1 |   100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
1 row in set (0.01 sec)
#
root@master 23:15:  [sqltest]> desc update user_info set username=‘yzw33‘ where id=3;
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | UPDATE      | user_info | NULL       | range | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set (0.00 sec)
1.3重現
session 1 session 2
begin;
update user_info set status=1 where username=‘yzw3‘;
此時session除了給索引username加x鎖,還給主鍵索引id=3的行記錄加了x鎖,但是這兩個鎖並不是同時獲取的
第一步先獲取普通索引username上的X鎖
此時session 2進行更新 begin;
update user_info set username=‘yzw33‘ where id=3;
session 2
第一步需要先獲取聚簇索引,也就是主鍵索引上的X鎖
第二步需要獲取普通索引username上的X鎖
出現死鎖ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
需要session 2的主鍵索引X鎖 需要session 1的普通索引username X鎖
root@master 23:11:  [(none)]> select * from information_schema.innodb_locks;
+-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
| lock_id           | lock_trx_id | lock_mode | lock_type | lock_table            | lock_index | lock_space | lock_page | lock_rec | lock_data |
+-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
| 48736590:1135:3:3 | 48736590    | X         | RECORD    | `sqltest`.`user_info` | PRIMARY    |       1135 |         3 |        3 | 3         |
| 48733648:1135:3:3 | 48733648    | X         | RECORD    | `sqltest`.`user_info` | PRIMARY    |       1135 |         3 |        3 | 3         |
+-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
2 rows in set, 1 warning (0.00 sec)
1.4重現
  • 1.給status列增加索引

    create index idx_status on user_info(status);
  • 2.執行計劃,兩條語句都是走的普通索引

    root@master 23:26:  [sqltest]> desc update user_info set status=1 where username=‘yzw3‘;
    +----+-------------+-----------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
    | id | select_type | table     | partitions | type  | possible_keys | key      | key_len | ref   | rows | filtered | Extra       |
    +----+-------------+-----------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
    |  1 | UPDATE      | user_info | NULL       | range | username      | username | 63      | const |    1 |   100.00 | Using where |
    +----+-------------+-----------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
    1 row in set (0.04 sec)
    #
    root@master 23:29:  [sqltest]> desc update user_info set username=‘yzw33‘ where status=0;
    +----+-------------+-----------+------------+-------+---------------+------------+---------+-------+------+----------+-------------+
    | id | select_type | table     | partitions | type  | possible_keys | key        | key_len | ref   | rows | filtered | Extra       |
    +----+-------------+-----------+------------+-------+---------------+------------+---------+-------+------+----------+-------------+
    |  1 | UPDATE      | user_info | NULL       | range | idx_status    | idx_status | 2       | const |    1 |   100.00 | Using where |
    +----+-------------+-----------+------------+-------+---------------+------------+---------+-------+------+----------+-------------+
    1 row in set (0.01 sec)
  • 3.兩個會話更新索引

    session 1 session 2
    begin;
    update user_info set status=1 where username=‘yzw3‘;
    第一步已經獲取了username的普通索引X鎖 begin;
    update user_info set username=‘yzw33‘ where status=0;
    第一步已經獲取status的普通索引X鎖
    第二步給主鍵索引加X鎖
    第二步需要給主鍵索引加X鎖,無法獲取 出現死鎖ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    因為要更新非主鍵索引username,因此要給username加X鎖,但是無法獲取
    root@master 23:30:  [sqltest]> select * from information_schema.innodb_locks;
    +-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
    | lock_id           | lock_trx_id | lock_mode | lock_type | lock_table            | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
    | 48739611:1135:5:2 | 48739611    | X         | RECORD    | `sqltest`.`user_info` | idx_status |       1135 |         5 |        2 | 0, 3      |
    | 48738737:1135:5:2 | 48738737    | X         | RECORD    | `sqltest`.`user_info` | idx_status |       1135 |         5 |        2 | 0, 3      |
    +-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
    2 rows in set, 1 warning (0.00 sec)
    1.5多查詢一次取主鍵,解決死鎖
    begin; 
    update user_info set status=1 where id in (select id  from (select id from user_info where username=‘yzw3‘) t);
    begin;
    update user_info set username=‘yzw33‘ where id in (select id from  (select id from user_info where status=0) t);
    root@master 23:56:  [sqltest]> select * from information_schema.innodb_locks;
    +-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
    | lock_id           | lock_trx_id | lock_mode | lock_type | lock_table            | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
    | 48744368:1135:3:3 | 48744368    | X         | RECORD    | `sqltest`.`user_info` | PRIMARY    |       1135 |         3 |        3 | 3         |
    | 48744353:1135:3:3 | 48744353    | X         | RECORD    | `sqltest`.`user_info` | PRIMARY    |       1135 |         3 |        3 | 3         |
    +-------------------+-------------+-----------+-----------+-----------------------+------------+------------+-----------+----------+-----------+
    2 rows in set, 1 warning (0.00 sec)

    依然存在死鎖,因為都給主鍵id加了X鎖

二.根據方案三:TMS死鎖分析和處理,模擬重現死鎖(詳細操作步驟)

  • 1.構造數據

    create table user_info1 (id int primary key,name varchar(10), level tinyint);
    insert into user_info1 values(1,‘AA‘,0);
    insert into user_info1 values(3,‘CC‘,0);
    insert into user_info1 values(5,‘EE‘,2);
    insert into user_info1 values(7,‘GG‘,5);
    insert into user_info1 values(9,‘GG‘,7);
    insert into user_info1 values(11,‘JJ‘,20);
    session 1 >select * from user_info1;
    +----+------+-------+
    | id | name | level |
    +----+------+-------+
    |  1 | AA   |     0 |
    |  3 | CC   |     0 |
    |  5 | EE   |     2 |
    |  7 | GG   |     5 |
    |  9 | GG   |     7 |
    | 11 | JJ   |    20 |
    +----+------+-------+
    6 rows in set (0.00 sec)
  • 2.過程

    session 1 session 2
    begin;
    insert into user_info1 values(2,‘BB‘,1);
    delete from user_info1 where name=‘BB‘;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    此時是RC級別 session 2首先插入數據並給這一行加X鎖,因為沒有索引,同時在兩邊加gap鎖
    delete from user_info1 where name=‘BC‘;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    delete from user_info1 where level=8;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    delete where條件沒有索引,會對整表掃描並加gap鎖,但是此時session2已經加了gap鎖,session1的delete事務被回退
    delete from user_info1 where  name=‘BB‘
    ------- TRX HAS BEEN WAITING 10 SEC FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 1137 page no 3 n bits 80 index PRIMARY of table `sqltest`.`user_info1` trx id 48906774 lock_mode X locks rec but not gap waiting
    Record lock, heap no 8 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
  • 3保持RCset tx_isolation=‘READ-COMMITTED‘;級別,增加索引

    session 1 >create index idx_name_level on user_info1(name,level);
    Query OK, 0 rows affected (0.12 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    session 1 session 2
    begin;
    insert into user_info1 values(2,‘BB‘,1);
    delete from user_info1 where name=‘BB‘;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    此時是RC級別 session 2首先插入數據並加gap鎖,
    session 1 >delete from user_info1 where name=‘BC‘;
    Query OK, 0 rows affected (0.00 sec)
    RC級別下,增加了索引,沒有了gap鎖
    session 1 >delete from user_info1 where name=‘BB‘;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 1 >delete from user_info1 where level=8;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 1 >delete from user_info1 where level=20;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 1 >delete from user_info1 where level=7;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 1 >delete from user_info1 where level=2;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    索引(a,b),如果where字段使用了索引a,則不存在gap鎖了,猜測是單獨使用索引b還是會存在gap鎖,因為無法使用索引
    root@master 16:31:  [(none)]> select * from information_schema.innodb_locks;
    +-------------------+-------------+-----------+-----------+------------------------+------------+------------+-----------+----------+-----------+
    | lock_id           | lock_trx_id | lock_mode | lock_type | lock_table             | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +-------------------+-------------+-----------+-----------+------------------------+------------+------------+-----------+----------+-----------+
    | 48942672:1137:3:8 | 48942672    | X         | RECORD    | `sqltest`.`user_info1` | PRIMARY    |       1137 |         3 |        8 | 2         |
    | 48942589:1137:3:8 | 48942589    | S         | RECORD    | `sqltest`.`user_info1` | PRIMARY    |       1137 |         3 |        8 | 2         |
    +-------------------+-------------+-----------+-----------+------------------------+------------+------------+-----------+----------+-----------+
    2 rows in set, 1 warning (0.00 sec)
  • 4增加索引(b),驗證

    session 1 >create index idx_level on user_info1(level);
    Query OK, 0 rows affected (0.08 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    session 1 session 2
    begin;
    insert into user_info1 values(2,‘BB‘,1);
    delete from user_info1 where name=‘BB‘;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    此時是RC級別 session 2首先插入數據並加X鎖
    session 1 >delete from user_info1 where name=‘BC‘;
    Query OK, 0 rows affected (0.00 sec)
    session 1 >delete from user_info1 where name=‘BB‘;
    ERROR 1205 (HY000): Lock wait timeout xceeded; try restarting transaction
    session 1 >delete from user_info1 where level=8;
    Query OK, 0 rows affected (0.01 sec)
    session 1 >delete from user_info1 where level=20;
    Query OK, 1 row affected (0.01 sec)
    session 1 >delete from user_info1 where level=7;
    Query OK, 1 row affected (0.00 sec)
    session 1 >delete from user_info1 where level=2;
    Query OK, 1 row affected (0.00 sec)
    此時b有了索引,RC級別下也就不存在gap鎖了
  • 5更改為RR級別set tx_isolation=‘REPEATABLE-READ‘;,先使用索引(a,b)

    session 2 >drop index idx_level on user_info1;
    Query OK, 0 rows affected (0.16 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    session 2 >begin;
    Query OK, 0 rows affected (0.00 sec)
    #
    session 2 >insert into user_info1 values(2,‘BB‘,1);
    Query OK, 1 row affected (0.00 sec)
    #
    session 1 >delete from user_info1 where name=‘BB‘;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 1 >delete from user_info1 where name=‘BC‘;
    Query OK, 0 rows affected (0.00 sec)
    #
    session 1 >delete from user_info1 where level=8;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 1 >delete from user_info1 where level=2;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 1 >delete from user_info1 where name=‘CB‘;
    Query OK, 0 rows affected (0.00 sec)
    #
    session 1 >delete from user_info1 where name=‘BA‘;
    Query OK, 0 rows affected (0.00 sec)
  • 6總結
    1) RC和RR級別下如果更新沒有索引的字段,會加gap鎖
    2) 必須能用上索引,(a,b)只用b是無效索引
    3) RC增加了索引後就不存在gap鎖了
    3) RR級別在沒有索引的情況,用不上索引也會產生gap鎖
    4) 解決方法,最好使用RC級別+索引,或者RR下修改參數innodb_locks_unsafe_for_binlog=on
    5) 間隙鎖只會block住insert操作

    session 1 >select * from user_info1;
    +----+------+-------+
    | id | name | level |
    +----+------+-------+
    |  1 | AA   |     0 |
    |  3 | BA   |     1 |
    |  5 | EE   |     2 |
    |  7 | GG   |     5 |
    |  9 | GG   |     7 |
    | 11 | JJ   |    20 |
    +----+------+-------+
    6 rows in set (0.00 sec)
    #
    session 1 >begin;
    Query OK, 0 rows affected (0.00 sec)
    #
    session 1 >update user_info1 set level=3 where name=‘BA‘;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    session 2 >begin;
    Query OK, 0 rows affected (0.01 sec)
    #
    session 2 >insert into user_info1 values(2,‘AB‘,1);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

三.根據案例四:MySQL間隙鎖導致的Deadlock分析,模擬重現死鎖(詳細操作步驟)

3.1 造數據
create table t3 (id int auto_increment,coupon_id int,uid int,is_inuse tinyint,primary key(id),key (coupon_id,uid));
insert into t3 (coupon_id,uid,is_inuse) values(16,160825,1);
insert into t3 (coupon_id,uid,is_inuse) values(16,160835,1);
insert into t3 (coupon_id,uid,is_inuse) values(16,160845,1);
insert into t3 (coupon_id,uid,is_inuse) values(16,160855,1);
insert into t3 (coupon_id,uid,is_inuse) values(16,160865,1);
root@master 22:56:  [sqltest]> select * from t3;
+----+-----------+--------+----------+
| id | coupon_id | uid    | is_inuse |
+----+-----------+--------+----------+
|  1 |        16 | 160825 |        1 |
|  2 |        16 | 160835 |        1 |
|  3 |        16 | 160845 |        1 |
|  4 |        16 | 160855 |        1 |
|  5 |        16 | 160865 |        1 |
+----+-----------+--------+----------+
5 rows in set (0.00 sec)
3.2 行記錄兩邊間隙鎖範圍內無法插入
  • 1.session 1給id=3的行記錄加X鎖,同時加間隙鎖:160835~160845,160845~160855

    set tx_isolation=‘REPEATABLE-READ‘;
    set autocommit=0;
    start transaction;
    select * from t3 where (uid=160845 and coupon_id=16 and is_inuse=1) for update;
    +----+-----------+--------+----------+
    | id | coupon_id | uid    | is_inuse |
    +----+-----------+--------+----------+
    |  3 |        16 | 160845 |        1 |
    +----+-----------+--------+----------+
    1 row in set (0.00 sec)
  • 2.session 2插入記錄,在間隙範圍:160835~160845

    set tx_isolation=‘REPEATABLE-READ‘;
    set autocommit=0;
    start transaction;
    insert into t3 (coupon_id,uid,is_inuse) values (16,160840,1);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  • 3.lock

    root@master 23:08:  [sqltest]> select * from information_schema.innodb_locks;
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    | lock_id           | lock_trx_id | lock_mode | lock_type | lock_table     | lock_index | lock_space | lock_page | lock_rec | lock_data     |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    | 49020303:1139:4:4 | 49020303    | X,GAP     | RECORD    | `sqltest`.`t3` | coupon_id  |       1139 |         4 |        4 | 16, 160845, 3 |
    | 49020011:1139:4:4 | 49020011    | X         | RECORD    | `sqltest`.`t3` | coupon_id  |       1139 |         4 |        4 | 16, 160845, 3 |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    2 rows in set, 1 warning (0.00 sec)
  • 4.session 2插入記錄,在間隙範圍:160845~160855

    set tx_isolation=‘REPEATABLE-READ‘;
    set autocommit=0;
    start transaction;
    insert into t3 (coupon_id,uid,is_inuse) values (16,160850,1);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  • 5.lock

    root@master 23:11:  [sqltest]> select * from information_schema.innodb_locks;
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    | lock_id           | lock_trx_id | lock_mode | lock_type | lock_table     | lock_index | lock_space | lock_page | lock_rec | lock_data     |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    | 49022356:1139:4:5 | 49022356    | X,GAP     | RECORD    | `sqltest`.`t3` | coupon_id  |       1139 |         4 |        5 | 16, 160855, 4 |
    | 49020011:1139:4:5 | 49020011    | X,GAP     | RECORD    | `sqltest`.`t3` | coupon_id  |       1139 |         4 |        5 | 16, 160855, 4 |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    2 rows in set, 1 warning (0.00 sec)
    3.3 行記錄間隙鎖範圍外可以插入
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    session 2 >insert into t3 (coupon_id,uid,is_inuse) values (16,160860,1);
    Query OK, 1 row affected (0.01 sec)
    3.4 update不存在的記錄,也會加間隙鎖
  • 1.數據

    session 1 >select * from t3;
    +----+-----------+--------+----------+
    | id | coupon_id | uid    | is_inuse |
    +----+-----------+--------+----------+
    |  1 |        16 | 160825 |        1 |
    |  2 |        16 | 160835 |        1 |
    |  3 |        16 | 160845 |        1 |
    |  4 |        16 | 160855 |        1 |
    |  5 |        16 | 160865 |        1 |
    +----+-----------+--------+----------+
    5 rows in set (0.00 sec)
  • 2.update一條不存在的記錄,此時gap鎖範圍:160835~160845,160845~160855

    set tx_isolation=‘REPEATABLE-READ‘;
    set autocommit=0;
    start transaction;
    update t3 set is_inuse=0 where coupon_id=16 and uid=160845 and is_inuse=1;
  • 3.在間隙範圍插入新值會產生死鎖

    set tx_isolation=‘REPEATABLE-READ‘;
    set autocommit=0;
    start transaction;
    insert into t3 (coupon_id,uid,is_inuse) values (16,160840,1);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  • 4.lock

    root@master 23:11:  [sqltest]> select * from information_schema.innodb_locks;
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    | lock_id           | lock_trx_id | lock_mode | lock_type | lock_table     | lock_index | lock_space | lock_page | lock_rec | lock_data     |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    | 49023844:1139:4:4 | 49023844    | X,GAP     | RECORD    | `sqltest`.`t3` | coupon_id  |       1139 |         4 |        4 | 16, 160845, 3 |
    | 49023813:1139:4:4 | 49023813    | X         | RECORD    | `sqltest`.`t3` | coupon_id  |       1139 |         4 |        4 | 16, 160845, 3 |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+---------------+
    2 rows in set, 1 warning (0.00 sec)
3.5刪除索引,即使是間隙範圍外的插入也會被block
  • 1.刪除索引

    session 2 >drop index coupon_id on t3;
    Query OK, 0 rows affected (0.09 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    #
    session 2 >commit;
    Query OK, 0 rows affected (0.00 sec)
  • 2.事務1加鎖

    set tx_isolation=‘REPEATABLE-READ‘;
    set autocommit=0;
    start transaction;
    select * from t3 where (uid=160845 and coupon_id=16 and is_inuse=1) for update;
  • 3.事務2在間隙鎖範圍外插入,此時不是間隙鎖,而是全表鎖,無法插入

    set tx_isolation=‘REPEATABLE-READ‘;
    set autocommit=0;
    start transaction;
    select * from t3 where (uid=160850 and coupon_id=16 and is_inuse=1) for update;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  • 4.lock

    root@master 23:27:  [sqltest]> select * from information_schema.innodb_locks;
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
    | lock_id           | lock_trx_id | lock_mode | lock_type | lock_table     | lock_index | lock_space | lock_page | lock_rec | lock_data |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
    | 49025074:1139:3:2 | 49025074    | X         | RECORD    | `sqltest`.`t3` | PRIMARY    |       1139 |         3 |        2 | 1         |
    | 49025016:1139:3:2 | 49025016    | X         | RECORD    | `sqltest`.`t3` | PRIMARY    |       1139 |         3 |        2 | 1         |
    +-------------------+-------------+-----------+-----------+----------------+------------+------------+-----------+----------+-----------+
    2 rows in set, 1 warning (0.00 sec)
3.6 總結
  • 1.RR級別下,有索引,更新的時候會給行加X鎖,前後加GAP鎖
  • 2.gap鎖範圍內無法插入新數據,避免產生幻讀
  • 3.gap鎖範圍外可以正常插入新數據
  • 4.如果沒有索引,更新的事務則加的就是全表鎖了
  • 5.間隙內或者間隙外都無法插入新數據

MySQL 間隙鎖