为啥我们在 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 语句的NOWAITWAITSKIP LOCKED 子句。有关这些子句的信息,请参阅 Oracle 数据库 SQL 语言参考。

如果select for update 在游标循环中,那么它也允许您这样做

  update table1 set col2 = something where current of <cursor>;

SELECT FOR UPDATE 与显式游标相关联时,该游标称为FOR UPDATE 游标。只有FOR UPDATE 游标可以出现在UPDATEDELETE 语句的CURRENT OF 子句中。 (CURRENT OF 子句是 SQL 语句 UPDATEDELETEWHERE 子句的 PL/SQL 扩展,将语句限制为游标的当前行。)

【讨论】:

以上是关于为啥我们在 Oracle SQL 中需要 SELECT FOR UPDATE?的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE中能否找到未提交事务的SQL语句

为啥 oracle 中的应用程序上下文使用 PL/SQL 过程

为啥 Oracle SQL 不允许我们在条件中使用别名?

oracle sql developer中的偏移和获取

为啥 Oracle Sql*Plus 打印许多不需要的标题?

为啥oracle数据库链接不显示来自sql server的图像类型的列