Croissance de la table TOAST hors de contrôle - FULLVAC ne fait rien


9

Récemment, j'ai eu un serveur PostgreSQL 8.2.11 mis à niveau vers 8.4 afin de profiter des fonctionnalités d'autovacuum et d'être en ligne avec 30 autres autres serveurs PGSQL. Cela a été fait par un groupe informatique distinct qui gère le matériel, nous n'avons donc pas beaucoup de choix sur les autres mises à niveau (ne verront pas 9+ pendant un certain temps). Le serveur existe dans un environnement très fermé (réseau isolé, privilèges root limités) et fonctionne sur RHEL5.5 (i686). Après la mise à niveau, la base de données a constamment augmenté de 5 à 6 Go par jour. Normalement, la base de données dans son ensemble est d'environ 20 Go; actuellement, il est d'environ 89 Go. Nous avons quelques autres serveurs qui exécutent des bases de données équivalentes et synchronisent réellement les enregistrements entre eux via une application tierce (dont je n'ai pas accès aux rouages ​​internes). Les autres bases de données font ~ 20 Go comme il se doit.

En exécutant le SQL suivant, il est assez évident qu'il y a un problème avec une table particulière et, plus spécifiquement, sa table TOAST.

SELECT nspname || '.' || relname AS "relation",
    pg_size_pretty(pg_relation_size(C.oid)) AS "size"
  FROM pg_class C
  LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
  WHERE nspname NOT IN ('pg_catalog', 'information_schema')
  ORDER BY pg_relation_size(C.oid) DESC
  LIMIT 20;

Ce qui produit:

              relation | Taille  
------------------------------------ + ---------  
  pg_toast.pg_toast_16874 | 89 Go  
  fews00.warmstates | 1095 Mo  
  ...  
(20 rangées)

Cette table TOAST est destinée à une table appelée "timeseries" qui enregistre de gros enregistrements de données blobées. Un SUM(LENGTH(blob)/1024./1024.)de tous les enregistrements de la série temporelle donne ~ 16 Go pour cette colonne. Il ne devrait y avoir aucune raison que la table TOAST de cette table soit aussi grande qu'elle l'est.

J'ai effectué un VACUUM FULL VERBOSE ANALYZE timeseries, et le vide se termine sans erreur.

INFO: passer l'aspirateur "pg_toast.pg_toast_16874"
INFO: "pg_toast_16874": trouvé 22483 versions amovibles, 10475318 non amovibles en 10448587 pages
DÉTAIL: aucune version de ligne morte ne peut encore être supprimée.
Les versions de ligne non amovibles ont une longueur de 37 à 2036 octets.
Il y avait 20121422 pointeurs d'article inutilisés.
L'espace libre total (y compris les versions de lignes amovibles) est de 0 octet. 4944885 pages sont ou deviendront vides, y compris 0 à la fin du tableau. 4944885 pages contenant 0 octets libres sont des destinations de déplacement potentielles.
CPU 75.31s / 29.59u sec écoulé 877.79 sec.
INFO: l'index "pg_toast_16874_index" contient désormais 10475318 versions de lignes sur 179931 pages.
DÉTAIL: 23884 versions de lignes d'index ont été supprimées.
Les pages d'index 101623 ont été supprimées, 101623 sont actuellement réutilisables.
CPU 1.35s / 2.46u sec écoulé 21.07 sec.

REINDEXer la table qui a libéré un certain espace (~ 1 Go). Je ne peux pas CLUSTER la table car il n'y a pas assez d'espace sur le disque pour le processus, et j'attends de reconstruire la table entièrement car j'aimerais savoir pourquoi elle est tellement plus grande que les bases de données équivalentes que nous avons.

Exécutez une requête du wiki PostgreSQL ici - "Afficher la base de données Bloat" , et voici ce que j'obtiens:

base de données_actuelle | schemaname | nom_table | tbloat | wastedbytes | iname | ibloat | gaspillages  
----------------- + ------------ + ------------------- ------------- + -------- + ------------- + ------------- -------------------- + -------- + --------------  
ptrdb04 | fews00 | timeseries | 1.0 | 0 | idx_timeseries_synchlevel | 0,0 | 0  
ptrdb04 | fews00 | timeseries | 1.0 | 0 | idx_timeseries_localavail | 0,0 | 0  
ptrdb04 | fews00 | timeseries | 1.0 | 0 | idx_timeseries_expirytime | 0,0 | 0  
ptrdb04 | fews00 | timeseries | 1.0 | 0 | idx_timeseries_expiry_null | 0,0 | 0  
ptrdb04 | fews00 | timeseries | 1.0 | 0 | uniq_localintid | 0,0 | 0  
ptrdb04 | fews00 | timeseries | 1.0 | 0 | pk_timeseries | 0,1 | 0  
ptrdb04 | fews00 | idx_timeseries_expiry_null | 0,6 | 0 | ? | 0,0 | 0

Il semble que la base de données ne considère pas du tout cet espace comme "vide", mais je ne vois tout simplement pas d'où vient tout l'espace disque!

Je soupçonne que ce serveur de base de données décide d'utiliser 4 à 5 fois plus d'espace disque pour enregistrer les mêmes enregistrements extraits des autres serveurs de données. Ma question est la suivante: existe-t-il un moyen de vérifier la taille du disque physique d'une ligne? Je voudrais comparer la taille d'une ligne de cette base de données à une autre base de données "saine".

Merci pour toute l'aide que vous pourrez fournir!

MISE À JOUR 1

J'ai fini par reconstruire la table à partir d'un schéma vidé en raison de sa taille (je ne pouvais pas la laisser seule un autre jour). Après avoir synchronisé les données, via le processus de synchronisation du logiciel, la table TOAST était ~ 35 Go; cependant, je ne pouvais en représenter que 9 Go à partir de cette colonne blob qui devrait être la plus longue en termes de valeurs. Je ne sais pas d'où vient l'autre 26 Go. CLUSTERed, VACUUM FULLed et REINDEXed in vain. Les fichiers postgresql.conf entre les serveurs de données locaux et distants sont exactement les mêmes. Y a-t-il une raison pour laquelle cette base de données essaie de stocker chaque enregistrement avec un espace plus grand sur le disque?

MISE À JOUR 2 - Corrigé

J'ai finalement décidé de reconstruire complètement la base de données à partir de zéro, allant même jusqu'à réinstaller les packages PostgreSQL84 sur le système. Le chemin d'accès à la base de données a été réinitialisé et les espaces disque logiques ont été nettoyés. Le processus de synchronisation des logiciels tiers a repeuplé les tables, et la taille finale s'est révélée être ~ 12 Go ! Malheureusement, cela ne permet en aucune façon de résoudre la source exacte du problème. Je vais le regarder pendant un jour ou deux et voir s'il y a des différences majeures avec la façon dont la base de données revitalisée gère la table TOAST et publier ces résultats ici.

Taille de la relation


ptrdb04=> SELECT nspname || '.' || relname AS "relation",
ptrdb04->     pg_size_pretty(pg_relation_size(C.oid)) AS "size"
ptrdb04->   FROM pg_class C
ptrdb04->   LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
ptrdb04->   WHERE nspname NOT IN ('pg_catalog', 'information_schema')
ptrdb04->   ORDER BY pg_relation_size(C.oid) DESC
ptrdb04->   LIMIT 2;

        relation          |   taille   
 ------------------------- + --------- 
 pg_toast . pg_toast_17269 | 18 Go 
 fews00 . warmstates        | 1224 Mo
 ( 2 rangées )  

VACUUM VERBOSE ANALYZE timeseries;

INFO: "séries temporelles": trouvé 12699 versions de lignes amovibles, 681961 non amovibles dans 58130 sur 68382 pages
DÉTAIL: aucune version de ligne morte ne peut encore être supprimée.
Il y avait 105847 pointeurs d'articles inutilisés.
0 pages sont entièrement vides.
CPU 0,83 s / 2,08 u sec écoulé 33,36 s.
INFO: passer l'aspirateur "pg_toast.pg_toast_17269"
INFO: index analysé "pg_toast_17269_index" pour supprimer les versions de ligne 2055849
DÉTAIL: CPU 0.37s / 2.92u sec écoulé 13.29 sec.
INFO: «pg_toast_17269»: versions de ligne 2055849 supprimées dans 518543 pages
DÉTAIL: CPU 8.60s / 3.21u sec écoulé 358.42 sec.
INFO: l'index "pg_toast_17269_index" contient désormais 7346902 versions de ligne sur 36786 pages
DÉTAIL: les versions de la ligne d'index 2055849 ont été supprimées.
10410 pages d'index ont été supprimées, 5124 sont actuellement réutilisables.
CPU 0,00 s / 0,00 u s écoulé 0,01 s.
INFO: "pg_toast_17269": trouvé 1286128 amovibles, 2993389 versions de lignes non amovibles en 1257871 sur 2328079 pages
DÉTAIL: aucune version de ligne morte ne peut encore être supprimée.
Il y avait 18847 pointeurs d'objets inutilisés.
0 pages sont entièrement vides.
CPU 26.56s / 13.04u sec écoulé 714.97 sec.
INFO: analyse de "fews00.timeseries"
INFO: "timeseries": numérisé 30000 de 68382 pages, contenant 360192 lignes en direct et 0 lignes mortes; 30000 lignes dans l'échantillon, 821022 lignes totales estimées

La seule différence notable après la reconstruction (autre que l'utilisation du disque) est

INFO: "pg_toast_17269": trouvé 1286128 amovible, 2993389 versions de ligne non amovible
comme @CraigRinger l'a mentionné dans un commentaire. Le nombre de lignes non amovibles est beaucoup plus petit qu'auparavant.

Nouvelle question: d' autres tables peuvent-elles affecter la taille d'une autre table? (via des clés étrangères et autres) La reconstruction de la table n'a rien fait, mais la reconstruction de la base de données entière s'est avérée résoudre le problème.


Pourquoi n'avez-vous pas mis à niveau directement vers 9.2? Il a encore plus d'améliorations dans le domaine du vide que le 8.4 (et le 8.4 sera en fin de vie l'année prochaine de toute façon)
a_horse_with_no_name

J'ai mis à jour le message. La mise à niveau n'a pas été effectuée par notre boutique et pas nécessairement par notre demande. Malheureusement, nous n'avons pas cette option pour passer à 9+.
BrM13

D'ACCORD. Je voulais juste m'assurer que vous ne
négligiez

Réponses:


9

Cette:

INFO: "pg_toast_16874": found 22483 removable, 10475318 nonremovable row versions in 10448587 pages 22483 removable, 10475318 nonremovable row versions in 10448587 pages

suggère que le problème sous-jacent est que quelque chose peut toujours "voir" ces lignes afin qu'elles ne puissent pas être supprimées.

Les candidats pour cela sont:

  • Perte des transactions préparées. Vérifiez pg_catalog.pg_prepared_xacts; il doit être vide. Exécutez également SHOW max_prepared_transactions; il doit signaler zéro.

  • Sessions de longue durée avec une transaction ouverte et inactive. Dans PostgreSQL 8.4 et supérieur, cela ne devrait être un problème que pour les SERIALIZABLEtransactions. Vérifiez pg_catalog.pg_stat_activitypour les <IDLE> in transactionsessions.

Vous avez probablement un client qui ne parvient pas à valider ou à annuler des transactions pendant de longues périodes d'inactivité.

Si cela ne s'avère pas être le cas, la prochaine chose que je vérifierais serait de faire une somme octet_sizede chaque colonne du tableau d'intérêt. Comparez cela à la pg_relation_sizetable et à sa TOASTtable d'appoint. S'il y a une grande différence, l'espace consommé n'est probablement plus par des lignes visibles et vous avez probablement des problèmes de ballonnement de table. S'ils sont assez similaires, vous pouvez commencer à réduire l'emplacement de l'utilisation de l'espace en résumant les tailles d'octets par colonne, en obtenant les premières valeurs 'n', etc.


1) pg_prepared_xacts et max_prepared_transactions sont en effet revenus vides. 2) Il y a certainement des transactions IDLE SELECT * FROM pg_stat_activity WHERE current_query LIKE '<IDLE>%';qui rapportent environ 30 à 40 résultats; cependant, cela semble assez normal. J'ai vérifié quelques serveurs "sains", et ils étaient les mêmes.
BrM13

3) Voici ce que j'ai fait. En boucle dans les colonnes de série temporelle, en tirant octet_length (colonne). Multipliez chaque valeur par le nombre de lignes et additionnez-les. Pour la série temporelle, j'ai obtenu ~ 430 Mo (près des 493 Mo de pg_relation_size) et 438 Mo pour la table TOAST (en utilisant les colonnes chunk_id, chunk_seq, chunk_data). Les estimations semblent correctes, et la table TOAST est loin de la relation_size d'environ 2 ordres de grandeur (60 Go aujourd'hui). Il semble que j'ai un ballonnement, mais pas le type traditionnel (ballonnement inutilisé). Sinon, un FULLVAC devrait s'occuper du problème.
BrM13

