PostgreSQL DELETE FROM échoue avec `Erreur: tentative de suppression d'un tuple invisible`


25

L'erreur

Essayer de supprimer des tuples contenant des horodatages non valides avec

DELETE FROM comments WHERE date > '1 Jan 9999' OR date < '1 Jan 2000' OR date_found > '1 Jan 9999' OR date_found < '1 Jan 2000';

fini dans

ERROR:  attempted to delete invisible tuple

Il y a une liste de diffusion de 2009 discutant exactement du même message d'erreur, où OP l'a corrigé, mais je ne trouve aucune explication sur la façon dont il l'a fait ou sur ce qui aurait pu conduire à cette erreur.

Je suis impuissant en raison du manque de hits sur Google et de ma connaissance limitée de PostgreSQL.

Ce qui a conduit à la corruption

J'ai eu un serveur PostgreSQL 9.5.5 ( ~ 4 To de données, tous les paramètres par défaut, à l'exception des limites de mémoire augmentées ) fonctionnant sur Debian 8, lorsque le noyau du système d'exploitation a paniqué - probablement lors de la reconstruction / dev / md1 où le swap était situé. Avant cela, PostgreSQL a consommé presque tout l'espace disque avec un fichier journal de 400 Go. Le système d'exploitation n'a plus jamais redémarré, les vérifications de disque étaient OK, j'ai donc démarré à partir d'un LiveCD et sauvegardé chaque périphérique de bloc sur des images, juste au cas où. J'ai réussi à reconstruire le répertoire / à partir de / dev / md2, fsck a montré un système de fichiers propre et j'ai sauvegardé le dossier PGDATA sur un disque dur externe.

Ce que j'ai fait pour tenter de récupérer

Après avoir formaté les périphériques md et réinstallé le système d'exploitation avec un nouveau postgresql-9.5, j'ai arrêté le serveur PostgreSQL, déplacé et tronqué le dossier PGDATA pour l'utilisateur de postgres et démarré le serveur - tout semblait bien, il n'y avait pas d'erreurs.

Dès que j'ai commencé pg_dumpall, il est mort avec

Error message from server: ERROR:  timestamp out of range

J'ai naturellement essayé de supprimer les tuples incriminés, pour finir avec la même invisible tupleerreur encore et encore.

Ce que j'ai essayé

Tout d'abord, les requêtes DELETE ont échoué en raison de pages endommagées, j'ai donc défini les paramètres suivants:

zero_damaged_pages = on
ignore_system_indexes = on
enable_indexscan = off
enable_bitmapscan = off
enable_indexonlyscan = off

Maintenant, j'ai remarqué que lorsque j'exécute à nouveau les mêmes requêtes, le serveur remet à zéro les mêmes pages encore et encore, je ne sais pas ce que cela signifie:

invalid page in block 92800 of relation base/16385/16443; zeroing out page

J'ai essayé de suivre dans un ordre non défini:

  • pg_resetxlog -D $PGDATA a fait son travail sans erreurs ni messages
  • Supprimé tous les index, y compris les contraintes pkey
  • CREATE TABLE aaa AS (SELECT * FROM comments);conduit à Segmentation faultle

    heap_deform_tuple (tuple=tuple@entry=0x7f0d1be29b08, tupleDesc=tupleDesc@entry=0x7f0d1a35abe0, values=values@entry=0x7ffd57a5beb0, isnull=isnull@entry=0x7ffd57a65af0 "\001\001") Il est reproductible et laisse un vidage de mémoire de ~ 9 Go.

  • SELECT COUNT(*) from comments;autorisé VACUUM comments;à terminer, la même astuce ne fonctionne pas sur les autres tables.
  • SELECT COUNT(*) from photos;et VACUUM photos;meurt maintenant avec ERROR: MultiXactId 302740528 has not been created yet -- apparent wraparound- celui-ci hante chaque table, où d'autres erreurs n'apparaissent plus.

Pensées

  • DB était martelé par de nombreuses écritures ( peut-être en double ) avec la ON CONFLICTclause DB faisait une VACUUMlorsque la panique du noyau s'est produite, je crois que c'est ce qui en reste qui cause des problèmes avec nonexistent MultiXactIdsetinvisible tuple
  • Les données ont été collectées avec le robot sur une période de 2 ans et plus, et je suis tout à fait d'accord pour en perdre une partie
  • Maintenant je fais des sauvegardes
  • Il n'y avait aucune contrainte relationnelle entre les tables ni aucun déclencheur

Voici la sortie de pg_controldata dès maintenant:

