Abrufen des letzten Änderungsdatums einer PostgreSQL-Datenbanktabelle

35

Ich versuche herauszufinden, wann meine Tabelle geändert wurde, indem ich das in dieser Antwort beschriebene Änderungsdatum der Datei überprüfe . Das Ergebnis ist jedoch nicht immer korrekt. Das Änderungsdatum der Datei wird einige Minuten nach dem Aktualisieren meiner Tabelle aktualisiert. Ist es korrektes Verhalten? Speichert PostgreSQL Tabellenänderungen in einem Cache und speichert sie dann auf der Festplatte?

Wie erhalte ich das korrekte Datum der letzten Änderung einer Tabelle (nehmen wir an, dass die automatischen Vakuumänderungen ebenfalls in Ordnung sind)?

Ich benutze PostgreSQL 9.2 unter Linux Centos 6.2 x64.

Strang
quelle
4
Ich denke nicht, dass die Änderungszeit der Datei zuverlässig ist. Es kann sich auch aufgrund eines automatischen Vakuums ändern. Die einzige zuverlässige Möglichkeit besteht darin, einen Änderungszeitstempel in Ihrer Tabelle zu speichern, der von einem Trigger verwaltet wird.
a_horse_with_no_name
Eine Idee wäre, dass die in den WAL-Dateien gespeicherten Informationen einige (kürzere oder längere) Zeit nach dem Festschreiben der Transaktion in die Datendateien geschrieben werden. Wenn du willst, kannst du das einen Cache nennen :) Ansonsten habe ich als zweites was @a_horse_with_no_name gesagt.
Dezso

Antworten:

35

Es gibt keine zuverlässige, autorisierende Aufzeichnung des letzten Änderungszeitpunkts einer Tabelle. Die Verwendung des relfilenode ist aus vielen Gründen falsch:

  • Schreibvorgänge werden zunächst im Schreibkopfprotokoll (WAL) und dann träge im Heap (den Tabellendateien) aufgezeichnet . Sobald der Datensatz in WAL ist, beeilt sich Pg nicht, ihn auf den Heap zu schreiben, und er wird möglicherweise erst beim nächsten Systemprüfpunkt geschrieben.

  • Größere Tabellen haben mehrere Gabeln. Sie müssen alle Gabeln überprüfen und den neuesten Zeitstempel auswählen.

  • Ein einfacher Befehl SELECTkann aufgrund der Einstellung des Hinweisbits Schreibaktivitäten für die zugrunde liegende Tabelle generieren.

  • Autovaccum und andere Wartungsarbeiten, die die vom Benutzer angezeigten Daten nicht ändern, ändern weiterhin die Beziehungsdateien.

  • Einige Operationen, wie zum Beispiel vaccum full, ersetzen den relfilenode. Es ist möglicherweise nicht der Ort, den Sie erwarten, wenn Sie versuchen, es gleichzeitig zu betrachten, ohne ein entsprechendes Schloss zu öffnen.

Ein paar Möglichkeiten

Wenn Sie keine Zuverlässigkeit benötigen, können Sie die Informationen möglicherweise in pg_stat_databaseund verwenden pg_stat_all_tables. Diese können Ihnen den Zeitpunkt des letzten Zurücksetzens der Statistiken und die Aktivitätsstatistik seit dem letzten Zurücksetzen der Statistiken anzeigen. Es gibt keine Auskunft darüber, wann die letzte Aktivität stattgefunden hat, nur darüber, dass sie seit dem letzten Zurücksetzen der Statistiken stattgefunden hat, und es gibt keine Informationen darüber, was vor dem Zurücksetzen der Statistiken geschehen ist. Also ist es begrenzt, aber es ist schon da.

Eine Möglichkeit, dies zuverlässig zu tun, besteht darin, einen Trigger zu verwenden, um eine Tabelle zu aktualisieren, die die zuletzt geänderten Zeiten für jede Tabelle enthält. Beachten Sie, dass dadurch alle Schreibvorgänge in die Tabelle serialisiert und die Parallelität zerstört wird. Außerdem wird jeder Transaktion ein gewisser Overhead hinzugefügt. Ich kann es nicht empfehlen.

Eine etwas weniger schreckliche Alternative ist die Verwendung von LISTENund NOTIFY. Stellen Sie über einen externen Daemon-Prozess eine Verbindung zu PostgreSQL und LISTENzu Ereignissen her. Verwenden Sie ON INSERT OR UPDATE OR DELETETrigger, um NOTIFYs zu senden, wenn sich eine Tabelle ändert, wobei die Tabelle oid als Benachrichtigungsnutzlast dient. Diese werden gesendet, wenn die Transaktion festgeschrieben wird. Ihr Daemon kann Änderungsbenachrichtigungen sammeln und diese träge in eine Tabelle in der Datenbank zurückschreiben. Wenn das System abstürzt, gehen die Aufzeichnungen der letzten Änderungen verloren, aber das ist in Ordnung. Wenn Sie nach einem Absturz starten, werden nur alle Tabellen als gerade geändert behandelt.

Um die schlimmsten Parallelitätsprobleme zu vermeiden, können Sie stattdessen die Änderungszeitstempel mithilfe eines before insert or update or delete or truncate on tablename for each statement executeTriggers protokollieren , der verallgemeinert wird, um die Relation oid als Parameter zu verwenden. Dies würde ein (relation_oid, timestamp)Paar in eine Änderungsprotokollierungstabelle einfügen . Sie haben dann einen Hilfsprozess auf einer separaten Verbindung oder werden regelmäßig von Ihrer App aufgerufen, aggregieren diese Tabelle für die neuesten Informationen, führen sie in einer Übersichtstabelle der neuesten Änderungen zusammen und kürzen die Protokolltabelle. Der einzige Vorteil gegenüber dem Listen / Notify-Ansatz besteht darin, dass beim Absturz keine Informationen verloren gehen - aber es ist auch noch weniger effizient.

