web-dev-qa-db-fra.com

Obtention de la dernière date de modification d'une table de base de données PostgreSQL

J'essaie de savoir quand ma table a été modifiée en vérifiant sa date de modification de fichier telle qu'elle est décrite dans cette réponse . Mais le résultat n'est pas toujours correct. La date de modification du fichier est mise à jour quelques minutes après la mise à jour de ma table. Est-ce un comportement correct? PostgreSQL stocke-t-il les modifications de table dans un cache, puis les vide-t-il sur le disque dur?

Alors, comment puis-je obtenir la date de dernière modification correcte d'une table (supposons que les modifications de vide automatique sont également correctes)?

J'utilise PostgreSQL 9.2 sous Linux Centos 6.2 x64.

40
hank

Il n'y a pas d'enregistrement fiable et faisant autorité de la dernière heure modifiée d'une table. L'utilisation du relfilenode est incorrecte pour de nombreuses raisons:

  • Les écritures sont d'abord enregistrées dans le journal de la tête d'écriture (WAL), puis paresseusement dans le tas (les fichiers de table). Une fois que l'enregistrement est dans WAL, Pg ne se précipite pas pour l'écrire dans le tas, et il pourrait même ne pas être écrit jusqu'au prochain point de contrôle du système;

  • Les plus grandes tables ont plusieurs fourches, vous devez vérifier toutes les fourches et choisir le plus récent horodatage;

  • Un simple SELECT peut générer une activité d'écriture dans la table sous-jacente en raison du paramètre hint-bit;

  • l'autovaccum et toute autre maintenance qui ne modifie pas les données visibles par l'utilisateur modifie toujours les fichiers de relation;

  • certaines opérations, comme vaccum full, remplacera le relfilenode. Ce n'est peut-être pas là où vous vous attendez si vous essayez de le regarder simultanément sans prendre un verrou approprié.

Quelques options

Si vous n'avez pas besoin de fiabilité, vous pouvez potentiellement utiliser les informations dans pg_stat_database et pg_stat_all_tables. Ceux-ci peuvent vous donner l'heure de la dernière réinitialisation des statistiques et les statistiques d'activité depuis la dernière réinitialisation des statistiques. Il ne vous dit pas quand l'activité la plus récente a eu lieu, mais seulement que c'était depuis la dernière réinitialisation des statistiques, et il n'y a aucune information sur ce qui s'est passé avant la réinitialisation des statistiques. C'est donc limité, mais c'est déjà là.

Une option pour le faire de manière fiable consiste à utiliser un déclencheur pour mettre à jour une table contenant les dernières heures modifiées pour chaque table. Sachez que cela sérialisera toutes les écritures dans la table , détruisant la concurrence. Cela ajoutera également un peu de frais généraux à chaque transaction. Je ne le recommande pas.

Une alternative un peu moins terrible est d'utiliser LISTEN et NOTIFY. Demandez à un processus démon externe de se connecter à PostgreSQL et à LISTEN pour les événements. Utilisation ON INSERT OR UPDATE OR DELETE déclenche l'envoi de NOTIFYs lorsqu'une table change, avec la table oid comme charge utile de notification. Ceux-ci sont envoyés lorsque la transaction est validée. Votre démon peut accumuler des notifications de modification et les réécrire paresseusement dans une table de la base de données. Si le système tombe en panne, vous perdez votre enregistrement des modifications les plus récentes, mais ce n'est pas grave, vous traitez simplement toutes les tables comme juste modifiées si vous démarrez après un crash.

Pour éviter le pire des problèmes de concurrence, vous pouvez enregistrer les horodatages de modification à l'aide d'un before insert or update or delete or truncate on tablename for each statement execute trigger, généralisé pour prendre la relation oid comme paramètre. Cela insère un (relation_oid, timestamp) paire dans une table d'enregistrement des modifications. Vous disposez ensuite d'un processus d'assistance sur une connexion distincte, ou appelé périodiquement par votre application, agrégez ce tableau pour les dernières informations, fusionnez-le dans un tableau récapitulatif des modifications les plus récentes et tronquez le tableau du journal. Le seul avantage de cela par rapport à l'approche d'écoute/notification est qu'il ne perd pas d'informations sur les plantages - mais c'est encore moins efficace.

