如何解决原子SELECT和UPDATE
| 如果我的数据如下所示: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服务器。
解决方法
在Oracle 10g中很难做到这一点。在11g中,使用
SELECT FOR UPDATE ... SKIP LOCKED
语法很容易。
一个简单的“ 2”语句将序列化。 will3ѭ也一样。当然,两个相互竞争的进程永远不会获得相同的行;问题在于它们充其量只能序列化,最坏的情况是它们可能会死锁。
推荐的方法是使用Oracle Advanced Queuing(或您选择的排队实现)将要处理的ID排队,并允许排队实现管理值的争用。
-
SQL可以运行,但是如果有人锁定该范围时第二个用户以相同的偏移量运行它,则该SQL将失败并显示ORA-00054。通过将选择包装在一个循环中,捕获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;
排序表达式需要确定性(简单地,将主键作为排序表达式的结尾)以防止冲突。
,使用事务来做到这一点。对事务使用隔离级别“可序列化”将防止在事务处理行时任何其他进程访问/修改行。
如果可序列化事务试图执行SQL数据操作语句,该语句修改了未提交事务已修改的任何表,则该语句将失败。
您可能要使用:
set transaction isolation level serializable;
,解决此问题的一种繁重方法是锁定表,以便其他会话都无法更新它:
lock your_table in exclusive mode
不幸的是,在释放锁之前,其他会话将无法插入新行,因此这确实可以减少应用程序的并发性。
,您可以创建一个新表并将其放在其中。然后,您的程序可以使用更新锁定行,或者选择要更新的行,然后再处理原始表。如果所有程序都使用相同的过程将表标记为“处理中”,则此方法将起作用。
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个最旧的”查询之前进行上述更新,就可以保证结果。我建议将所有内容放在一个过程(或一个包中的一个过程)中,以使应用程序程序员可以轻松地“做正确的事”。
,只是为了注意
同样,乐观锁定策略也可以解决这个问题
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。