0

我有一个情况,我有多个(可能数百个)线程重复相同的任务(如果你好奇的话,使用 java 调度执行器)。此任务需要选择尚未处理的更改行(从名为 change 的表中)(已处理的更改在 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

请让我知道这是否可行

4

1 回答 1

0

您必须以某种方式“锁定”您要处理的行。这种“锁定”当然应该与最小的冲突/错误同时发生。
一种方法如下:

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
);

每个这样的语句都会在表中插入 1 或 0 行change_process_rel,这里用作“锁定”表。返回对应ID的from change,您可以在同一个事务中继续处理对应的事件。
如果事务成功完成,则保存插入到change_process_rel表中的行,因此可以认为对应id的 from已处理。change如果事务失败,则相应的“锁定”行从change_process_rel消失,并且此行可能稍后由该应用程序或另一个应用程序处理。
这种方法的问题是,当两个表都变得足够大时,这样的子选择可能不会像以前那样快速工作。

另一种方法是使用Evaluate uncommitted data through lock deferral

它需要将status列放入change表中。不幸的是,Db2 for LUW 没有SKIP LOCKED功能,这可能有助于这种算法。
如果,比方说,status=0是“未处理”,并且status<>0是一些处理/处理状态,那么在设置这些DB2_EVALUNCOMMITTEDDB2_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一旦你得到它,你就可以像以前一样在同一个事务中做进一步的处理。
为性能创建索引很好:create index change1 on change(status); 并且可以将此表设置为 volatile 或收集此列的分布统计信息,以及定期对表及其索引进行定期统计。
请注意,这样的注册表变量设置具有全局效果,您应该牢记...

于 2020-02-12T19:33:46.447 回答