如果我的数据如下:
ID STATUS DATE_ADDED
== ========== ==========
1 Processing 2011-04-01
2 New 2011-04-02
3 New 2011-04-03
4 Processing 2011-04-03
5 Done 2011-04-06
6 New 2011-04-06
7 New 2011-04-14
8 Done 2011-04-14
...
...建议选择状态为“新建”的10条最旧记录,并将其状态设置为“正在处理”,同时确保任何其他并发进程不能对相同记录执行相同操作?
这是一个在Windows Server 2003下运行在PHP / 5.2.6上的Web应用程序,它通过ODBC(Oracle的驱动程序,而不是Microsoft的)连接到远程Oracle 10g服务器。
答案 0 :(得分:3)
这在Oracle 10g中很难做到。在11g中,SELECT FOR UPDATE ... SKIP LOCKED
语法使其变得简单。
一个简单的UPDATE
语句将序列化。和SELECT FOR UPDATE
一样。当然,两个竞争过程不会得到相同的行;问题是他们最多会序列化,最糟糕的是,他们可以陷入僵局。
推荐的方法是使用Oracle Advanced Queuing(或您选择的排队实现)将要排队的ID排入队列,并允许排队实现来管理值的争用。
-
SQL将起作用,但是如果第二个用户针对相同的偏移量运行它而某人将该范围锁定,则会因ORA-00054而失败。这可以通过将select包装在循环中,捕获ORA-00054错误并使用它来增加偏移量来缓解。
select * from my_table
where rowid in
(select row_id
from (select rowid as row_id, rownum as rn
from mytable where some_condition
order by deterministic_sort_order)
where rn between :low_rn and :hi_rn
)
for update nowait;
排序表达式需要是确定性的(简单地说,包括主键作为排序表达式的结尾)以防止冲突。
答案 1 :(得分:2)
使用交易来做到这一点。使用隔离级别" serializable"对于事务,将阻止任何其他进程在您的事务处理它们时访问/修改行。
如果可序列化事务尝试执行修改已由未提交事务修改的任何表的SQL数据操作语句,则该语句将失败。
您可能想要使用:
set transaction isolation level serializable;
答案 2 :(得分:1)
您可以创建一个新表并在其中放入一行。然后,您的程序可以使用更新锁定行,或者在执行原始表之前选择更新。如果所有程序使用相同的过程来标记表“处理”,这将起作用。
create table Lock_Table (
app_catagory varchar2(20) primary key,
usage_ts timestamp(6)
);
插入一行:insert into Lock_Table (app_category) values 'APP1'
并提交。这是一次插入。
然后锁定其他会话:update Lock_Table set usage_ts = current_timestamp where app_category = 'CAT1'
您不需要usage_ts列,可以使用select for update
。
只要您在“选择最早的10个”查询之前执行上述更新,您就可以保证您的结果。我建议将所有内容放在一个程序中(或程序包中的一个程序),以便应用程序员轻松“做正确的事情。”
答案 3 :(得分:1)
解决这个问题的一个重要方法是锁定表,以便其他会话无法更新它:
lock your_table in exclusive mode
不幸的是,在释放锁之前,其他会话将无法插入新行,因此这可以真正降低应用程序的一致性。
答案 4 :(得分:-1)
仅供注意
此外,乐观锁定策略可用于解决此问题