Инкапсуляция транзакций многопроцессных записей - PullRequest
2 голосов
/ 30 сентября 2008

У меня есть сценарий базы данных (я использую Oracle), в котором несколько процессов делают вставки в таблицу, и один процесс выбирает из нее. Таблица в основном используется в качестве промежуточного хранилища, в которое несколько процессов (в дальнейшем называемых Writers) записывают события журнала, и из которых один процесс (в дальнейшем называемый Reader) считывает события для дальнейшей обработки. Читатель должен прочитать все события, вставленные в таблицу.

В настоящее время это выполняется каждой вставленной записью, которой присваивается идентификатор из восходящей последовательности. Читатель периодически выбирает блок записей из таблицы, где идентификатор больше, чем самый большой идентификатор ранее прочитанного блока. Например. что-то вроде:

SELECT
  *
FROM
  TRANSACTION_LOG
WHERE
  id > (
    SELECT
      last_id
    FROM
      READER_STATUS
   );

Проблема с этим подходом состоит в том, что, поскольку писатели работают одновременно, строки не всегда вставляются в порядке в соответствии с назначенным им идентификатором, даже если они назначаются в порядке возрастания. То есть строка с id = 100 иногда записывается после записи с id = 110, потому что процесс записи строки с id = 110 начинался после процессов, записывающих запись id = 100, но выполнялся первым. Это может привести к тому, что Reader пропустит строку с id = 100, если он уже прочитал строку с id = 110.

Принудительная запись Авторов на эксклюзивную блокировку на столе решит проблему, поскольку это заставит их последовательно вставлять данные, а также Читатель будет ожидать любых ожидающих фиксаций. Это, однако, вероятно, будет не очень быстро.

Я думаю, что Читателю будет достаточно подождать, пока какой-нибудь выдающийся коммитер Writer перед прочтением. То есть, Writers могут продолжать работать одновременно, пока Reader читает, пока все авторы не закончили.

Мой вопрос такой:
Как я могу дать указание своему процессу чтения ждать каких-либо выдающихся коммитов моих процессов записи? Любые альтернативные предложения по вышеуказанной проблеме также приветствуются.

Ответы [ 5 ]

1 голос
/ 30 сентября 2008

Возможно, вы захотите установить эксклюзивную блокировку на столе в процессе чтения. Это подождет, пока все записывающие устройства завершат и снимают свои блокировки строк, поэтому вы можете быть уверены, что нет незавершенных транзакций записывающего устройства.

1 голос
/ 30 сентября 2008

Я бы не делал никаких блокировок, которые могут помешать параллелизму и пропускной способности.

Вам также не понадобится таблица Reader_Status, если вы отслеживаете, какие строки журнала вы обрабатывали построчно.

Вот что я сделаю: добавлю новый столбец в вашу таблицу журналов. Назовите это "обработанным", например. Сделайте его логическим, по умолчанию false (или маленьким целым числом, по умолчанию 0 или любым другим). Writers используют значение по умолчанию при вставке.

Когда Читатель запрашивает следующий блок записей для обработки, он запрашивает строки, где обработано значение false, а значение id низкое.

SELECT * FROM Transaction_Log
WHERE processed = 0
ORDER BY id
LIMIT 10;

По мере того, как он их обрабатывает, Считыватель использует UPDATE для изменения обработанного с ложного на истинное. Поэтому в следующий раз, когда Читатель запросит блок записей, он уверен, что не получит строки, которые он уже обработал.

UPDATE Transaction_Log
SET processed = 1
WHERE id = ?;  -- do this for each row processed

Это ОБНОВЛЕНИЕ не должно конфликтовать с операциями ВСТАВКИ, выполняемыми Авторами.

Если какие-либо строки зафиксированы другими Писателями вне последовательности, Читатель увидит их в следующий раз, когда запросит, если он всегда обрабатывает их в порядке столбца id от минимального значения к наибольшему.

1 голос
/ 30 сентября 2008

Интересная проблема. Похоже, вы создаете хорошее решение.
Я надеюсь, что смогу помочь.

Пара предложений ...

Статус писателя

Вы можете создать таблицу WRITER_STATUS с полем last_id: каждый автор обновляет эту таблицу перед записью с идентификатором, который собирается записать в журнал, но только если его идентификатор больше текущего значения last_id.

Читатель также проверяет эту таблицу и теперь знает, писатели еще не написали.

Reader Log

Это может быть более эффективным.
После того, как читатель выполнит чтение, он проверяет наличие дыр в извлеченных записях.
Затем он записывает все недостающие идентификаторы в таблицу MISSING_IDS и для следующего чтения делает что-то вроде

SELECT *
FROM   TRANSACTION_LOG
WHERE  id > (SELECT last_id
             FROM   READER_STATUS)
OR     id IN ( SELECT id from MISSING_IDS ) 
0 голосов
/ 01 октября 2008

Я согласен с решением AJ ( ссылка ). Кроме того, следующие рекомендации могут помочь уменьшить количество отверстий.

1) Используйте Oracle Sequence для создания идентификатора и используйте auto-increment, как показано ниже

INSERT INTO transaction_table VALUES(id__seq.nextval, <other columns>);

2) Используйте autoCommit(true), чтобы вставка немедленно зафиксировалась.

Эти два шага существенно уменьшат количество отверстий. Тем не менее, существует вероятность того, что некоторые вставки начались сначала, но были зафиксированы позже, и между ними произошла операция чтения.

0 голосов
/ 30 сентября 2008

Поскольку вы знаете, last_id обработано Reader, вы можете запросить следующий рабочий элемент следующим образом:

select * from Transaction_log where id = (
  select last_id + 1 /* or whatever increment your sequencer has */
    from Reader_status)
...