Les sessions @Brad Idle sont correctes , ce ne sont que les sessions inactives avec des transactions ouvertes qui posent problème, c'est-à-dire <IDLE> in transactionet seulement si elles (a) sont inactives depuis un certain temps et (b) utilisent l' SERIALIZABLEisolement ou si vous êtes sur 8.3 ou plus âgée.
Craig Ringer

@Brad Il est intéressant de noter que seul le TOASTtableau semble cependant gonflé. BTW, si vous avez utilisé VACUUM FULLbeaucoup de choses sur un serveur antérieur à 9.0, vous voudrez, REINDEXcar VACUUM FULLsur ces versions, cela pourrait causer un ballonnement d'index important. Je me demande maintenant si quelqu'un a placé un absurde FILLFACTORsur la table des toasts, bien que cela ne devrait pas vous laisser dépasser la consommation d'espace 10 fois.
Craig Ringer

Merci pour la clarification IDLE. J'ai pensé que c'est ce que tu voulais dire, mais c'est bon à savoir avec certitude. Comme pour FILLFACTOR, le tableau utilise la valeur par défaut. FYI - Selon la documentation 8.4 CREATE TABLE, la valeur par défaut est 100, et vous ne pouvez pas définir un FILLFACTOR pour la table TOAST.
BrM13

