MySQL的锁:innodb锁粒度详解

锁定义

lock_rec_not_gap锁

Record Locks

lock_gap锁

Gap Locks

lock_ordinary锁

next-key lock

lock_insert_intention 锁

Insert Intention Locks

原则和优化

测试环境准备

准备脚本
CREATE TABLE `dboop_locktest` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
  `age` int NOT NULL DEFAULT '0',
  `gender` tinyint NOT NULL DEFAULT '-1',
  PRIMARY KEY (`id`),
  KEY `idx_dboop_locktest_age` (`age`),
  KEY `idx_dboop_locktest_name` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci 




insert into dboop_locktest(username,age,gender) 
values
('aaa',0,0),
('bbb',5,1),
('aaa',6,1),
('bbb',7,1),
('aaa',9,1),
('ccc',10,0),
('ddd',15,0),
('eee',20,1),
('fff',25,1)
;

验证
select * from dboop_locktest;
+----+----------+-----+--------+
| id | username | age | gender |
+----+----------+-----+--------+
|  1 | aaa      |   0 |      0 |
|  2 | bbb      |   5 |      1 |
|  3 | aaa      |   6 |      1 |
|  4 | bbb      |   7 |      1 |
|  5 | aaa      |   9 |      1 |
|  6 | ccc      |  10 |      0 |
|  7 | ddd      |  15 |      0 |
|  8 | eee      |  20 |      1 |
|  9 | fff      |  25 |      1 |
+----+----------+-----+--------+


例子1:update不存在的数据(有二级索引)

事务1:

begin ;
update dboop_locktest set username='ggg' where age=4;

锁范围

优化 2:索引上的等值查询,未命中,所在的Net-Key Lock,退化为GAP Lock 。

根据lock_ordinary锁的定义,需要锁:

+----------------+------------------------+-----------+-----------+-------------+-----------+
| OBJECT_NAME    | INDEX_NAME             | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
+----------------+------------------------+-----------+-----------+-------------+-----------+
| dboop_locktest | NULL                   | TABLE     | IX        | GRANTED     | NULL      |
| dboop_locktest | idx_dboop_locktest_age | RECORD    | X,GAP     | GRANTED     | 5, 2      |
+----------------+------------------------+-----------+-----------+-------------+-----------+

事务2:

insert into dboop_locktest(username,age,gender)  values ('bbb',-1,1); --成功
insert into dboop_locktest(username,age,gender)  values ('bbb',1,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',2,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',3,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',4,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',5,1);  --成功

例子2:update数据(命中二级索引)

事务1:

begin ;
update dboop_locktest set username='ggg' where age=15;

锁范围

索引上的等值查询,如果命中唯一索引,退化为行锁。如果命中普通索引,左右两边的GAP Lock + Record Lock。

根据lock_ordinary锁的定义,需要锁:

+----------------+------------------------+-----------+---------------+-------------+-----------+
| OBJECT_NAME    | INDEX_NAME             | LOCK_TYPE | LOCK_MODE     | LOCK_STATUS | LOCK_DATA |
+----------------+------------------------+-----------+---------------+-------------+-----------+
| dboop_locktest | NULL                   | TABLE     | IX            | GRANTED     | NULL      |
| dboop_locktest | idx_dboop_locktest_age | RECORD    | X             | GRANTED     | 15, 7     |
| dboop_locktest | PRIMARY                | RECORD    | X,REC_NOT_GAP | GRANTED     | 7         |
| dboop_locktest | idx_dboop_locktest_age | RECORD    | X,GAP         | GRANTED     | 20, 8     |
+----------------+------------------------+-----------+---------------+-------------+-----------+

事务2:

insert into dboop_locktest(username,age,gender)  values ('bbb',10,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',12,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',15,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',16,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',20,1);  --成功
update dboop_locktest  set username='ggg' where id=7; --失败 
update dboop_locktest  set username='ggg' where id=6; --成功
update dboop_locktest  set username='ggg' where id=8; --成功

例子3:update数据(二级索引范围)

事务1:

begin ;
update dboop_locktest set username='ggg' where age>=14 and age<17 ;

锁范围

根据lock_ordinary锁的定义,需要锁:

+----------------+------------------------+-----------+---------------+-------------+-----------+
| OBJECT_NAME    | INDEX_NAME             | LOCK_TYPE | LOCK_MODE     | LOCK_STATUS | LOCK_DATA |
+----------------+------------------------+-----------+---------------+-------------+-----------+
| dboop_locktest | NULL                   | TABLE     | IX            | GRANTED     | NULL      |
| dboop_locktest | idx_dboop_locktest_age | RECORD    | X             | GRANTED     | 15, 7     |
| dboop_locktest | idx_dboop_locktest_age | RECORD    | X             | GRANTED     | 20, 8     |
| dboop_locktest | PRIMARY                | RECORD    | X,REC_NOT_GAP | GRANTED     | 7         |
| dboop_locktest | PRIMARY                | RECORD    | X,REC_NOT_GAP | GRANTED     | 8         |
+----------------+------------------------+-----------+---------------+-------------+-----------+

事务2:

insert into dboop_locktest(username,age,gender)  values ('bbb',9,1); --成功
insert into dboop_locktest(username,age,gender)  values ('bbb',10,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',12,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',15,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',16,1);  --失败 
insert into dboop_locktest(username,age,gender)  values ('bbb',20,1);  --成功
update dboop_locktest  set username='ggg' where id=7; --失败 
update dboop_locktest  set username='ggg' where id=6; --成功
update dboop_locktest  set username='ggg' where id=8; --失败

例子3:update数据(无索引)

事务1:

begin ;
update dboop_locktest set username='ggg' where  gender=1 ;

锁范围

对无索引列的情况,需要对表中的所有行都加上lock_ordinary锁+最大间隙锁 = 锁全表记录

根据lock_ordinary锁的定义,需要锁:

+----------------+------------+-----------+-----------+-------------+------------------------+
| OBJECT_NAME    | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA              |
+----------------+------------+-----------+-----------+-------------+------------------------+
| dboop_locktest | NULL       | TABLE     | IX        | GRANTED     | NULL                   |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | supremum pseudo-record |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 1                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 2                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 3                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 4                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 5                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 6                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 7                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 8                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 9                      |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 13                     |
| dboop_locktest | PRIMARY    | RECORD    | X         | GRANTED     | 14                     |
+----------------+------------+-----------+-----------+-------------+------------------------+

事务2:

insert into dboop_locktest(username,age,gender)  values ('bbb',11,1); 
insert into dboop_locktest(username,age,gender)  values ('bbb',20,1); 
insert into dboop_locktest(username,age,gender)  values ('bbb',25,0);
insert into dboop_locktest(username,age,gender)  values ('bbb',300,0);
insert into dboop_locktest(username,age,gender)  values ('bbb',-300,0);
update dboop_locktest  set username='ggg' where id=7;
update dboop_locktest  set username='ggg' where id=6; 
update dboop_locktest  set username='ggg' where id=8; 
insert into dboop_locktest(id,username,age,gender)  values (11,'bbb',11,1); 

以上语句全部失败。

– done

>> Home

51ak

2021/02/13

Categories: mysql innodb Tags: 原创 精品

《数据库工作笔记》公众号
扫描上面的二维码,关注我的《数据库工作笔记》公众号