Une autre approche pourrait consister à écrire une fonction d'extension C qui utilise (par exemple) ProcessUtility_hook, ExecutorRun_hook, etc. pour intercepter les changements de table et mettre à jour les statistiques paresseusement. Je n'ai pas cherché à voir comment cela serait pratique; jetez un œil aux différentes options _hook dans les sources.

La meilleure façon serait de patcher le code statistique pour enregistrer ces informations et de soumettre un patch à PostgreSQL pour inclusion dans le core. Ne commencez pas simplement par écrire du code; soulevez votre idée sur les pirates une fois que vous y avez suffisamment réfléchi pour avoir une façon bien définie de le faire (c'est-à-dire commencer par lire le code, ne vous contentez pas de poster en demandant "comment puis-je ..."). Il serait peut-être bien d'ajouter la dernière mise à jour à pg_stat_..., mais vous devrez convaincre la communauté que cela en vaut la peine ou fournir un moyen de le faire éventuellement suivre - et vous devrez écrire le code pour conserver les statistiques et soumettre un patch , car seul celui qui veut cette fonctionnalité va s'en préoccuper.

Comment je le ferais

Si je devais le faire et que je n'avais pas le temps d'écrire un correctif pour le faire correctement, j'utiliserais probablement l'approche d'écoute/notification décrite ci-dessus.

Mise à jour pour les horodatages de validation de PostgreSQL 9.5

Mise à jour : PostgreSQL 9.5 a validation d'horodatages . Si vous les avez activés dans postgresql.conf (et cela aussi par le passé), vous pouvez vérifier l'horodatage de validation de la ligne avec le plus grand xmin à approximatif la dernière fois modifiée. Ce n'est qu'une approximation car si les lignes les plus récentes ont été supprimées, elles ne seront pas comptées.

En outre, les enregistrements d'horodatage de validation ne sont conservés que pendant une durée limitée. Donc, si vous voulez savoir quand une table qui n'est pas beaucoup modifiée est modifiée, la réponse sera effectivement "ne sais pas, il y a quelque temps".

36
Craig Ringer

PostgreSQL 9.5 nous permet de suivre la dernière validation modifiée.

  1. Vérifiez que la validation du suivi est activée ou désactivée à l'aide de la requête suivante

    show track_commit_timestamp;
    
  2. S'il retourne "ON", passez à l'étape 3 sinon modifiez postgresql.conf

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

    Changement

    track_commit_timestamp = off
    

    à

    track_commit_timestamp = on
    

    Redémarrez le système

    Répétez l'étape 1.

  3. Utilisez la requête suivante pour suivre le dernier commit

    SELECT pg_xact_commit_timestamp(xmin), * FROM  YOUR_TABLE_NAME;
    
    SELECT pg_xact_commit_timestamp(xmin), * FROM YOUR_TABLE_NAME where COLUMN_NAME=VALUE;
    
17
Thirumal

J'ai presque la même exigence afin de maintenir un cache de certaines tables sur une application cliente. Je dis presque, car je n'ai pas vraiment besoin de connaître l'heure de la dernière modification, mais seulement de détecter si quelque chose a changé depuis la dernière synchronisation du cache.

Voici mon approche:

À condition d'avoir un id (PK), created_on (horodatage d'insertion) et updated_on (horodatage de mise à jour, peut être NULL) sur chaque table, vous pouvez

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

Si vous concattez ceci et ajoutez le nombre de lignes, vous pouvez créer un version tag qui ressemble à count:id#timestamp, et il sera unique pour chaque version des données du tableau.

3
laurent

Oui, cela peut se produire - les données sur les modifications sont immédiatement enregistrées dans le journal des transactions. Les fichiers de données peuvent être mis à jour avec un délai checkpoint_timeout (la valeur par défaut est de 5 minutes). Postgres ne tient pas en permanence à tout moment que vous demandez.

3
Pavel Stehule