1 读写锁:
-
select … lock in share mode:当前读,加读锁,又叫共享锁
-
select … for update:当前读,加写锁,又叫排他锁
-
innoDB里面,update,delete,insert都会自动给涉及的语句添加写锁
读锁(共享锁)
读取结果集的最新版本,同时防止其他事务产生更新的数据版本。
本读取模式在读取前后对资源处理如下:
-
读取行为发生之前,获取读锁。这意味着如果有其他尚未提交的事务已经修改了结果集,本读取模式会等待这些事务结束,以确保自己稍后可以读取到这些事务对结果集的修改。
-
读取行为发生之后,当前事务提交之前,本读取模式会阻塞其他事务对结果集的修改。
-
当前事务提交后,释放读锁。这意味着所有之前被阻塞的事务可恢复继续执行。
写锁(排他锁)
写锁拥有读锁的一切功能,同时它还额外具备阻止其他事务读取最新版本的能力。
本读取模式在读取前后对资源的处理如下:
-
读取行为发生之前,获取写锁。这意味着如果有其他尚未提交的事务已经修改了结果集,本读取模式会等待这些事务结束,以确保自己稍后可以读取到这些事务对结果集的修改。
-
读取行为发生之后,当前事务提交之前,本读取模式会阻塞其他事务对结果集的修改,也会阻塞其他事务对结果集最新版本的读取(注:其他事务仍可以读取快照版本)。
-
当前事务提交后,释放写锁。这意味着所有之前被阻塞的事务可恢复继续执行。
2 写锁的分类
数据库加写锁的时候可能会添加表锁,行锁和间隙锁
案例数据:表名 t
id(主键) | c(普通索引) | d(无索引) |
---|---|---|
5 | 5 | 5 |
10 | 10 | 10 |
15 | 15 | 15 |
20 | 20 | 20 |
25 | 25 | 25 |
表锁
表锁会锁定整张表,如果当前有用户正在执行写操作并且获取了写锁,这可能导致整张表被锁定,阻塞其他用户的读写操作。如果用户执行的是读操作,则会获取读锁,此时其他用户的并发读操作将被接受,写操作会被阻塞。
行锁
行锁的粒度是在每一条行数据,这意味行锁可以尽可能的支持并发处理,相应的行锁开销也会比较大。并且,在InnoDB中的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则行锁将会自动升级为表锁。
间隙锁
间隙锁(Gap Lock)是Innodb在提交下为了解决幻读问题时引入的锁机制。在可重复读隔离级别下,数据库锁是通过行锁和间隙锁(开区间)共同组成来实现的。(-∞,5)5(5,10)10(10,15)15(15,20)20(20,25)25(25,+supernum] (其中supernum是数据库维护的最大的值。)
临键锁(next-key lock)
间隙锁和行锁结合一起就是临键锁。左开右闭。(-∞,5](5,10](10,15](15,20](20,25](25,+supernum]
3 加锁规则
规则1:InnoDB的行锁是基于索引实现的,通过索引访问时使用行锁,如果不通过索引访问数据,InnoDB会使用表锁
update t set c=2 where c=5
,c列有索引,加的是行锁update t set d=2 where d=5
,d列无索引,加的是表锁
规则2:走索引的时候
- 加锁的基本单位是(next-key lock),他是前开后闭原则
- 插叙过程中访问的对象会增加锁
- 索引上的等值查询–给唯一索引加锁的时候,next-key lock升级为行锁
- 索引上的等值查询–向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁
- 唯一索引上的范围查询会访问到不满足条件的第一个值为止
案例:
update t set d= d+ 1 where id = 10;
,会添加id=10的行锁,因为是唯一索引,(5,10]的临键锁升级为行锁update t set d= d+ 1 where d = 10;
,会添加(5,10]的临键锁和(10,15)的间隙锁,注意15右边是开的,因为是等值查询,id = 15 不满足id= 10的查询要求,临键锁退化为间隙锁select * form t where id >= 10 and id <11 for update;
,会添加id=10的行锁(10,15]的临键锁select * form t where id >= 10 and id <11 for update;
,会添加(5,10]和(10,15]的临键锁