pg_control version number:            942
Catalog version number:               201510051
Database system identifier:           6330224129664261958
Database cluster state:               in production
pg_control last modified:             Thu 08 Dec 2016 01:06:22 AM EET
Latest checkpoint location:           1562/8F9F8A8
Prior checkpoint location:            1562/8F7F460
Latest checkpoint's REDO location:    1562/8F9F8A8
Latest checkpoint's REDO WAL file:    000000010000156200000008
Latest checkpoint's TimeLineID:       1
Latest checkpoint's PrevTimeLineID:   1
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID:          0/40781255
Latest checkpoint's NextOID:          67798231
Latest checkpoint's NextMultiXactId:  1
Latest checkpoint's NextMultiOffset:  0
Latest checkpoint's oldestXID:        615
Latest checkpoint's oldestXID's DB:   1
Latest checkpoint's oldestActiveXID:  0
Latest checkpoint's oldestMultiXid:   1
Latest checkpoint's oldestMulti's DB: 1
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Time of latest checkpoint:            Thu 08 Dec 2016 01:06:22 AM EET
Fake LSN counter for unlogged rels:   0/1
Minimum recovery ending location:     0/0
Min recovery ending loc's timeline:   0
Backup start location:                0/0
Backup end location:                  0/0
End-of-backup record required:        no
wal_level setting:                    minimal
wal_log_hints setting:                off
max_connections setting:              100
max_worker_processes setting:         8
max_prepared_xacts setting:           0
max_locks_per_xact setting:           64
track_commit_timestamp setting:       off
Maximum data alignment:               8
Database block size:                  8192
Blocks per segment of large relation: 131072
WAL block size:                       8192
Bytes per WAL segment:                16777216
Maximum length of identifiers:        64
Maximum columns in an index:          32
Maximum size of a TOAST chunk:        1996
Size of a large-object chunk:         2048
Date/time type storage:               64-bit integers
Float4 argument passing:              by value
Float8 argument passing:              by value
Data page checksum version:           0

