行锁
行锁(record lock): 请注意它是针对索引的锁(所以如果没有索引时,最终行锁就会导致整个表都会被锁住)
行锁种类
共享锁(S Lock): 也叫读锁
- 允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁,即写操作
上锁方式
select * from tableName limit 1 //基于MVCC方式,不上锁
select * from tableName lock in share mode;//读锁(显示)
排它锁(X Lock): 也叫写锁
允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享锁和排他锁 即读和写
上锁方式
insert、update、delete //上写锁(隐式)
select * from tableName for update;//写锁(显示)
意向共享锁(IS)
1.一个事务给一个数据行加共享锁时,必须先获得表的IS锁
意向排它锁(IX)
1.一个事务给一个数据行加排他锁时,必须先获得该表的IX锁
行锁的实现算法
不同算法实现不同的读锁或者写错
Record Lock 锁
单个行记录上的锁
Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表建立的时候没有设置任何一个索引,这时InnoDB存储引擎会使用隐式的主键来进行锁定
使用方式: 根据主键/唯一键锁定确定的记录
update table set xx=xx where id =1//id 唯一索引
select * from tableName where id=1 lock in share mode
Gap Lock 锁:也叫间隙锁
针对索引之间的间隙加锁。需要注意,这仅仅是会影响间隙的插入,不会影响更新
使用方式:普通索引或者范围查询
update table set xx=xx where userName = ‘张三’//userName普通索引
select * from tableName where userName=‘张三’ lock in share mode //userName普通索引
Select * from emp where empid > 100 for update; //范围查询,会对mpid值大于为100所有记录全部枷锁
案例分析
id 唯一索引
uid 普通索引
id | uid | name |
---|---|---|
1 | 10 | 李四 |
5 | 20 | 王二 |
10 | 30 | 张三 |
15 | 25 | 王五 |
案例一:
START TRANSACTION;
SELECT * from ti WHERE uid=20 for update;
或
update ti set nane="李四二" where uid=20;
解释:UID为普通索引,UID会根据大小排序,即20的前后是【10,25】此时:INSERT 语句的UID在【10,25】都是无法插入的,但是更新除了uid=20其他都可以正常更新。
即间隙范围:【10,25】
案例二:
START TRANSACTION;
SELECT * from ti WHERE uid=10 for update;
间隙范围:【10,20】
Next-key Lock 锁
同时锁住数据+间隙锁
在Repeatable Read隔离级别下,Next-key Lock 算法是默认的行记录锁定算法。
使用方式:同间隙锁一样
三种方式区别
- 行锁针对确定的记录
- 间隙锁是两个确定记录之间的范围,即包含行锁的记录;
- next key lock则是除了间隙还包括确定的记录
参考资料:
https://blog.csdn.net/python8989/article/details/113918316
https://www.cnblogs.com/yaochunhui/p/14186371.html
https://learnku.com/articles/39212
https://segmentfault.com/a/1190000011164489
https://blog.csdn.net/qq_40378034/article/details/90904573
模拟数据;
CREATE TABLE `tn` (
`id` int(11) unsigned NOT NULL,
`uid` int(11) unsigned NOT NULL
) ENGINE=InnoDB;
CREATE TABLE `tu` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u_uid` (`uid`)
) ENGINE=InnoDB;
CREATE TABLE `ti` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `u_uid` (`uid`)
) ENGINE=InnoDB;
INSERT INTO `tn` (`id`, `uid`) VALUES (1, 10), (5, 20), (10, 30);
INSERT INTO `tu` (`id`, `uid`) VALUES (1, 10), (5, 20), (10, 30);
INSERT INTO `ti` (`id`, `uid`) VALUES (1, 10), (5, 20), (10, 30);