我遇到的情况是,我有多个(可能是数百个(线程重复相同的任务(如果您好奇,请使用java调度执行器(。此任务需要选择尚未处理的更改行(从名为 change 的表中((处理的更改在名为 process_change_rel 的 m:n join 表中跟踪,该表跟踪进程 ID、记录 ID 和状态(,然后更新回状态。
我的问题是,如何防止来自同一进程的两个线程选择同一行的最佳方法?以下解决方案(用于更新以锁定行(是否有效? 如果没有,请提出可行的解决方案
Create table change(
—id , autogenerated pk
—other fields
)
Create table change_process_rel(
—change id (pk of change table)
—process id (pk of process table)
—status)
下面列出了我将使用的查询
Select * from
change c
where c.id not in(select changeid from change_process_rel with cs) for update
请让我知道这是否有效
您必须"锁定"要以某种方式处理的行。当然,这种"锁定"应该同时进行,并尽量减少冲突/错误.
一种方法如下:
Create table change
(
id int not null generated always as identity
, v varchar(10)
) in userspace1;
insert into change (v) values '1', '2', '3';
Create table change_process_rel
(
id int not null
, pid int not null
, status int not null
) in userspace1;
create unique index change_process_rel1 on change_process_rel(id);
现在,您应该能够从多个并发会话运行相同的语句:
SELECT ID
FROM NEW TABLE
(
insert into change_process_rel (id, pid, status)
select c.id, mon_get_application_handle(), 1
from change c
where not exists (select 1 from change_process_rel r where r.id = c.id)
fetch first 1 row only
with ur
);
每个这样的语句都会在change_process_rel
表中插入 1 或 0 行,此处用作"锁定"表。返回来自change
的相应ID
,您可以继续处理同一事务中的相应事件.
如果事务成功完成,则插入change_process_rel
表中的行将被保存,因此,来自change
的相应id
可能被视为已处理。如果事务失败,change_process_rel
中相应的"锁定"行将消失,此行稍后可能会由此应用程序或其他应用程序处理。
此方法的问题是,当两个表都变得足够大时,这样的子选择可能不会像以前那样快速工作。
另一种方法是使用通过锁定延迟评估未提交的数据。
它需要将status
列放入change
表中。 不幸的是,Db2 for LUW 没有SKIP LOCKED
功能,这可能有助于这种算法.
假设status=0
"未处理",并且status<>0
是某些正在处理/已处理的状态,那么在设置这些DB2_EVALUNCOMMITTED
和DB2_SKIP*
注册表变量并重新启动实例后,您可以使用以下语句"捕获"下一个ID
进行处理。
SELECT ID
FROM NEW TABLE
(
update
(
select id, status
from change
where status=0
fetch first 1 row only
)
set status=1
);
获得它后,您可以在与之前相同的事务中对此ID
进行进一步处理.
最好创建一个性能索引: 在更改(状态(时创建索引更改1; 并且可以将此表设置为易失性,或者除了定期收集表及其索引的常规统计信息外,还收集此列的分布统计信息。
请注意,此类注册表变量设置具有全局影响,您应该记住它...