为啥我们在 Oracle SQL 中需要 SELECT FOR UPDATE?
Posted
技术标签:
【中文标题】为啥我们在 Oracle SQL 中需要 SELECT FOR UPDATE?【英文标题】:Why we need SELECT FOR UPDATE in Oracle SQL?为什么我们在 Oracle SQL 中需要 SELECT FOR UPDATE? 【发布时间】:2021-02-17 17:55:21 【问题描述】:请注意,我是 Oracle SQL 的新手。所以根据我对任何数据库的了解,有一些方法可以处理并发。所以我想澄清的是,就像我之前提到的,据我所知,当我们并行执行两个或多个事务时,数据库会自动自己弄清楚,如何并行处理这些事务(作为可序列化的调度)通过克服读写冲突(为了实现这个数据库使用锁定机制)。所以通过最终遵循所有这些机制,我知道数据库默认情况下会确保当我们同时运行两个或多个事务时,它们之间会有隔离。所以我的问题是,当事务之间总是存在隔离时(这意味着事务之间不会发生任何读写冲突)为什么我们仍然需要使用像SELECT FOR UPDATE
这样的语句来锁定数据库中的任何数据。我的意思是为什么我们不能在事务块中继续我们想要的任何东西?
例如:
Begin transaction
//Queries
//commit
End transaction
【问题讨论】:
【参考方案1】:为什么我们不能在事务块中继续我们想要的任何东西?
因为在 Oracle 中,readers don't block writers。如果您从表中select
,您不会锁定它并且没有事务。所以,假设你这样做:
select col1, col2 into l_col1, l_col2
from table1
where ...;
if (<some logic based on l_col2`) then
update table1 set col2 = something where col1 = l_col1;
end if;
没关系;但是如果两个会话同时运行该代码,一个会话可能会更新 之间的行,而另一个会话选择并执行其更新 - 导致更新丢失或逻辑不正确,因为状态实际上并非如此正如预期的那样。
如果你改为select ... for update
:
select col1, col2 into l_col1, l_col2
from table
where ...
for update; -- or: for update of col2
if (<some logic based on l_col2`) then
update table1 set col2 = something where col1 = l_col1;
end if;
那么当第一个会话select for update
时,表被锁定,因此第二个会话必须等待该事务完成才能获得锁,它会看到第一次更新应用之后行的状态。没有丢失更新,没有混乱。
您也可以锁定不想更新的行,正如the documentation 所说:
带有
FOR UPDATE
子句的SELECT
语句(SELECT FOR UPDATE
语句)选择结果集中的行并锁定它们。SELECT FOR UPDATE
允许您基于行中的现有值进行更新,因为它确保在您更新它们之前没有其他用户可以更改这些值。您还可以使用SELECT FOR UPDATE
锁定您不想更新的行,如示例 9-6 所示。
你可以调整行为:
默认情况下,
SELECT FOR UPDATE
语句会一直等待,直到获得请求的行锁。要更改此行为,请使用SELECT FOR UPDATE
语句的NOWAIT
、WAIT
或SKIP LOCKED
子句。有关这些子句的信息,请参阅 Oracle 数据库 SQL 语言参考。
如果select for update
在游标循环中,那么它也允许您这样做
update table1 set col2 = something where current of <cursor>;
当
SELECT FOR UPDATE
与显式游标相关联时,该游标称为FOR UPDATE
游标。只有FOR UPDATE
游标可以出现在UPDATE
或DELETE
语句的CURRENT
OF 子句中。 (CURRENT OF
子句是 SQL 语句UPDATE
和DELETE
的WHERE
子句的 PL/SQL 扩展,将语句限制为游标的当前行。)
【讨论】:
以上是关于为啥我们在 Oracle SQL 中需要 SELECT FOR UPDATE?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 oracle 中的应用程序上下文使用 PL/SQL 过程