为什么 SELECT FOR UPDATE 只在事务中起作用
Posted 郭慕荣博客园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么 SELECT FOR UPDATE 只在事务中起作用相关的知识,希望对你有一定的参考价值。
在MySQL中SELECT FOR UPDATE建议要在事务中运行,原因是当SELECT FOR UPDATE执行完之后,就释放锁了。其实查询出来的数据接下来还要更新,所以建议必须要在事务中运行,针对spring事务其实就是加注解@Transaction。
图二中显示一直转圈圈,说明上一个图中已经上了锁,第二图的更新必须要在图一结束之后才能进行更新,防止数据出现问题。
for update的使用场景
如果遇到存在高并发并且对于数据的准确性很有要求的场景,是需要了解和使用for update的。
比如涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。如果库存刚开始读的时候是1,而立马另一个进程进行了update将库存更新为0了,而事务还没有结束,会将错的数据一直执行下去,就会有问题。所以需要for upate 进行数据加锁防止高并发时候数据出错。
记住一个原则:一锁二判三更新
排他锁的申请前提
没有线程对该结果集中的任何行数据使用排他锁或共享锁,否则申请会阻塞。
for update仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时,通过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。
PostgreSQL SELECT ... FOR UPDATE:并发长时间运行的查询会发生啥?
【中文标题】PostgreSQL SELECT ... FOR UPDATE:并发长时间运行的查询会发生啥?【英文标题】:PostgreSQL SELECT ... FOR UPDATE: What happens with concurrent long running queries?PostgreSQL SELECT ... FOR UPDATE:并发长时间运行的查询会发生什么? 【发布时间】:2021-07-14 18:30:42 【问题描述】:我想知道当两个事务并行执行SELECT ... FOR UPDATE
查询时会发生什么。背景是我想用SELECT ... FOR UPDATE
和SKIP LOCKED
实现一个作业队列,如下图:https://vladmihalcea.com/database-job-queue-skip-locked/。但在本文中,查询非常琐碎。
有两个事务 T1 和 T2 的示例(事务隔离级别设置为 READ_COMMITTED
):
-
T1 开始
T1 执行
SELECT ... FOR UPDATE
,搜索新行,这需要一些时间。
T2 开始
T2 使用与 T1 相同的 WHERE 子句和参数执行 SELECT ... FOR UPDATE
,这也需要一些时间。
T1 终于找到所有行,锁定它们
T1 开始更新行(例如,将它们标记为现在为 IN_PROGRESS)
T2 终于找到行 => 现在会发生什么?
一些问题:
-
我假设 T1 在原子操作中锁定行。这是正确的吗?
那么,当 T2 最终找到它的结果集并尝试锁定它无法做到的行时?在这种情况下,T2 如何反应?我的假设是它会等到 T1 释放锁(当不使用 NOWAIT 时)。
如果 T2 在 T1 进行任何更改(例如,将作业状态从 NEW 更改为 IN_PROGRESS)之前完成查询怎么办?两个事务能否找到相同的结果集?
如果 T1 以某种方式标记锁定的行(例如,通过将状态列从 NEW 更改为 IN_PROGRESS)并且 T2 查找原始状态 (NEW),那么 T2 是否会使用
SKIP LOCKED
跳过标记的行? T2 会在 T1 进行更改后重新评估其结果集吗?
【问题讨论】:
【参考方案1】:每个事务在找到行时都会锁定它们,因此锁定不是原子的。可能会发生 T1 锁定几行而 T2 锁定其他一些行的情况。
由于每个事务在找到行时会立即锁定它们,因此不会发生这种情况。一行被锁定,在这种情况下被跳过,或者它没有被锁定,在这种情况下它被锁定。
如果 T1 在 T2 完成扫描表之前提交,T2 将愉快地锁定所有已由 T1 处理的行。
是的,这会起作用。 T2 将在检查条件并锁定行之前获取每行的最新版本。
【讨论】:
感谢您的信息,这澄清了一些问题!如果没有SKIP LOCKED
,T2 会在找到符合所有条件并被锁定的行时,等到它被解锁(另一个事务提交更改)然后检查条件并跳过它或再次锁定它(如果条件仍然成立)?使用SKIP LOCKED
,它不会等待,而是继续下一个匹配条件?还有NOWAIT
会报错吗?
三个问题都是肯定的!
非常感谢! :)以上是关于为什么 SELECT FOR UPDATE 只在事务中起作用的主要内容,如果未能解决你的问题,请参考以下文章
为啥不支持它的数据库可以简单地忽略 select_for_update?