Mises à jour

  • ( 9 décembre 2016 ) En lisant sur MultiXactIds inexistant , je me suis souvenu que ma base de données n'était pas sous charge opérationnelle au moment de l'accident, mais qu'elle traitait une VACUUMdemande manuelle . J'ai mis les serveurs Web et les robots d'exploration hors ligne après avoir réalisé qu'il ne restait que 3% d'espace sur les disques. J'aurais dû vérifier les /var/logfichiers volumineux, mais j'ai blâmé PostgreSQL par erreur et j'ai essayé VACUUM FULLde le trouver avorté en raison du peu d'espace restant sur l'appareil. J'ai donc commencé VACUUM ordinaire et en reste là.
  • ( 14 décembre 2016 ) A téléchargé une branche 9.5 des sources PostgreSQL depuis Github, a commenté les blocs dans heapam.c et multixact.c et l'a compilé en espérant qu'il ne lèvera pas ces erreurs. Mais le serveur ne démarrait pas, car il devait être configuré avec les mêmes drapeaux que celui utilisé par APT. Il y avait environ 47 drapeaux, chacun nécessitant une dépendance avec un nom non évident, alors j'ai abandonné cette idée.
  • ( 16 décembre 2016 ) J'ai trouvé un moyen de me débarrasser des tuples avec des horodatages invalides en remettant à zéro les pages pertinentes. J'ai d'abord défini les options suivantes dans psql:

    \set FETCH_COUNT 1
    \pset pager off

    Je fais alors SELECT ctid, * FROM comments;. De cette façon, il recrache ctidun mauvais tuple avant la fin de la requête. Je procède ensuite à remplir cette page avec des zéros: dd if=/dev/zero of=/var/lib/postgresql/9.5/main/base/16385/16443 bs=8K seek=92803 count=1 conv=notruncMais chaque page, mise à zéro de cette façon, rompt la page précédente, ce qui donne à la page 16442un tuple avec un horodatage non valide. Je ne sais pas ce que je fais mal ici.

  • ( 16 décembre 2016 ) Tentative de pg_dump -Fc --table photos vw > photos.bakrésultats en erreur de segmentation après 1,3 Go ( sur probablement 800 Go ) écrits. Voici le journal du serveur:

    2016-12-16 18:48:05 EET [19337-2] LOG:  server process (PID 29088) was terminated by signal 11: Segmentation fault
    2016-12-16 18:48:05 EET [19337-3] DETAIL:  Failed process was running: COPY public.photos (id, owner_id, width, height, text, date, link, thumb, album_id, time_found, user_id, lat, long) TO stdout;
    2016-12-16 18:48:05 EET [19337-4] LOG:  terminating any other active server processes
    2016-12-16 18:48:05 EET [19342-2] WARNING:  terminating connection because of crash of another server process
    2016-12-16 18:48:05 EET [19342-3] DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
    2016-12-16 18:48:05 EET [19342-4] HINT:  In a moment you should be able to reconnect to the database and repeat your command.
    2016-12-16 18:48:05 EET [19337-5] LOG:  all server processes terminated; reinitializing
    2016-12-16 18:48:06 EET [29135-1] LOG:  database system was interrupted; last known up at 2016-12-14 22:58:59 EET
    2016-12-16 18:48:07 EET [29135-2] LOG:  database system was not properly shut down; automatic recovery in progress
    2016-12-16 18:48:07 EET [29135-3] LOG:  invalid record length at 1562/A302F878
    2016-12-16 18:48:07 EET [29135-4] LOG:  redo is not required
    2016-12-16 18:48:07 EET [29135-5] LOG:  MultiXact member wraparound protections are now enabled
    2016-12-16 18:48:07 EET [19337-6] LOG:  database system is ready to accept connections
    2016-12-16 18:48:07 EET [29139-1] LOG:  autovacuum launcher started

    Voici un bref stacktrace:

    #0  pglz_decompress (source=source@entry=0x7fbfb6b99b13 "32;00/0ag4d/Jnz\027QI\003Jh3A.jpg", slen=<optimized out>,
        dest=dest@entry=0x7fbf74a0b044 "", rawsize=926905132)
    #1  0x00007fc1bf120c12 in toast_decompress_datum (attr=0x7fbfb6b99b0b)
    #2  0x00007fc1bf423c83 in text_to_cstring (t=0x7fbfb6b99b0b)

    Je ne sais pas comment contourner cela.

  • ( 29 décembre 2016 ) J'ai écrit un utilitaire qui SELECT * FROM tablename LIMIT 10000 OFFSET 0incrémente le décalage et se rétrécit autour des tuples morts, et il a réussi à dupliquer les données sur ma machine locale à l'exception des tuples ( j'espère les seuls ) que j'ai corrompus manuellement. Il est également censé attendre si le serveur redémarre. Cependant, je n'avais pas assez d'espace sur mon RAID et j'ai créé un espace slowdiskde table sur un disque dur de 8 To. Lorsque j'essaie de le faire CREATE DATABASE vwslow WITH TABLESPACE slowdisk, il ne le fera pas avec des erreurs:

    2016-12-29 02:34:13 EET [29983-1] LOG:  request to flush past end of generated WAL; request 950412DE/114D59, currpos 1562/A3030C70
    2016-12-29 02:34:13 EET [29983-2] CONTEXT:  writing block 58368001 of relation base/16385/16473
    2016-12-29 02:34:13 EET [29983-3] ERROR:  xlog flush request 950412DE/114D59 is not satisfied --- flushed only to 1562/A3030C70
    2016-12-29 02:34:13 EET [29983-4] CONTEXT:  writing block 58368001 of relation base/16385/16473
    2016-12-29 02:34:13 EET [30005-44212] postgres@vw ERROR:  checkpoint request failed
    2016-12-29 02:34:13 EET [30005-44213] postgres@vw HINT:  Consult recent messages in the server log for details.
    2016-12-29 02:34:13 EET [30005-44214] postgres@vw STATEMENT:  CREATE DATABASE vwslow WITH TABLESPACE slowdisk;

    Le manuel a CHECKPOINTentraîné les mêmes erreurs.

    Un redémarrage du serveur a fait disparaître l'erreur de point de contrôle et m'a permis d'exécuter mon outil. Répondra à ma question et publiera le code si cela fonctionne.


Lisez ceci et agissez dessus avant d'essayer de faire autre chose: wiki.postgresql.org/wiki/Corruption . Bien qu'il semble qu'il soit un peu tard. Je soupçonne fortement les problèmes de disque / la reconstruction RAID d'être la cause principale ici.
Craig Ringer

Avez-vous conservé une copie du répertoire de données avant d'effectuer un resetxlog, etc.?
Craig Ringer

Pas le datadir lui-même, mais j'ai déplacé les images de disque brutes hors site où elles sont plus sûres. Ils vont bien, car j'ai reconstruit mon RAID à partir d'eux.
Kai

1
@CraigRinger allez-vous écrire une réponse à ce sujet? Vous êtes probablement l'un des rares utilisateurs qui répondent dans la balise Postgres et pourrait dire quelque chose d'utile sur le problème. Il semble que très peu de choses puissent être faites.
ypercubeᵀᴹ

4
Vous n'allez pas trouver la réponse sur celui-ci. Erwin pourra peut-être vous aider. Timide, cherchez David Fetter ou AndrewSW / RhodiumToad sur irc.freenode.net/#postgresql. Dites-nous (dba.se) ce qu'ils trouvent. J'ai le sentiment que ce sera un travail de conseil rémunéré qui nécessitera un accès total à votre base de données. developer.postgresql.org/~adunstan linkedin.com/in/davidfetter Je n'ai aucune affiliation avec l'un de ces gars-là ou avec leurs sociétés. Mais ce sont les seuls à qui je ferais personnellement confiance pour sortir de cet accroc.
Evan Carroll

Réponses:


2

Eh bien, j'ai réussi à automatiser le processus de récupération de SELECTet INSERT INTO, en ignorant les plages et en attendant que le serveur tombe en panne. Je l'ai d'abord codé dans Node - il a extrait des données intactes commentset continue de fonctionner.

Hier, j'ai décidé d'essayer Golang, et voici un repo avec le code Go: https://github.com/kaivi/pg_ripper Je vais le mettre à jour bientôt afin qu'il fonctionne vraiment autour des mauvais tuples, et pas seulement abandonne dans l'ensemble gamme contenant un.

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.