在数据库表中同时读取和更新

L-F*_*our 5 c# oracle entity-framework transactions devart

我有一个使用Devart和Entity Framework访问的Oracle数据库.

有一个叫做IMPORTJOBS列的表STATUS.

我还有多个进程同时运行.他们每个人都读取IMPORTJOBS有状态的第一行'REGISTERED',将其置于状态'EXECUTING',如果完成则将其置于状态'EXECUTED'.

现在因为这些进程并行运行,我相信可能会发生以下情况:

  • 进程A读取具有状态的第10行REGISTERED,
  • 进程B还读取仍具有状态的第10行REGISTERED,
  • 进程A将第10行更新为状态EXECUTING.

进程B不应该读取第10行,因为进程A已经读取了它并且将更新其状态.

我该怎么解决这个问题?在事务中放入读取和更新?或者我应该使用一些版本控制方法或其他什么?

谢谢!

编辑:感谢接受的答案我得到了它的工作并在此处记录:http://ludwigstuyck.wordpress.com/2013/02/28/concurrent-reading-and-writing-in-an-oracle-database.

Vin*_*rat 3

您应该使用数据库的内置锁定机制。不要重新发明轮子,特别是因为 RDBMS旨在处理并发性和一致性。

在Oracle 11g中,我建议您使用该SKIP LOCKED功能。例如,每个进程可以调用这样的函数(假设id是数字):

CREATE OR REPLACE TYPE tab_number IS TABLE OF NUMBER;

CREATE OR REPLACE FUNCTION reserve_jobs RETURN tab_number IS
   CURSOR c IS 
      SELECT id FROM IMPORTJOBS WHERE STATUS = 'REGISTERED'
      FOR UPDATE SKIP LOCKED;
   l_result tab_number := tab_number();
   l_id number;
BEGIN
   OPEN c;
   FOR i IN 1..10 LOOP
      FETCH c INTO l_id;
      EXIT WHEN c%NOTFOUND;
      l_result.extend;
      l_result(l_result.size) := l_id;
   END LOOP;
   CLOSE c;
   RETURN l_result;
END;
Run Code Online (Sandbox Code Playgroud)

这将返回 10 行(如果可能)未锁定的行。这些行将被锁定,并且会话不会互相阻塞。

在 10g 及之前版本中,由于 Oracle 返回一致的结果,请FOR UPDATE明智地使用,您不应该遇到您所描述的问题。例如考虑以下内容SELECT

SELECT *
  FROM IMPORTJOBS 
 WHERE STATUS = 'REGISTERED'
   AND rownum <= 10
FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)

如果所有进程都使用此 SELECT 保留其行,会发生什么情况?这将如何影响您的场景:

  1. 会话 A 获取 10 行未处理的数据。
  2. 会话 B 将获得相同的 10 行,被阻止并等待会话 A。
  3. 会话 A 更新所选行的状态并提交其事务。
  4. Oracle 现在将(自动)从头开始重新运行会话 B 的 select,因为数据已被修改并且我们已指定FOR UPDATE(此子句强制 Oracle 获取该块的最后版本)。
    这意味着会话 B 将获得 10 个新行

所以在这种情况下,你不存在一致性问题。此外,假设请求行并更改其状态的事务速度很快,则并发影响将很小。