###InnoDB 加锁方式(REPEATABLE-READ)
共享锁:允许事物读取数据,阻止其它事物获取该数据集的排他锁
排他锁:允许获取锁的事物更新数据集,阻止其它事物获取数据集的共享 锁和排他锁。
根据锁定数据集的大小,mysql 的排他分为表锁和行锁
- 表锁,锁定整个表
- 行锁,锁定一个数据子集
在 InnoDB,行锁是通过给索引上的索引项加锁来实现的,也就是说,只有通过索引条件检索数据,才能使用行锁,否则会使用表锁。
表锁:
无索引
session1 | session2 |
---|---|
set autocommit = false | |
update lock_test set fkey = 'A' where c = 1 | |
Rows matched: 2 Changed: 2 Warnings: 0 | |
update lock_test set fkey = 'C' where c = 2 | |
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
行锁:
- 对字段 c 加普通索引
session1 | session2 |
---|---|
set autocommit = false | |
update lock_test set fkey = 'A' where c = 1 | |
Rows matched: 2 Changed: 2 Warnings: 0 | |
update lock_test set fkey = 'C' where c = 2 | |
Rows matched: 2 Changed: 2 Warnings: 0 |
使用主键索引、唯一索引或普通索引,InnoDB 都会使用行锁来对数据加锁。
- 对字段 C 加普通索引
session1 | session2 |
---|---|
set autocommit = false | |
update lock_test set fkey = 'A' where c = 1 and fkey = 'D' | |
Rows matched: 1 Changed: 1 Warnings: 0 | |
update lock_test set fkey = 'C' where c = 2 and fkey = 'M' | |
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
InnoDB 对索引项加锁,尽管更新两条不同的数据,但使用相同的索引项
注意:
- 不是使用了索引字段过滤就会使用行级锁,需要 explain 看下 mysql 具体的执行计划
- InnoDB 行级锁是对索引项加锁,即
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | UPDATE | lock_test | NULL | range | idx_lock | idx_lock | 8 | const | 2 | 100.00 | Using where; Using temporary |
rows 对应的数据写操作均会被锁定
###间隙锁
-
当使用范围查询时,InnoDB 不仅会对存在的数据加锁,也会对不存在的空隙加锁,即间隙锁(max(id)=10 | where id > 9 | 对 10 加锁 | 对 >10 空隙加锁)
-
这种加锁的目的是为了防止幻读, 防治写操作过程中出现其它 session 提交了 id > 10 的数据。
间隙锁:
- 对不存在的索引间隙添加锁,阻止对该间隙的修改.
session1 | session2 |
---|---|
set autocommit = false | |
update lock_test set fkey = 'A' where c < 6 | |
Rows matched: 1 Changed: 1 Warnings: 0 | |
insert into lock_test(c, fkey) values (2, 'T'); | |
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
session1 | session2 |
---|---|
set autocommit = false | |
update lock_test set fkey = 'A' where c < 6 | |
Rows matched: 1 Changed: 1 Warnings: 0 | |
update lock_test set fkey = 'A' where id = 2; 注释:存在一条 id = 2 and c = 3 的数据 | |
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
session1 | session2 |
---|---|
set autocommit = false | |
update lock_test set fkey = 'A' where c < 6 | |
Rows matched: 1 Changed: 1 Warnings: 0 | |
insert into lock_test(c, fkey) values (7, 'CA'); | |
Rows matched: 1 Changed: 1 Warnings: 0 |
- 当 update 过滤条件为范围查询并且使用普通索引时,会对普通索引不存在部分添加间隙锁,主键索引数据添加排他锁。
- 当 update 过滤条件为范围查询并且使用主键索引时,会对主键索引添加间隙锁。
- 当执行 insert 时,会同时对普通索引以及主键添加间隙锁
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于