我有多个线程正在处理来自同一个表的行,实际上是一个队列。
我希望每行只由一个线程处理。所以,我添加了一个列 - “IsInProccess”,并在线程SELECT语句中添加了“WHERE IsInProccess = 0”。 另外我使用“SELECT FOR UPDATE”,所以在一个线程从表中获取一行后,没有其他线程会在它将“1”放入“IsInProccess”之前得到它。
问题是我有很多线程,并且在很多时候会发生以下情况: 线程 A 通过“SELECT FOR UPDATE”选择表格并获取行号。的 1 即可。在将IsInProccess更改为1之前,线程 B 以相同的方式从表中选择并获得行 1 。 Oracle保存行号。 1 以线程 A 会话且线程 B 无法更改此行并返回错误 - “无法获取”。
我希望当某个线程从表中选择时,Oracle将返回未保存到其他打开会话的行。
我能这样做吗?
答案 0 :(得分:2)
这是我之前看到过非常成功的解决方案草图:
使用SELECT FOR UPDATE NOWAIT语法,以便如果会话无法立即锁定某行,则会引发异常,而不是等待锁定。异常处理程序可能会等待几秒钟(例如,使用dbms_lock.sleep);整个区块可以被包裹在一个循环中,在它放弃之前再次尝试一定次数。
将WHERE ROWNUM<=n
添加到查询中,以便它一次只尝试获取一定数量的行(例如1);如果成功,则将行更新为“正在处理中”。
将行标记为“正在处理”(我已经看到成功使用)的好方法是有两列 - SID和SERIAL# - 并使用SID和SERIAL#更新这些列会话。
如果会话在标记为“正在处理”的行中失败,则另一个进程可以通过搜索具有SID / SERIAL的任何行来“清理”标记为“正在处理”的行#在v$session
中未找到的活动会话。
答案 1 :(得分:2)
Oracle已经为您解决了这个问题:使用Advanced Queuing API
答案 2 :(得分:1)
如果您有11克,请查看SKIP LOCKED 它在那里,但没有记录(因此没有支持,也许是马车)在10g。 这样,当会话A锁定行时,会话B可以跳过它并处理下一行。
答案 3 :(得分:0)
一个解决方案:
将选择和更新查询放入事务中。这类问题正是交易发明的原因。
您还需要担心“孤儿”行 - 例如一个线程拾取行然后死亡而不完成工作。要解决这个问题,一种解决方案是拥有2列:“IsInProcess”和“StartprocessingTime”。
isInProcess将有3个值:0(未处理),1(拾取),2(完成)。
原始事务会将行的“isInProcess”设置为1,将“StartprocessingTime”设置为其他内容,但select也会将此项添加到where子句(假设您可以指定有效的超时时间)
“WHERE isInProcess = 0 OR(isInProcess = 1 AND StartprocessingTime&lt; getdate() - timeout)”。
请注意,上面的语法不是ORACLE,只是伪代码。