防止更新和插入之间的异步读取

Posted

技术标签:

【中文标题】防止更新和插入之间的异步读取【英文标题】:Prevent asynchronous read between update and insert 【发布时间】:2014-03-03 19:25:27 【问题描述】:

我有一位同事正在处理一个遇到问题的应用程序。他使用SqlCommand.ExecuteNonQuery 启动了一个存储过程。此存储过程在同一个表中更新一行并插入另一行。与此同时,他的应用程序继续并从表中读取。在更新和插入之间发生读取时会出现竞争条件。

有问题的数据是访问级别的记录。当访问级别更改时,它会终止(更新)旧访问级别,然后实例化(插入)新访问级别。读取会在更新和插入之间进行并且只找到终止的访问级别——这有点问题。

解决我同事问题的最佳方法是什么?


我掌握了他试图修复的存储过程:
BEGIN
SELECT OBJECT_ACCESS_ID, PERSON_AUTH_LEVEL
INTO lAccessID, lExistingAccessLevel
FROM SHPAZ.SH_PAZ_OBJECT_ACCESS
WHERE
    USER_ID = pUserID
    AND (GRGR_ID = pGroupID OR (GRGR_ID IS NULL AND pGroupID IS NULL))
    AND SYSDATE BETWEEN OBJECT_ACCESS_EFF_DATE AND OBJECT_ACCESS_END_DATE
FOR UPDATE;

-- If the new access level is the same as the existing, then do nothing.
IF lExistingAccessLevel = pLevel THEN
    RETURN;
END IF;

-- Terminate the existing record.
UPDATE SHPAZ.SH_PAZ_OBJECT_ACCESS
SET OBJECT_ACCESS_END_DATE = SYSDATE
WHERE OBJECT_ACCESS_ID = lAccessID;

-- Create the new record.
SELECT CASE WHEN pGroupID IS NULL THEN 'Broker' ELSE 'Employer' END
INTO lSource
FROM DUAL;

INSERT INTO SHPAZ.SH_PAZ_OBJECT_ACCESS (USER_ID, GRGR_ID, SOURCE, PERSON_AUTH_LEVEL, OBJECT_ACCESS_EFF_DATE, OBJECT_ACCESS_END_DATE) 
VALUES (pUserID, pGroupID, lSource, pLevel, SYSDATE, TO_DATE('12/31/2199', 'MM/DD/YYYY'));

COMMIT;
EXCEPTION
-- If there is no record, then just create a new one.
WHEN NO_DATA_FOUND THEN
    SELECT CASE WHEN pGroupID IS NULL THEN 'Broker' ELSE 'Employer' END
    INTO lSource
    FROM DUAL;

    INSERT INTO SHPAZ.SH_PAZ_OBJECT_ACCESS (USER_ID, GRGR_ID, SOURCE, PERSON_AUTH_LEVEL, OBJECT_ACCESS_EFF_DATE, OBJECT_ACCESS_END_DATE) 
    VALUES (pUserID, pGroupID, lSource, pLevel, SYSDATE, TO_DATE('12/31/2199', 'MM/DD/YYYY'));
END SHSP_SET_USER_ACCESS; 

【问题讨论】:

不要在更新和插入之间提交,问题不存在。 @Mat 据我所知,他没有承诺。是否有自动提交的操作或我们应该查看的数据库设置? 您的存储过程需要包装在事务中。 在 NO_DATA_FOUND 处理程序中的 INSERT 之后添加一个 COMMIT。分享和享受。 “同时他的应用程序继续并从表中读取”。这个应用程序是否从工作线程触发存储过程,然后立即继续从表中读取数据?如果是这样,这一切都发生在同一个 Oracle 会话/连接中吗? 【参考方案1】:

解决方案是从您的过程中删除commit,并拥有它 程序返回后完成。假设您创建了名称为 my_procedure 的过程:

SQL> exec my_procedure(my_in_arg, my_out_arg);
SQL> commit;

当原子功能操作包装在事务中时,根本不应该有竞争

【讨论】:

以上是关于防止更新和插入之间的异步读取的主要内容,如果未能解决你的问题,请参考以下文章

防止在 postgres 上的异步插入重复

使用Effect清理功能,防止未挂载的组件异步更新

如何防止 ReSharper 在我的 MSpec 字段之间插入空行?

防止我们的 Rails 应用程序中的重复数据库插入/更新同时发生事务

从 JSONArray 插入/更新 +10000 行到 SqLite 时如何防止应用程序崩溃

使用快照隔离防止 SQL 视图被冗长的删除/插入事务阻塞