Ein weiterer Ansatz könnte sein , eine C - Erweiterungsfunktion , dass Nutzungen (zB) zu schreiben ProcessUtility_hook, ExecutorRun_hooketc. zu stoppen Tabellenänderungen und lazily Update Statistiken. Ich habe nicht nachgesehen, wie praktisch dies sein würde. Schauen Sie sich die verschiedenen _hook-Optionen in den Quellen an.

Am besten patchen Sie den Statistikcode, um diese Informationen aufzuzeichnen, und senden Sie einen Patch an PostgreSQL, um ihn in den Core aufzunehmen. Beginnen Sie nicht einfach damit, Code zu schreiben. Wenden Sie sich an -hacker, wenn Sie genug darüber nachgedacht haben, um eine genau definierte Möglichkeit zu haben (dh lesen Sie zunächst den Code, und posten Sie nicht einfach die Frage, wie ich ...). Es mag nett sein, die zuletzt aktualisierten Zeiten hinzuzufügen pg_stat_..., aber Sie müssten die Community davon überzeugen, dass sich der Aufwand gelohnt hat, oder eine Möglichkeit bieten, ihn optional zu verfolgen - und Sie müssten den Code schreiben, um die Statistiken und Daten zu speichern Reichen Sie einen Patch ein , denn nur wer dieses Feature möchte, wird sich darum kümmern.

Wie ich es machen würde

Wenn ich dies tun müsste und nicht die Zeit hätte, einen Patch zu schreiben, um es richtig zu machen, würde ich wahrscheinlich den oben beschriebenen Listen / Notify-Ansatz verwenden.

Update für PostgreSQL 9.5-Commit-Zeitstempel

Update : PostgreSQL 9.5 verfügt über festgeschriebene Zeitstempel . Wenn Sie sie aktiviert haben postgresql.conf(und dies auch in der Vergangenheit getan haben), können Sie den Commit-Zeitstempel für die Zeile mit dem größten Wert überprüfen, um die letzte geänderte Zeit xminzu approximieren . Dies ist nur eine Annäherung, da die zuletzt gelöschten Zeilen nicht gezählt werden.

Commit-Zeitstempeldatensätze werden nur für eine begrenzte Zeit aufbewahrt. Wenn Sie also feststellen möchten, wann eine Tabelle, die nicht wesentlich geändert wurde, geändert wurde, lautet die Antwort "Keine Ahnung, vor einiger Zeit".

Craig Ringer
quelle
17

Mit PostgreSQL 9.5 können wir das zuletzt geänderte Commit nachverfolgen.

  1. Überprüfen Sie anhand der folgenden Abfrage, ob das Track-Commit aktiviert oder deaktiviert ist

    show track_commit_timestamp;
  2. Wenn "ON" zurückgegeben wird, fahren Sie mit Schritt 3 fort, andernfalls ändern Sie postgresql.conf

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

    Veränderung

    track_commit_timestamp = off

    zu

    track_commit_timestamp = on

    Starten Sie das System neu

    Wiederholen Sie Schritt 1.

  3. Verwenden Sie die folgende Abfrage, um das letzte Festschreiben zu verfolgen

    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
quelle
1
Sie müssen das System nicht in Schritt 2 neu starten. Starten Sie einfach den Prozess neu. zb sudo service postgresql restart.
ijoseph
3

Ja, dies kann erwartet werden - Daten über Änderungen werden sofort im Transaktionsprotokoll gespeichert. Datendateien können mit einer Verzögerung von checkpoint_timeout aktualisiert werden (Standard sind 5 Minuten). Postgres hält nicht permanent die Zeit, die Sie anfordern.

Pavel Stehule
quelle
Ich bin nicht sicher, ob ich verstehe, wie dies die Frage beantwortet. Ja, die Daten werden im Transaktionsprotokoll gespeichert, aber das bedeutet nicht, dass man leicht eine Änderungszeit für eine bestimmte Tabelle abrufen kann ( wenn sich dieser Inhalt noch im Protokoll befindet, kann man das Protokoll analysieren, aber die Dinge werden eher abgespielt schnell).
Charles Duffy
Sie können zwar alle erforderlichen Informationen aus dem Protokoll abrufen, die Fragen richteten sich jedoch an mtime of datafiles - die Aktualisierung von Datendateien kann ziemlich zufällig sein - einige Sekunden - einige Minuten (max. 1 Stunde) nach dem Festschreiben.
Pavel Stehule
Der eigene Versuch des OP bestand darin, Dateien zu betrachten, aber ihre eigentliche Absicht ist eindeutig, mtime einen Tisch zu bekommen. Aber ja, ich verstehe, woher du kommst (zu erklären, warum das, was sie getan haben, nicht funktioniert hat).
Charles Duffy
2

Ich habe fast die gleiche Anforderung, um einen Cache einiger Tabellen in einer Client-Anwendung zu verwalten. Ich sage fast , weil ich den Zeitpunkt der letzten Änderung nicht wirklich wissen muss, sondern nur um festzustellen, ob sich etwas geändert hat, seit der Cache das letzte Mal synchronisiert wurde.

Hier ist mein Ansatz:

Vorausgesetzt, Sie haben eine Spalte id(PK), created_on(Einfügezeitstempel) und updated_on(Aktualisierungszeitstempel, kann NULL sein) in jeder Tabelle, können Sie

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

Wenn Sie dies zusammenfassen und die Anzahl der Zeilen voranstellen, können Sie ein Versions-Tag erstellen , das aussieht count:id#timestampund für jede Version der Daten in der Tabelle eindeutig ist.

laurent
quelle