【问题标题】:Oracle SQL update double-check lockingOracle SQL 更新双重检查锁定
【发布时间】:2016-08-30 12:26:21
【问题描述】:

假设我们有一个表 A,其中包含字段 time: datestatus: intplayerId: intserverid: int

我们添加了对时间、playerid 和 serverid 的限制 (UNQ_TIME_PLAYERID_SERVERID)

有时我们尝试用新的状态和日期更新表 A 中的所有行:
更新状态 = 1, 时间 = sysdate where serverid=XXX and status != 1 and time >系统日期

问题是在不同的机器上有两个独立的进程可以在相同的 sysdate 执行相同的更新。

并且发生UNQ_TIME_PLAYERID_SERVERID违规!

是否有可能强制 Oracle 在具体更新之前检查原因(获取行锁定时)?

我不想使用任何'select for update'东西

【问题讨论】:

  • 为什么会出现这个问题?只需抓住错误并忽略它。
  • 我不想简单地忽略错误。是的,有可能。但我实际上想知道是否有可能编写更新该错误根本不会出现。
  • 你不能。就在您得到肯定回答之后,表格(行)可以被锁定。做这种事情的唯一可靠方法是 try catch。
  • 如果有 2 个具有相同 playerId 和不同时间的记录满足 where 子句怎么办?甚至一个更新查询都会达到您的约束。您可能需要重新考虑您的应用程序逻辑。
  • @MartinSchapendonk 是的,对于同一个玩家 ID,我们有 2 次或更多次可能的情况。是的,你是对的。我们在索引中有另一个字段。我会更新问题

标签: sql oracle sql-update


【解决方案1】:

如果 100% 的时间确实是相同的更新,那么只需捕获异常并忽略它。

如果您想首先防止错误发生,您需要实现一些逻辑来防止第二个更新语句执行。

我可以为此考虑一个“锁定表”。创建一个表 TABLE_A_LOCK_TB (根据您出于管理原因希望存储在那里的信息添加列,例如设置锁定或时间戳的用户,...)。

在对表 A 执行更新语句之前,只需在 TABLE_A_LOCK_TB 中插入一行。更新成功后,删除该行。

在表 A 上执行任何更新语句之前,只需检查 TABLE_A_LOCK_TB 是否有数据集。如果不是你的更新是好的,如果是你不执行更新。

为了使这个过程更容易,您可以通过从 TABLE_A_LOCK_TB 中插入/删除一行来编写一个用于“锁定”和“解锁”表 A 的包。还要实现一个函数来检查“锁定状态”。

如果您需要为多个表使用此逻辑,您还可以通过在 TABLE_A_LOCK_TB 中保留一个包含表名的列并对其进行检查来使其动态化。

在您的应用程序逻辑中,您可以像这样处理每个更新(伪代码):

IF your_lock_package.lock_status(table_name) = false THEN 

     your_lock_package.set_lock(table_name);
     -- update statement(s)
     your_lock_package.release_lock(table_name);

ELSE
    -- "error" handling / information to user + exit

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-08
    • 1970-01-01
    相关资源
    最近更新 更多