2

我想在基于数据库的应用程序中实现故障安全持久同步(在我的例子中是 Oracle,但希望看到 ANSI SQL 解决方案)。

我处理可以在不同线程、应用程序或服务器中运行的任务。

每种类型的任务(我区分它们ID)可能不会同时运行——这就是我需要同步的原因。所有人都可以访问数据库,因此这是同步的好地方!

ID每个线程/应用程序/服务器都可能失败,所以我需要一种方法来在超时后删除获取的锁。

首先想到的是使用表:

ID
STATE
TS

字段。我需要的只是原子操作:

  • 尝试将STATE值从已完成更改为正在执行(同步)并设置TS为当前时间。STATE如果未完成则返回 false 。
  • 尝试将STATE值从执行/恢复更改为恢复ifsysdate - TS > delay并设置TS为当前时间(以防故障)。否则返回假。

SQL更新语句主要是我想要的:

update TASK set STATE = 'executing', TS = sysdate
  where ID = :id and STATE = 'completed'

和:

update TASK set STATE = 'recovering', TS = sysdate
  where ID = :id and STATE in ('executing', 'recovering')
    and sysdate - TS > :delay

我只看到一个问题 - 如何知道(通过 JDBC 从 Java 应用程序)是否实际执行了更新(为了成为真正的比较和交换操作)?可能是通过获取更新的行号(此信息是否可通过 JDBC 获得)?

我的假设是否正确,即更新对于where条件是原子的?

是否有另一种方法可以在基于数据库的应用程序中实现故障安全持久同步?

PS我的问题不同于:

4

1 回答 1

2

这种最简单的方法可能是创建并运行一个存储过程,然后可以返回更改的结果。

编辑(扩展答案):对不起,我以为您正在寻找一种方法来获得答案。我会做类似以下的事情(虽然我家里没有一个 oracle 实例来测试它,抱歉)

function GetLockForRun(p_id task.id%TYPE)
  return VARCHAR2
declare
 l_result VARCHAR2;
begin
 select STATE, TS
   into l_state, l_ts
   from Task
   where ID = p_id;
     for update; --this locks the row until you commit 

 if (state == 'completed' or sysdate - TS > delay)
   update TASK 
      set STATE = 'executing', TS = sysdate
    where ID = p_id;
   l_result := "OK";
 else 
   l_result := "Do not run";
 end if
 commit; --release for update lock
 return l_result;
end;

此函数在行执行期间锁定行,因此其他进程无法编辑它,这意味着一次只能运行一个进程。由于此过程将在数据库中运行,因此它将完成,因此您不必担心 java 进程是否死亡。我想唯一的缺点是这是阻塞的,但仅适用于这个简短的函数的运行。如果你真的做不到,那么你可以尝试选择更新而不是等待;我不记得该怎么做。

于 2013-08-22T21:27:28.300 回答