简体   繁体   English

对数据库(Oracle 或 ANSI SQL)中具有选定 id 的行进行比较和交换操作?

[英]Compare-and-swap operation for row with selected id in database (Oracle or ANSI SQL)?

I want to implement failsafe persistent synchronization in application based on database (Oracle in my case, but like to see ANSI SQL solution).我想在基于数据库的应用程序中实现故障安全持久同步(在我的例子中是 Oracle,但喜欢看 ANSI SQL 解决方案)。

I work with tasks which can be run in different threads, applications or servers.我处理可以在不同线程、应用程序或服务器中运行的任务。

Each type of task (I differ them by ID ) may not run in concurrent - that is why I need synchronization.每种类型的任务(我通过ID区分它们)可能不会并发运行 - 这就是我需要同步的原因。 All have access to DB so it is good place for synchronization!所有人都可以访问数据库,因此它是同步的好地方!

Each thread/application/server can fail, so I need a way to remove acquired lock from ID after timeout.每个线程/应用程序/服务器都可能失败,所以我需要一种方法来在超时后从ID删除获取的锁。

The first that come to mind is to use table with:首先想到的是使用 table :

ID
STATE
TS

fields.领域。 All I need is atomic operations which:我所需要的只是原子操作,它:

  • try to change STATE value from completed to executing (to synchronize) and set TS to current time.尝试将STATE值从完成更改为执行(同步)并将TS设置为当前时间。 Return false if STATE is not completed .如果STATE完成,则返回 false。
  • try to change STATE value from executing / recovering to recovering if sysdate - TS > delay and set TS to current time (to be failsafe).如果sysdate - TS > delay并将TS设置为当前时间(以确保故障安全),请尝试将STATE值从执行/恢复更改为恢复 Else return false.否则返回假。

SQL update statement mostly make that I want: SQL更新语句主要是我想要的:

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

and:和:

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

Only one issue that I see - how to get know (from Java application through JDBC) if update actually performed or not (in order to be true compare-and-swap operation )?我只看到一个问题 - 如何知道(从 Java 应用程序通过 JDBC)更新是否实际执行(为了成为真正的比较和交换操作)? May be by getting updated row number (is this info available through JDBC)?可能是通过获取更新的行号(此信息是否可通过 JDBC 获得)?

Is I am correct with my assumption that update is atomic for where condition?我假设更新对于where条件是原子的这是否正确?

Is there another way to implement failsafe persistent synchronization in application based on database?有没有另一种方法可以在基于数据库的应用程序中实现故障安全持久同步?

PS My question is differ from: PS我的问题不同于:

This easiest way might be to create and run a stored procedure which could then return what the result of the change was.这种最简单的方法可能是创建并运行一个存储过程,然后该过程可以返回更改的结果。

Edit (expanded answer): Sorry I thought you were looking for a way to get the answer back.编辑(扩展答案):抱歉,我以为您正在寻找一种方法来获得答案。 I would do something like the following (although I haven't got an oracle instance at home to test it on, sorry)我会做类似以下的事情(虽然我家里没有一个 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;

This function locks the row for the length of its execution so no other process can edit it, this means only one process can run this at a time.该函数在其执行期间锁定该行,因此其他进程无法对其进行编辑,这意味着一次只有一个进程可以运行该行。 Since this procedure will run in the database it will finish so you don't have to worry if the java process dies.由于此过程将在数据库中运行,它将完成,因此您不必担心 java 进程是否终止。 I guess the only downside is that this is blocking but only for the run of this function which is short.我想唯一的缺点是这是阻塞的,但仅适用于这个较短的函数的运行。 If you really can't have that then you could try making the select for update not wait;如果你真的不能,那么你可以尝试让选择更新不等待; off the top of my head I can not remember how to do this.在我的头顶上,我不记得如何做到这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM