Запретить двум потокам выбирать одну и ту же строку - PullRequest
0 голосов
/ 12 февраля 2020

У меня есть ситуация, когда несколько (потенциально сотни) потоков повторяют одну и ту же задачу (используя запланированного исполнителя java, если вам интересно). Эта задача влечет за собой выбор строк изменений (из таблицы, называемой изменением), которые еще не были обработаны (обработанные изменения отслеживаются в am: n присоединяется к таблице с именем process_change_rel, которая отслеживает идентификатор процесса, идентификатор записи и состояние), обрабатывая их, затем обновление обратно статус.

Мой вопрос: как наилучшим способом предотвратить выбор одной строки двумя потоками из одного процесса? Будет ли работать следующее решение (используется для обновления для блокировки строк)? Если нет, пожалуйста, предложите рабочее решение

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

Пожалуйста, дайте мне знать, если это будет работать

1 Ответ

0 голосов
/ 12 февраля 2020

Вы должны «заблокировать» строку, которую собираетесь обработать. Такая «блокировка» должна выполняться одновременно с минимальным количеством конфликтов / ошибок.
Один из способов заключается в следующем:

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 из change, и вы можете продолжить обработку соответствующего события в той же транзакции.
Если транзакция завершается успешно, строка, вставленная в таблицу change_process_rel, сохраняется, поэтому соответствующий id из change может рассматриваться как обработанный. Если транзакция завершается неудачно, соответствующая строка «lock» из change_process_rel исчезает, и эта строка может быть позже обработана этим или другим приложением.
Проблема этого метода в том, что, когда обе таблицы становятся достаточно большими, такой суб-выбор может работать не так быстро, как раньше.

Другой метод заключается в использовании Оценка непереданных данных через отсрочку блокировки .

Требуется разместить столбец status в таблицу change. К сожалению, Db2 для 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 в той же транзакции, что и ранее.
Хорошо создать индекс производительности: создать индекс change1 on change (status); и может установить эту таблицу как изменчивую или собирать статистику распределения по этому столбцу в дополнение к регулярной статистике по таблице и ее индексам периодически.
Обратите внимание, что такая настройка переменных реестра имеет глобальный эффект, и вам следует помнить об этом .. .

...