3.锁
mysql加锁相关知识: https://www.cnblogs.com/crazylqy/p/7611069.html
Mysql锁系统总结:https://www.infoq.cn/article/ZAu0eWzsdTX9zoFr6C8W
1.锁
1.1.分类
- 按照锁数据的粒度
- 表锁。
- 行锁。隔离级别RR、RC,update、delete等语句命中索引时加上的锁都是行锁。只有LOCK TABLE …READ和LOCK TABLE … WRITE才能申请表级别的锁。
- 页锁。
- 按照锁的类型
- 共享锁(S锁):假设事务T1对数据A加上共享锁,那么事务T2可以读数据A,不能修改数据A。
- 排它锁(X锁):假设事务T1对数据A加上共享锁,那么事务T2不能读数据A,不能修改数据A。
- 按照状态
- 意向共享锁(IS锁):一个事务在获取(任何一行/或者全表)S锁之前,一定会先在所在的表上加IS锁。
- 意向排它锁(IX锁):一个事务在获取(任何一行/或者全表)X锁之前,一定会先在所在的表上加IX锁。
- 特殊
- 主键锁:自增的时候触发:锁[maxId, 正无穷)。
- 间隙锁,范围查询时,锁定一个范围。比如 id > 10,锁定范围 (10,无穷],即使没有数据。
1.2.为什么需要锁
锁机制用于管理对共享资源的并发访问,是对数据库的一种保护机制,也是数据库在事务操作中保证事务数据一致性和完整性的一种机制。
2.间隙锁
Mysql间隙锁是什么?主要用来解决什么问题的?
https://blog.csdn.net/sinat_27143551/article/details/81736330
Mysql隔离级别是可重复读,主要是用来解决幻读的问题(当然隔离级别为串行化的时候也可以,就是效率太低了)
死锁:MySQL在RR隔离级别下,间隙锁是默认开启,可能导致死锁。原因如下:
- 间隙锁范围交叉。间隙锁的范围交叉,会导致死锁。
- 间隙锁与插入意向锁冲突。
- 插入操作会先获取插入意向锁(Insert Intention Lock),而插入意向锁与已有的间隙锁互斥:
- 事务A锁定区间 (5, 10)(例如 DELETE FROM t WHERE id=7)。
- 事务B尝试插入 id=8,需要获取 (5, 10) 的插入意向锁,但被事务A的间隙锁阻塞。
- 事务A尝试插入 id=8(例如回滚后重新插入),需要获取自己的间隙锁,但此时事务B已在等待队列中。
- 结果:事务A等待事务B释放插入意向锁,事务B等待事务A释放间隙锁,触发死锁。
3.常见问题
MySQL,在数据库隔离级别是RR是,update table set b = ? where a = ?, 在a字段分别是主键、普通索引,唯一索引,没有索引的时候MySQL是什么加锁的?
主键 行锁, mysql 的 innodb的行锁加在主键索引上。
普通索引 间隙锁+行锁,也就是next key lock。
唯一索引 行锁
没有索引 锁全表
使用数据库能实现类型分布式锁的效果吗?
https://www.cnblogs.com/wintersoft/p/10787474.html
- 新增数据的场景:使用insert ignore实现,例如分布式定时任务执行任务。
- 砍价的业务逻辑中。
-- 1.使用悲观锁
BEGIN; -- 开始事务,必须增加需要变化的数据为查询条件,例如库存,用于进行双重校验
SELECT * FROM table_name WHERE id = 10 and num=10 FOR UPDATE;
-- 加的是行锁,并发时别的线程的SQL是阻塞的,高并发时会出现lock timeout。
COMMIT; -- 事务完成
-- 2.乐观锁
第一步:在t_share中增加一个砍价次数的字段,可以作为版本号
第二步:
BEGIN; -- 开始事务
Update table_name set num = 1 where id=10 and num=0; -- 通过修改的行数判断是否成功
COMMIT; -- 事务完成
事务锁超时(5.7): Lock wait timeout exceeded; try restarting transaction
https://blog.csdn.net/java__project/article/details/100137526
设置锁的超时时间(5.7)
https://blog.csdn.net/qq_37168856/article/details/105094465
4.死锁
发生死锁的场景?
- 批量更新数据间隙锁导致(修改当前事务的隔离级别);间隙锁和插入意向锁冲突。插入意向锁更像是队列需要前面的执行完才能轮到下一个事务执行。
- 行锁之间死锁(修改逻辑)。 事务A持有X1锁,申请X2锁。 事务B持有X2锁,申请X1锁。 两个事务死锁。
如何避免
- 降低隔离级别。RR隔离级别下,InnoDB会使用间隙锁防止幻读,但也增加了死锁风险。若无严格幻读要求,可以降为RC。
- 合理设计索引。避免索引覆盖范围过大
- 统一数据访问顺序。比较难
- 设置事务等待锁的超时时间,开启主动死锁检测。
- 谨慎使用SELECT … FOR UPDATE。使用乐观锁替代悲观锁
- 本文作者:zhangxue
- 本文链接:https://lucksnow1989.github.io/article/7.数据库/【关系型】MySQL/3.锁.html
- 版权声明:本文为本人原创文章,遵循 CC 4.0 BY-NC-SA版权协议,转载请附上原文出处链接和本声明。