Инкапсуляция транзакций многопроцессорной записи

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

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

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 перед чтением. То есть писатели могут продолжать работать одновременно до тех пор, пока читатель не читает, пока все писатели не закончат работу.

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

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
0
512
5

Ответы 5

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

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

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

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

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

Журнал читателя

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

SELECT *
FROM   TRANSACTION_LOG
WHERE  id > (SELECT last_id
             FROM   READER_STATUS)
OR     id IN ( SELECT id from MISSING_IDS ) 

Спасибо. Конечно, стоит задуматься. Я буду пробовать разные разбавители в ближайшие пару дней.

aggergren 30.09.2008 14:15

Здорово. пожалуйста, обновите свой вопрос, чтобы сообщить нам, как вы поживаете (или, что еще лучше, опубликуйте ответ). Это одна из тех проблем, которые иногда возникают, и хорошее решение - золотой песок! Удачи.

AJ. 30.09.2008 16:34

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

да, я согласен, что это заставит Reader подождать. Вопрос в том, какой удар по производительности это вызовет.

aggergren 30.09.2008 14:15

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

csgero 30.09.2008 15:28

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

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

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

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

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

В процессе их обработки Читатель использует ОБНОВЛЕНИЕ, чтобы изменить обработанное значение с ложного на истинное. Поэтому в следующий раз, когда Читатель запросит блок записей, он уверен, что не получит уже обработанные строки.

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

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

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

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

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

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

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

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

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

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

Другие вопросы по теме