0

Je n'ai aucune idée de pourquoi il est gonflé. Mais j'ai fait quelques recherches et ce lien a peut-être un aperçu: http://postgresql.1045698.n5.nabble.com/A-154-GB-table-swelled-to-527-GB-on-the-Slony-slave -Comment compact-it-td5543034.html ... Ce n'est pas votre situation exacte mais peut-être est-ce assez proche pour vous aider à atteindre le fond de la météorisation fantôme.

Cependant, je pense que la seule façon de compacter cette table à ce stade est de la CLUSTER. Comme vous manquez d'espace disque, c'est un problème.

Voici ma suggestion pour cela: créer un espace disque logique sur un lecteur différent avec beaucoup d'espace supplémentaire, puis affecter votre table problème à cet espace disque logique. PostgreSQL copiera la table dans le nouvel espace de table (probablement en la verrouillant dans le processus, vous aurez donc besoin d'une fenêtre de maintenance). VACFULL ensuite la table (efface la plupart de l'ancien espace consommé par la table dans l'espace de table par défaut). Ensuite, CLUSTER la table et elle devrait se compacter. Ensuite, remettez-le dans l'espace disque logique par défaut et exécutez à nouveau VACFULL (pour effacer l'espace inutilisé dans le nouvel espace disque logique).


En fait, j'ai fini par reconstruire la table (vider le schéma et reconstruire à partir de cela) et extraire les données directement de l'une des bases de données distantes. Une fois le processus terminé, la base de données était toujours de 35 Go, dont seulement 9 Go étaient représentés par une colonne blob "large". CLUSTERed, VACUUM FULLed, REINDEXed, et je suis toujours assis sur une tonne d'utilisation mystérieuse du disque.
BrM13

Link is dead :(
hayd
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.