MYSQL 深入浅出系列 - 锁机制
一、锁粒度
全局锁:锁整个库,一般在 mysqldum 备用的时候用,为保证数据一致性。但是引入 mvcc 后,可以做到不锁整个库就 mysqldum。
flush tables with read lock 添加全局锁
unlock tables 释放全局锁
表锁:锁住整个表。触发场景:
alter table,
drop table ,
truncate table,
flush tables with read loc,
lock tables(显式的给表上锁)
myisam 引擎不支持事务,所以对表读操作会自动加上读锁,写操作自动加上写锁
适用场景:读多写少,并发不激烈的场景
表锁的风险:性能下降、并发差,死锁(锁多个表的时候)
行锁:一般在事务中使用,如在事务外使用,则执行语句结束立即释放锁
共享锁(S 锁):
排他锁(X 锁):
select for update (加排他锁,悲观)
select ... lock in share mode 加 S 锁
insert\update\delete 操作的行都会添加一个 x 锁
行锁的风险:
1、死锁
2、锁升级:一个事务试图锁定的行过多,可能升级为表锁
3、锁等待
4、资源消耗:行锁要更多内存存储锁信息,而且需要更多 cpu 来处理锁请求和释放,如数据库中行越多,或者并发事物越多,会导致资源消息(锁是有成本的)
5、难以调式和排查:因为行锁粒度小
6、事务隔离级别:不同的隔离级别会影响锁的性能和行为
二、锁种类
乐观锁:根据版本号检查*更新,巧妙的实现
悲观锁:操作的时候加锁,适合写多读少的场景。
意向锁:为了平衡行锁和表锁之间产生的锁。如事物 A 对某一行加锁,然后事务 B 想申请表锁,这个时候如果没 IX(IS)意向锁的话,要逐一遍历行去检查有没行锁。所以意向锁是表锁,这个事务 B 此时判断事物 A 加上了意向锁,事务 B 就要等等了。(主要为了解决这一场景产生的),在加行锁的时候,mysql 会自动加上 IS 或者 IX
间隙锁:(事务隔离级别要 RR 才有该锁)
为了解决幻读,例如要查询年龄 10-20 区间的数据,它会在这个区间加锁,当另外一个事物想插入年龄=15 的数据时,会发生锁等待
临键锁:间隙锁 + 记录锁(记录所在的行锁)
三、间隙锁场景分析
在 RR 隔离级别下:
没有索引:升级到表锁,锁全表
有索引:
以上表格为例子:(age 是普通索引,id 为主键)
主键/唯一键,查询值存在的情况,只加记录锁。如 select * from stu where id = 7 for update,只会所在 id=7 行。因为有唯一约束,并发事务也不可能插入多行 id=7 的记录
主键/唯一键,查询值不存在的情况,加间隙锁。如 select * from stu where id = 8 for update,锁住间隙(7,17)
普通索引,查询值存在的情况,如 select * from stu where age = 5 for update,会锁住间隙[3,7)
普通索引,查询值不存在的情况,如 select * from stu where age = 4 for update,会锁住间隙[3,5)
版权声明: 本文为 InfoQ 作者【俊】的原创文章。
原文链接:【http://xie.infoq.cn/article/1b3b98d62510aab31e4451816】。文章转载请联系作者。
评论