Получение даты последнего изменения таблицы базы данных PostgreSQL

35

Я пытаюсь узнать, когда моя таблица была изменена, проверив дату изменения файла, как описано в этом ответе . Но результат не всегда правильный. Дата изменения файла обновляется через несколько минут после обновления таблицы. Это правильное поведение? Сохраняет ли PostgreSQL модификации таблиц в некотором кеше, а затем записывает их на жесткий диск?

Итак, как мне получить правильную дату последней модификации таблицы (давайте предположим, что автоматические вакуумные модификации тоже подходят)?

Я использую PostgreSQL 9.2 под Linux Centos 6.2 x64.

моток
источник
4
Я не думаю, что время изменения файла является надежным. Это также может измениться из-за автовакуума. Единственный надежный способ - сохранить временную метку модификации в вашей таблице, поддерживаемую триггером.
a_horse_with_no_name
Одна из идей заключается в том, что информация, хранящаяся в файлах WAL, записывается в файлы данных через некоторое (более короткое или длинное) время после совершения транзакции. Если вы хотите, вы можете назвать это кешем :) В противном случае, я второе, что сказал @a_horse_with_no_name.
Дезсо

Ответы:

35

Не существует надежной, авторской записи последнего измененного времени таблицы. Использование relfilenode неправильно по многим причинам:

  • Записи сначала записываются в журнал головки записи (WAL), затем лениво в кучу (файлы таблиц). Когда запись находится в WAL, Pg не спешит записывать ее в кучу, и она может даже не записаться до следующей системной контрольной точки;

  • Большие столы имеют несколько вилок, вам нужно будет проверить все вилки и выбрать новейшую временную метку;

  • Простой SELECTможет генерировать операции записи в базовую таблицу из-за установки подсказки;

  • autovaccum и другое обслуживание, которое не изменяет видимые пользователю данные, все еще изменяет файлы отношения;

  • некоторые операции, например vaccum full, заменят relfilenode. Это может быть не там, где вы ожидаете, если вы пытаетесь смотреть на него одновременно, не взяв соответствующую блокировку.

Несколько вариантов

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

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

Немного менее ужасной альтернативой является использование LISTENи NOTIFY. Подключите внешний процесс-демон к PostgreSQL и LISTENдля событий. Используйте ON INSERT OR UPDATE OR DELETEтриггеры для отправки NOTIFYs при изменении таблицы с таблицей oid в качестве полезной нагрузки notify. Они отправляются при фиксации транзакции. Ваш демон может накапливать уведомления об изменениях и лениво записывать их обратно в таблицу в базе данных. Если происходит сбой системы, вы теряете записи о последних изменениях, но это нормально, вы просто рассматриваете все таблицы как только что измененные, если запускаете после сбоя.

Чтобы избежать худшего из проблем параллелизма, вы могли бы вместо этого регистрировать временные метки изменения, используя before insert or update or delete or truncate on tablename for each statement executeтриггер, обобщенный, чтобы принять отношение oid в качестве параметра. Это добавит (relation_oid, timestamp)пару в таблицу регистрации изменений. Затем у вас есть вспомогательный процесс на отдельном соединении или периодически вызываемый вашим приложением, объединяющий эту таблицу для получения последней информации, объединяющий ее в сводную таблицу самых последних изменений и усекающую таблицу журнала. Единственное преимущество этого подхода по сравнению с прослушиванием / уведомлением состоит в том, что он не теряет информацию о сбое - но он еще менее эффективен.

Другой подход может написать функцию расширения C , который использует (например) ProcessUtility_hook, ExecutorRun_hookи т.д. , чтобы изменения таблицы ловушки и Лениво статистик обновлений. Я не смотрел, чтобы увидеть, насколько это будет практично; взгляните на различные опции _hook в источниках.

