2012-02-10 3 views
1

Мы имеем следующие два неоднократно и независимо друг от друга выполнение задач в нашей базе данных (PostgreSQL):Установка временной метки внутри транзакции

Сессия 1 делает некоторые обновления в транзакции и установить метку обновленных наборов данных:

BEGIN; 
... 
UPDATE table SET ..., timestamp = current_timestamp WHERE ...; 
... // (A) 
COMMIT; 

Session 2 выбирает все наборы данных, которые были обновлены с момента его последнего запуска:

SELECT * FROM table WHERE timestamp BETWEEN last_run AND current_timestamp; 
last_run = current_timestamp; 
... 

Если сеанс 2 начинается во время сеанса 1 находится в точке (A) он не будет видеть тя потому что временная метка не будет установлена ​​до фиксации, но до более раннего значения. Кроме того, никакая последующая сессия 2 не выберет изменения, поскольку last_run уже будет больше, чем временная метка. Таким образом, проблема заключается в том, что сеанс 1 устанавливает временную метку в неправильное значение соответственно в неподходящее время и, следовательно, изменения могут быть «забыты».

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

+0

Вам нужен механизм, позволяющий одновременно запускать? Если это так, требуется точный код и бизнес-логика.Чаще всего вы рассматриваете каждый блок транзакций как фиксированный блок, когда один процесс находится внутри блока, ни один другой процесс не может запустить блок. Это прямо избегает «но что, если кто-то начал, но не закончил» проблему. Это достигается за счет использования блокировок таблиц/строк. Знаете ли вы, что вы предпочитаете? – MatBailie

+0

Должно быть возможно запустить их одновременно. Код довольно большой, поэтому я не могу опубликовать его, но задача 1 в основном импортирует данные, а задача 2 экспортирует изменения. Экспортер должен запускаться каждые 30 минут и не может дождаться возможного импорта. Импортер работает чаще, но на короткое время и не может дождаться, когда экспортер будет работать около 20 минут. – jpstrube

ответ

1

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

После отметки процесса импорта обновленные строки могут быть выполнены довольно легко или даже реализованы с использованием триггеров в вашей таблице данных. Если у вас только один потребительский процесс, все, что нужно сделать, это delete from updated_item returning item_id, чтобы получить список обновлений. Похоже, это намного сложнее, но ИМХО это не так. Такие функции, как возможность отслеживать, насколько большой объем отставания также появляется бесплатно.

1

Простой решение состоит в том, чтобы избежать выбора строк, которые могут быть в конфликте. Выберите строки между last_run и current_timestamp - интервал '1' минута. Точно, сколько времени вы считаете необходимым буферизировать, вам нужно будет решить, исходя из объема транзакций, и сколько времени потребуется для завершения транзакции обновления. Просто убедитесь, что вы также установили last_run = current_timestamp - interval '1' minute, и у вас не должно быть проблем с потерянными строками, которые не были зафиксированы непосредственно перед началом SELECT.

+0

Мы думали о чем-то подобном. Но найти лучший интервал сложно, потому что у нас есть очень короткие и очень длинные обновления. – jpstrube

1

IMHO session2 должен select where zetimestamp > lastrun, и установите last_run на номер MAX(timestamp of processed_items). Сеансы, которые выполнялись, но имели незафиксированные данные во время сеанса session2, имеют временные метки до session2 и будут скрыты для последующих сеансов session2, если вы установите last_run на current_timestamp.

Кроме того: в большинстве случаев использование current_timestamp нежелательно. Натуральные временные метки не могут иметь значение больше, чем current_timestamp, поэтому каждая существующая временная метка будет < = current_timestamp, и по сравнению с ней бесполезно.

0

current_timestamp возвращает время начала текущей транзакции, а не текущее время. Проверьте clock_timestamp(), который изменится в текущей транзакции.

+0

Правда. Но это все равно было бы раньше, чем фиксация. – jpstrube

+0

Вот почему мое решение действительно работает. MAX (что-то в прошлом) всегда перед current_timestamp = time_used_at_commit. – wildplasser

Смежные вопросы