文章目录
锁的分类
表锁&行锁
根据锁定的范围大小来分
- 表锁 lock tables 表名 READ/WRITE
- 行锁 select * from 表名 where id=1 for update/share
悲观锁&乐观锁
- 悲观锁
悲观锁,认为只有加锁,才能保证数据的一致性,实现有共享锁 (S)、排它锁(X) - 乐观锁
通过版本号的形式来实现,每次更新去检查下版本号,是否和之前的值相同,如果不同,则证明已经有更新操作发生,重试操作
意向锁
先说意向锁要解决的问题,解决行锁和表锁一起使用的场景
场景
事务A | 事务B |
---|---|
start transaction | |
select * from user where id=1 for update | |
start transaction | |
lock tables user read |
现在来看事务B要成功加共享锁要有两个条件
- user 表没有排它锁
- user 里面的所有行没有排它锁
看第二个问题,所有行有没有加锁,只有遍历所有行有没有排它锁,这种试效率太低,有没有更好的办法?
思考下
我们在加行锁的时候,能不能加一个其它的标记,证明已经有行锁了,于是代表这个作用的锁就产生了
- 意向共享锁 IS
- 意向排它锁 IX
意向锁特点
- 内部实现,不能外部操作
- 意向锁的级别是表锁
- 意向锁申请场景 执行for update/for share 会来申请意向锁
- 意向锁与行锁没有冲突,只会和表锁有冲突
表锁兼容性
如果锁与现有锁兼容,则将锁授予请求事务,但如果与现有锁冲突则不会。事务等待直到释放冲突的现有锁
意向排它锁示例
查看记录
- 第二行显示加了一个排它锁(X)
- 每一行显示加了一个意向排它锁(IX)
意向共享锁示例
意向共享锁的含义,在得到表中行共享锁前,必须得到意向共享锁(IS),理解可以测试用例1
- 第二行显示加了共享锁(S)
- 第一行显示加了意向共享锁(IS)
测试用例
用例1
事务A | 事务B | 事务C | 事务D | 事务E |
---|---|---|---|---|
start transaction | start transaction | start transaction | start transaction | start transaction |
select * from user where id=1 for update | ||||
select * from user where id=2 for update | lock tables user read | select * from user where id=1 for update | lock tables user write |
分析
- 事务A加了意向排它锁,排它锁
- 事务B申请行排它锁,这里只用看记录id=2上有没有排它锁,没有申请成功
- 事务C,检测到事务A已经有了意向排它锁,要加的表级别的共享锁,查看表不兼容,加锁失败
- 事务D,检测到事务A已经有了id=1的排它锁,加锁失败
- 事务E,检测到事务A已经有了意向排它锁,要加的表级别的排它锁,查看表不兼容,加锁失败
用例2
事务A | 事务B | 事务C | 事务D | 事务E |
---|---|---|---|---|
start transaction | start transaction | start transaction | start transaction | start transaction |
select * from user where id=1 for share | ||||
select * from user where id=2 for update | lock tables user read | select * from user where id=1 for share | lock tables user write |
分析
- 事务A加了意向共享锁,共享锁
- 事务B申请行排它锁,这里只用看记录id=2上有没有排它锁,没有申请成功
- 事务C,检测到事务A已经有了意向共享锁,要加的表级别的共享锁,查看表兼容,加锁成功
- 事务D,检测到事务A有了id=1的共享锁,加锁成功
- 事务E,检测到事务A已经有了意向共享锁,要加的表级别的排它锁,查看表不兼容,加锁不成功