Лучшим способом было бы исправить код статистики для записи этой информации и отправить исправление в PostgreSQL для включения в ядро. Не просто начните с написания кода; подними свою идею о хакерах, как только ты подумаешь об этом достаточно, чтобы иметь четко определенный способ сделать это (то есть начать с чтения кода, а не просто публиковать вопрос "как мне ..."). Возможно, было бы неплохо добавить время последнего обновления pg_stat_..., но вам нужно будет убедить сообщество в том, что оно того стоило, или предоставить способ сделать его отслеживаемым по желанию - и вам придется написать код, чтобы сохранить статистику и отправить патч , потому что только тот, кто хочет эту функцию, будет беспокоиться об этом.

Как бы я это сделал

Если бы мне пришлось это сделать, и у меня не было времени написать патч, чтобы сделать это правильно, я бы, вероятно, использовал подход прослушивания / уведомления, описанный выше.

Обновление для временных отметок фиксации PostgreSQL 9.5

Обновление : PostgreSQL 9.5 имеет временные метки коммитов . Если вы включили их postgresql.conf(и делали это в прошлом), вы можете проверить метку времени фиксации для строки с наибольшим, xminчтобы приблизить время последнего изменения. Это только приблизительное значение, потому что, если самые последние строки были удалены, они не будут учитываться.

Кроме того, записи отметок времени фиксации хранятся только в течение ограниченного времени. Так что, если вы хотите сказать, когда таблица, которая не была сильно изменена, будет изменена, ответ будет «не знаю, давно».

Крейг Рингер
источник
17

PostgreSQL 9.5 позволяет отслеживать последний измененный коммит.

  1. Проверьте, что фиксация трека включена или выключена с помощью следующего запроса

    show track_commit_timestamp;
  2. Если он возвращает «ON», перейдите к шагу 3, иначе измените postgresql.conf

    cd /etc/postgresql/9.5/main/
    vi postgresql.conf

    + Изменить

    track_commit_timestamp = off

    в

    track_commit_timestamp = on

    Перезагрузите систему

    Повторите шаг 1.

  3. Используйте следующий запрос для отслеживания последнего коммита

    SELECT pg_xact_commit_timestamp(xmin), * FROM  YOUR_TABLE_NAME;
    
    SELECT pg_xact_commit_timestamp(xmin), * FROM YOUR_TABLE_NAME where COLUMN_NAME=VALUE;
Thirumal
источник
1
Вам не нужно перезагружать систему на шаге 2. просто перезапустите процесс. например sudo service postgresql restart.
Иосиф
3

Да, такое поведение можно ожидать - данные об изменениях немедленно сохраняются в журнале транзакций. Файлы данных могут быть обновлены с задержкой checkpoint_timeout (по умолчанию 5 минут). Postgres не хранится постоянно в любое время, когда вы запрашиваете.

Павел Стухуле
источник
Я не уверен, что понимаю, как это отвечает на вопрос. Да, данные хранятся в журнале транзакций, но это не означает, что можно легко получить время модификации для конкретной таблицы ( если это содержимое все еще находится в журнале, то можно проанализировать журнал, но вещи воспроизводятся скорее быстро).
Чарльз Даффи
Конечно, вы можете получить всю необходимую информацию из журнала, но вопросы были направлены на mtime файлов данных - актуализация файлов данных может быть довольно случайной - несколько секунд - несколько минут (максимум 1 час) после фиксации.
Павел Стехуле
Сама попытка ОП заключалась в просмотре файлов, но их реальное намерение - получить таблицу времени. Но да, я понимаю, откуда вы пришли (объясняя, почему то, что они делали, не сработало) сейчас.
Чарльз Даффи
2

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

Вот мой подход:

При условии, что в каждой таблице есть столбцы id(PK), created_on(отметка времени вставки) и updated_on(отметка времени обновления, может быть NULL), вы можете

SELECT id,greatest(created_on,updated_on) FROM %s ORDER BY greatest(created_on,updated_on) DESC LIMIT 1;

Если вы сопоставите это и добавите число строк, вы можете создать тег версии, который будет выглядеть так count:id#timestamp, и он будет уникальным для каждой версии данных в таблице.

Laurent
источник