我有多个(可能是数百个)线程重复同一任务的情况(如果您很好奇,请使用Java调度的执行程序)。此任务需要选择尚未处理的更改行(从称为更改的表中)(已处理的更改在am:n联接表中被跟踪,该表称为process_change_rel,该表跟踪进程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行,此处将其用作“锁定”表。返回来自ID
的相应change
,并且您可以在同一事务中继续处理相应的事件。如果事务成功完成,则将保存插入change_process_rel
表的行,因此,可以认为id
中的相应change
已被处理。如果事务失败,则change_process_rel
中对应的“锁定”行将消失,并且此行或其他应用程序以后可能会处理该行。这种方法的问题是,当两个表都变得足够大时,这种子选择可能无法像以前那样快地工作。
[需要将status
列放入change
表中。不幸的是,用于LUW的Db2不具有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
进行进一步处理。创建性能索引是很好的:在change(status)上创建索引change1;并且除了可以定期对该表及其索引进行定期统计外,还可以将该表设置为volatile或在此列上收集分发统计信息。请注意,这样的注册表变量设置具有全局作用,因此请牢记...