MySQL 锁
事务级别与脏读幻读
s锁和x锁
共享锁(s锁,share lock,读锁):一个事务lock in share mode,其他事务读数据不能更新数据,只能加s锁进行读数据,阻塞其它事务修改真实数据。
排他锁(x锁,exclusive lock,写锁):一个事务for update 加上x锁,其他事务不能对相关数据进行加其他锁。update,insert,delete默认会加上排他锁。
意向锁(Itention Locks)
表示当前表中有记录已被上锁。
比如某行数据被加了s锁,则这张表上就会增加一个IS的意向锁标值。同理x锁。
该锁能够快速判断该表已经有记录上锁了,避免遍历来查看锁,提高加锁效率。
行锁、表锁和页面锁
MyISAM和MEMORY储存引擎采用表级锁
BDB储存引擎采用页面锁
InnoDB既支持行级锁也支持表级锁,默认采用行级锁。
表锁:开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突概率大,并发度低。
行锁:开销大,加锁慢,会出现死锁,锁定粒度小,发生锁冲突概率小,并发度高。
页面锁:性能介于行锁表锁之间,会出现死锁
对一个表大量使用行锁,是会升级为表锁的
行锁是通过给索引上的索引项加锁来实现的,通过索引条件使用行锁,否则使用表锁。
InnoDB select默认不加任何锁。手动指定lock in share mode 共享锁、for update 排他锁
什么情况下使用什么锁?
当使用update、insert、delete、select… for update
时,有指定的索引列,则使用行锁;
如果没有指定索引,则InnoDB使用表锁。
for update排他锁没有线程对该结果集中的任何行数据使用排他锁或共享锁,否则申请会阻塞
。select * from account where id > 8 and id <> 10 for update;
(会对查询结果集中每行数据都添加排他锁)
next-key、记录锁、间隙锁
间隙锁和next-key锁都是RR隔离级别特有的
Next-key锁:必须在RR隔离级别下,行锁默认使用next-key锁。锁记录本身,同时还要锁记录之间的间隙,例如select * from student where id > 3 and id<=5 for update
会锁住区间(3,5],(5,~) 的数据。【锁住的区间会包含最后一个record的右边的临键区间】
当该查询没匹配到任何记录的时候退化为间隙锁。
间隙锁(Gap Lock):必须在RR隔离级别下,行锁默认next-key锁,当使用索引查询没有查到任何记录的时候退化为间隙锁。假设目前仅存在id=1和id=4。select * from student where id = 3 for update,select * from student where id > 1and id < 4 for update,会锁住(1,4)区间的值。
记录锁(Record Lock):mysql默认的行锁是next-key锁。当使用唯一性索引等值查询匹配到记录时,退化为记录锁。
next-key:包含了间隙锁和记录锁。
InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引检索数据的时候InnoDB才使用行锁,否则只能用表锁。
间隙锁的目的:
- 防止幻读、防止间隙内有新数据插入
- 防止已存在的数据更新为间隙内的数据。
如果看到这里了,强烈推荐看看这篇博客:Mysql加锁过程详解(9)-innodb下的记录锁,间隙锁,next-key锁
超级详细的间隙锁案例,值得学习。
参考资料:
- 面试专栏 72期 MySQL是如何实现ACID的
- Mysql X锁,S锁、间隙锁、死锁