У меня есть сценарий базы данных (я использую 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 перед чтением. То есть писатели могут продолжать работать одновременно до тех пор, пока читатель не читает, пока все писатели не закончат работу.
У меня такой вопрос:
Как я могу проинструктировать мой процесс чтения ждать любых невыполненных коммитов моих процессов записи? Также приветствуются любые альтернативные предложения по вышеупомянутой проблеме.


Интересная проблема. Похоже, вы создаете хорошее решение. Я надеюсь, что смогу помочь.
Пара предложений ...
Вы можете создать таблицу 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 )
Здорово. пожалуйста, обновите свой вопрос, чтобы сообщить нам, как вы поживаете (или, что еще лучше, опубликуйте ответ). Это одна из тех проблем, которые иногда возникают, и хорошее решение - золотой песок! Удачи.
Возможно, вы захотите установить исключительную блокировку таблицы в процессе чтения. Это будет ждать, пока все писатели закончат работу и освободят свои блокировки строк, поэтому вы можете быть уверены, что нет незавершенных транзакций записи.
да, я согласен, что это заставит Reader подождать. Вопрос в том, какой удар по производительности это вызовет.
Зависит от того, как часто и как долго выполняется процесс чтения. Это решение, по крайней мере, позволит нескольким модулям записи работать параллельно, поэтому, если запись происходит значительно чаще, снижение производительности должно быть терпимым.
Я бы не стал делать никаких блокировок, которые могут помешать параллелизму и пропускной способности.
Вам также не нужна таблица 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), чтобы вставить немедленную фиксацию.
Эти два шага существенно уменьшат количество отверстий. Тем не менее существует вероятность того, что некоторые вставки были запущены первыми, но были зафиксированы позже, и между ними произошла операция чтения.
Спасибо. Конечно, стоит задуматься. Я буду пробовать разные разбавители в ближайшие пару дней.