web-dev-qa-db-fra.com

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

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 ne liste de diffusion de 2009 discutant exactement le 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 de/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 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 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 erreur invisible Tuple 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 fault sur

    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 mémoire de ~ 9 Go.

  • SELECT COUNT(*) from comments; a permis à VACUUM comments; de se 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ù les autres erreurs n'apparaissent plus.

Pensées

  • DB était martelé par de nombreuses écritures ( éventuellement en double ) avec la clause ON CONFLICT DB faisait un VACUUM lorsque la panique du noyau s'est produite, je crois que c'est ce qui reste qui cause des problèmes avec nonexistent MultiXactIds Et invisible 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 environ MultiXactIds inexistant , je me suis souvenu que ma base de données n'était pas sous charge opérationnelle pour le moment du crash, mais il traitait une demande manuelle de VACUUM. 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 /var/log Pour les fichiers volumineux, mais j'ai blâmé PostgreSQL par erreur et essayé VACUUM FULL, Seulement pour le trouver abandonné en raison du peu d'espace restant sur l'appareil. J'ai donc commencé VACUUM ordinaire et en reste là.
  • ( 14 décembre 2016 ) Téléchargé une branche 9.5 des sources PostgreSQL depuis Github, commenté les blocs dans heapam.c et - multixact.c et l'a compilé en espérant qu'il ne générera pas ces erreurs. Mais le serveur ne voulait pas démarrer, 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 se 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 ensuite SELECT ctid, * FROM comments;. De cette façon, il crache le ctid d'un 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=notrunc Mais chaque page, mise à zéro de cette façon, brise la page précédente, ce qui fait que la page 16442 A maintenant un tuple avec un horodatage invalide. Je ne sais pas ce que je fais mal ici.

  • ( 16 décembre 2016 ) La tentative de pg_dump -Fc --table photos vw > photos.bak Entraîne une erreur de segmentation après 1,3 Go ( sur probablement 800 Go ) écrit. 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 fait SELECT * FROM tablename LIMIT 10000 OFFSET 0, Incrémentant le décalage et se rétrécissant autour des tuples morts, et il a avec succès les données dupliquées sur ma machine locale à l'exception des tuples ( J'espère que les seules ) que j'ai corrompues 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 de table slowdisk sur un disque dur de 8 To. Lorsque j'essaie de 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 CHECKPOINT a provoqué 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.

25
Kai

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

Hier, j'ai décidé d'essayer Golang, et voici un dépôt 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 ne renonce pas seulement à toute la gamme en contenant un.

2
Kai