MySQL 间隙锁

Posted 不懂ABAP的python不是好basis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL 间隙锁相关的知识,希望对你有一定的参考价值。

一.根据案例二:不同索引加锁顺序的问题,模拟重现死锁(详细操作步骤)

  • 1.RR级别下,更新操作默认会加行级锁,行级锁会对索引加锁
  • 2.如果更新语句使用多个索引,行级锁会先锁定普通索引,再锁定聚簇索引
  • 3.如果两个SQL用到了不同的普通索引,或者一个用了,另外一个没用
  • 4.会导致这两个SQL加行级锁的顺序不一致,形成多个事物之间X锁的循环等待,形成死锁
1.1表结构
[email protected] 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)
#
[email protected] 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执行计划
[email protected] 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)
#
[email protected] 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锁
[email protected] 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.执行计划,两条语句都是走的普通索引

    [email protected] 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)
    #
    [email protected] 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锁,但是无法获取
    [email protected] 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);
    [email protected] 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锁,因为无法使用索引
    [email protected] 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);
[email protected] 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

    [email protected] 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

    [email protected] 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

    [email protected] 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

    [email protected] 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 间隙锁的主要内容,如果未能解决你的问题,请参考以下文章

mysql间隙锁

MySQL间隙锁(幻读解决原理)

mysql 间隙锁和临键锁原理

深入理解MySQL的间隙锁

MySQL里的间隙锁以及加锁规则

Mysql间隙锁