MySQL读锁的区别和应用场景分析
读锁的概念和区别
如果在MySQL
的事务里查询数据,然后在同一事务中插入或更新相关数据,常规的SELECT
语句不能提供足够的保护。其他并行的事务可以更新或删除第一个事务里刚查询的相同行。InnoDB
支持两种类型的读锁,提供了额外的安全性:
SELECT ... LOCK IN SHARE MODE
在读取到的行上设置共享锁。其他会话可以读取行,也可以继续给行加共享锁,但是在当前事务提交之前其他会话不能修改加了共享锁的行。如果这些行中的任何一个被尚未提交的另一个事务更改,则当前查询将等待直到该事务结束,然后使用最新值。
SELECT ... FOR UPDATE
用排他锁锁定行和任何关联的索引条目,就像在这些行上执行
UPDATE
语句一样。禁止其他事务在这些加了锁的行上进行UPDATE
、执行LOCK IN SHARE MODE
或者读取某些事务隔离级别的数据。
通过对比,发现FOR UPDATE
的加锁方式类似并发编程里的写锁,而LOCK IN SHARE MODE
则是读锁,同一时间点相同的行上只允许出现一个写锁,或者是多个读锁。一旦有一种锁在数据行上成功加上了锁,另外一种加锁尝试就会进入等待。
这两种锁都不会阻塞普通SELECT
语句读取这些行,一致的读(快照读)将忽略行记录上设置的任何锁。(行记录的旧版本无法被锁定;可以通过在行记录的内存副本上应用undo log重构它们。)
应用场景
SELECT ... LOCK IN SHARE MODE
的应用场景适合于两张表存在关系时的写操作,拿MySQL
官方文档的例子来说,假如存在两张有关系的表:PARENT和CHILD,使用普通的SELECT语句(快照读)来查询表PARENT并验证父行是否存在后再将子行插入CHILD表,这种方式安全吗?答案是否定的,因为其他会话可能会在你这个会话的SELECT和INSERT之间的某个时间点删除了父行,这个删除操作你是无法察觉到的。
为避免这种潜在的问题,我们使用“加共享锁”的方式执行SELECT:
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;
在LOCK IN SHARE MODE
查询返回PARENT表里名为" Jones"的数据行之后,你就可以安全地将子记录添加到CHILD表中并提交事务。在事务提交前任何试图在PARENT表的对应行上获取排他锁的事务都将等到你完成操作提交事务后再进行。
但是如果是同一张表的应用场景,举个例子,电商系统中在产生订单之前需要确认商品数量大于1,产生订单之后应该将商品数量减1。
1. | select id, amount from products where product_name='Apple11' |
---|---|
2. | update products set amount=amount-1 where id = {id} |
显然上面的做法是是有问题,因为如果1查询出amount的值为1,但是这时正好其他会话也买了该商品并产生了订单,那么amount就变成了0,这时这个会话里的第二步再执行就有问题。
那么可以使用LOCK IN SHARE MODE
给这行加共享锁可行吗?也是不合理的,因为两个会话同时用共享读锁锁定该行记录时,这时两个会话再进行第二步的UPDATE时都会等待其他事务的读锁释放,这必然会产生死锁导致其中一个事务回滚。
时间 | 事务1 | 事务2 |
---|---|---|
1 | SELECT id, amount FROM products WHERE product_name='Apple11' LOCK IN SHARE MODE; | |
2 | 判断amount > 1 | SELECT id, amount FROM products WHERE product_name='Apple11' LOCK IN SHARE MODE; |
3 | UPDATE products SET amount=amount-1 WHERE id = {id}; | 判断amount > 1 |
4 | 等待事务2释放读锁 | UPDATE products SET amount=amount-1 WHERE id = {id}; |
5 | 继续等待 | 检测到死锁 ( Deadlock found when trying to get lock; try restarting transaction) |
6 | 继续等待 | 回滚事务 |
7 | 事务提交 |
通过上面这个案例可知LOCK IN SHARE MODE的方式在这个场景中不适用,如果两个事务以共享模式锁定该行,则任何人都将无法更新该行(当其他事务持有该行的锁时,不允许进行更新)。在这种情况下真正发生的是,两者之一将超时,释放锁,然后另一个将成功更新该行。
我们需要使用FOR UPDATE的方式直接加写锁,从而短暂地阻塞事务2。下面是两个操作根据发生时间的推演过程
时间 | 事务1 | 事务2 |
---|---|---|
1 | SELECT id, amount FROM products WHERE product_name='Apple11' FOR UPDATE | |
2 | 判断amount > 1 | SELECT id, amount FROM products WHERE product_name='Apple11' FOR UPDATE |
3 | UPDATE products SET amount=amount-1 WHERE id = {id}; | 等待事务1释放写锁 |
4 | 事务提交 | 等待事务1释放写锁 |
5 | 判断amount > 1 不成立 | |
6 | 事务提交退出 |
通过上面两个案例的分析,我们可以得出两种读锁的适用场景。LOCK IN SHARE MODE
适合用于两张表存在业务关系时的一致性要求,而FOR UPDATE
适用于操作同一张表时保证业务的一致性要求。
总结
LOCK IN SHARE MODE是共享锁,多个事务允许同时持有一行的读锁。
FOR UPDATE 是独占锁,事务用FOR UPDATE锁定行后,会阻塞其他事务对该行的写锁和读锁的获取,反之亦然。
任何行锁都不影响普通SELECT查询的快照读,保证了MySQL的并发能力。
LOCK IN SHARE MODE 适合用于两张表存在业务关系上的一致性要求时的操作场景。
FOR UPDATE 适用于操作同一张表时保证业务的一致性要求。
MySQL读锁的区别和应用场景分析相关推荐
- Java NIO (十四)NIO 和 IO 的区别和适用场景分析
在研究Java NIO和IO API时,很快就会想到一个问题: 什么时候应该使用IO,什么时候应该使用NIO? 在本文中,我将尝试阐明Java NIO和IO之间的区别,它们的用例以及它们如何影响代码的 ...
- mysql 事务 查询 范围加锁_MySQL死锁系列-常见加锁场景分析
本文我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能举一反三,灵活地分析真实开发过程中遇到的加锁问题. 如下图所示,数据库的隔离等级,SQL 语句和当前数据库 ...
- mysql常见死锁_MySQL死锁系列-常见加锁场景分析
如下图所示,数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量. 下面,我们会首先讲解一下隔离等级.不同 SQL 语句 和 当前数据库数据对 ...
- mysql where从句_MySQL死锁系列-常见加锁场景分析
如下图所示,数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量. 下面,我们会首先讲解一下隔离等级.不同 SQL 语句 和 当前数据库数据对 ...
- mysql死锁影响_MySQL死锁系列-常见加锁场景分析
各位看官内容喜欢的话,动动手指点个 ,点个关注呗!!谢谢支持! 如下图所示,数据库的隔离等级,SQL 语句和当前数据库数据会共同影响该条 SQL 执行时数据库生成的锁模式,锁类型和锁数量. 下面,我们 ...
- mysql锁场景_MySQL死锁系列-常见加锁场景分析
在上一篇文章<锁的类型以及加锁原理>主要总结了 MySQL 锁的类型和模式以及基本的加锁原理,今天我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能 ...
- mysql表分区占用存储_MySQL 分区分表应用场景分析和分区中可能遇到的坑点
MySQL的分区和分表应用场景分析 在日常工作中当我们的某张表的数据量过大的时候,首当其冲的可能就是进行分区和分表,但是是如何分区或者分表都要结合一点的业务场景下进行分析,才会显著的提升性能,来聊一聊 ...
- mysql 分区表_MySQL 分区分表应用场景分析和分区中可能遇到的坑点
MySQL的分区和分表应用场景分析 在日常工作中当我们的某张表的数据量过大的时候,首当其冲的可能就是进行分区和分表,但是是如何分区或者分表都要结合一点的业务场景下进行分析,才会显著的提升性能,来聊一聊 ...
- 了解爱陆通5G工业路由器和5G工业CPE的区别,5G工业网关应用场景分析
爱陆通5G工业路由器:路由器大家都很熟悉,几乎每家每户都有一个路由器用来上网,5G工业路由器功能和家用路由器的基本一样,区别在于应用场景为工业现场,通过5G网络给工业现场终端提供通信网络.5G工业路由 ...
最新文章
- k8s helm 私服chartmuseum minio s3 存储配置
- LVS/NAT 配置
- J2ME Nokia 模拟器 安装运行
- ElasticSearch搜索引擎: 内存分析与设置
- spring 的配置 beanpropertyname属性
- datagrid 什么时候结束编辑_孕吐到底什么时候结束
- flash一个按钮控制动画_PLC三组灯用一个按钮控制
- 【学员管理系统】0x01 班级信息管理功能
- 从零开始构建 RPM 包
- JNI/NDK入门指南之正确姿势了解JNI和NDK
- erp系统的优点和不足?云系统给企业带来的好处?
- 装了双系统怎么删除一个
- 在perl中简单的正则匹配
- 专注与拓展-向携程学习
- Elasticsearch:在 Elasticsearch 中使用语言识别进行多语言搜索
- Linux(十二)中断系统
- 如何应对学习知识、技能不用就会忘(节选自《穷查理宝典》第十一讲:人类误判心理学之不用就忘倾向)
- Jenkins中Changelog插件使用
- amp sqlserver中 什么意思_PLSQL中是什么意思
- 总结一下软通外派阿里的面试题
热门文章
- 浅谈,JavaScript 运行机制和Event Loop
- 负margin在布局中的运用(*****************************************************************)...
- 使用 PSD Validator 在线校验 PSD 文件的质量
- 团队冲刺第二阶段-9
- Redis笔记(一)
- 数据结构2 - 线性表
- 解决 python中 使用tesserocr,File tesserocr.pyx, line 2401, in tesserocr._tesserocr.image_to_text 报错问题...
- python基础3——运算符
- Android微信跳一跳,自动跳App实现
- P1855 榨取kkksc03