Установка метки времени внутри транзакции - PullRequest
1 голос
/ 10 февраля 2012

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

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

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

Сессия 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. Но, возможно, у кого-то есть идея получше ...

Ответы [ 4 ]

1 голос
/ 10 февраля 2012

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

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

1 голос
/ 10 февраля 2012

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

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

1 голос
/ 10 февраля 2012

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

0 голосов
/ 10 февраля 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...