it-swarm.com.de

PostgreSQL DELETE FROM schlägt mit "Fehler: Versuch, unsichtbares Tupel zu löschen" fehl

Der Fehler

Versuch, Tupel mit ungültigen Zeitstempeln mit zu löschen

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';

endet in

ERROR:  attempted to delete invisible Tuple

Es gibt eine Mailingliste aus dem Jahr 2009 , die genau dieselbe Fehlermeldung bespricht, bei der OP sie behoben hat, aber ich finde keine Erklärung dafür, wie er es getan hat oder was möglicherweise zu diesem Fehler geführt hat.

Ich bin hilflos, weil es bei Google an Treffern mangelt und ich nur begrenzte Kenntnisse über PostgreSQL habe.

Was zu Korruption führte

Ich hatte einen PostgreSQL 9.5.5-Server ( ~ 4 TB Daten, alle Standardeinstellungen, mit Ausnahme der erhöhten Speicherbeschränkungen ), die unter Debian 8 ausgeführt wurden, wenn OS-Kernel geriet in Panik - wahrscheinlich während der Neuerstellung von/dev/md1, wo sich Swap befand. Zuvor hat PostgreSQL fast den gesamten Speicherplatz mit einer 400-GB-Protokolldatei belegt. Das Betriebssystem wurde nie wieder gestartet, die Festplattenprüfungen waren in Ordnung, daher habe ich von einer LiveCD gestartet und jedes Blockgerät für alle Fälle auf Images gesichert. Ich habe das Verzeichnis/erfolgreich aus/dev/md2 neu erstellt, fsck hat ein sauberes Dateisystem angezeigt und den PGDATA-Ordner auf einer externen Festplatte gesichert.

Was ich getan habe, um mich zu erholen

Nachdem ich md-Geräte formatiert und das Betriebssystem zusammen mit einem neuen Postgresql-9.5 neu installiert habe, habe ich den PostgreSQL-Server gestoppt, den PGDATA-Ordner verschoben und an den Postgres-Benutzer weitergeleitet und den Server gestartet - alles schien in Ordnung zu sein, es gab keine Fehler.

Sobald ich pg_dumpall Gestartet habe, ist es mit gestorben

Error message from server: ERROR:  timestamp out of range

Ich habe natürlich versucht, die beleidigenden Tupel zu löschen, nur um immer wieder den gleichen invisible Tuple Fehler zu erhalten.

Dinge, die ich versucht habe

Zunächst einmal sind DELETE-Abfragen aufgrund beschädigter Seiten fehlgeschlagen. Daher habe ich folgende Einstellungen vorgenommen:

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

Jetzt ist mir aufgefallen, dass der Server beim erneuten Ausführen derselben Abfragen immer wieder dieselben Seiten auf Null setzt und nicht sicher ist, was dies bedeutet:

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

Ich habe versucht, in einer undefinierten Reihenfolge zu folgen:

  • pg_resetxlog -D $PGDATA Hat seine Arbeit ohne Fehler oder Meldungen erledigt
  • Alle Indizes einschließlich pkey-Einschränkungen wurden gelöscht
  • CREATE TABLE aaa AS (SELECT * FROM comments); führt zu Segmentation fault on

    heap_deform_Tuple ([email protected]=0x7f0d1be29b08, [email protected]=0x7f0d1a35abe0, [email protected]=0x7ffd57a5beb0, [email protected]=0x7ffd57a65af0 "\001\001") Es ist reproduzierbar und hinterlässt einen Core-Dump von ~ 9 GB.

  • SELECT COUNT(*) from comments; erlaubte VACUUM comments; abzuschließen, der gleiche Trick funktioniert nicht in anderen Tabellen.
  • SELECT COUNT(*) from photos; und VACUUM photos; sterben jetzt mit ERROR: MultiXactId 302740528 has not been created yet -- apparent wraparound - dieser verfolgt jede Tabelle, in der andere Fehler nicht mehr auftauchen.

Gedanken

  • DB wurde von vielen ( möglicherweise doppelten ) Schreibvorgängen mit der Klausel ON CONFLICT Gehämmert DB hat ein VACUUM ausgeführt, als eine Kernel-Panik auftrat. Ich glaube, es ist das, was davon übrig ist, das Probleme mit nonexistent MultiXactIds Und invisible Tuple Verursacht.
  • Die Daten wurden mit Crawler über einen Zeitraum von mehr als 2 Jahren gesammelt, und ich bin völlig in Ordnung, wenn ich einen Teil davon verliere
  • Jetzt mache ich Backups
  • Es gab keine relationalen Einschränkungen zwischen Tabellen und keine Trigger

Hier ist die Ausgabe von pg_controldata ab sofort:

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

Aktualisierung

  • ( 9. Dezember 2016 ) Beim Lesen über nicht vorhandene MultiXactIds habe ich mich daran erinnert, dass meine Datenbank im Moment nicht betriebsbereit war des Absturzes, aber es wurde eine manuelle VACUUM Anfrage verarbeitet. Ich habe die Webserver und Crawler offline geschaltet, nachdem mir klar wurde, dass nur noch 3% Speicherplatz auf den Festplatten vorhanden sind. Ich hätte /var/log Auf große Dateien überprüfen sollen, aber ich habe fälschlicherweise PostgreSQL beschuldigt und VACUUM FULL Versucht, nur um festzustellen, dass es abgebrochen wurde, weil auf dem Gerät nur noch wenig Speicherplatz vorhanden war. Also habe ich gewöhnliches VAKUUM gestartet und es dabei belassen.
  • ( 14. Dezember 2016 ) Hat einen 9.5-Zweig von PostgreSQL-Quellen von Github heruntergeladen und Blöcke in heapam.c und - auskommentiert. multixact.c und kompilierte es mit der Hoffnung, dass es diese Fehler nicht auslöst. Der Server wurde jedoch nicht gestartet, da er mit denselben Flags konfiguriert werden musste, die in einem von APT verwendeten verwendet wurden. Es gab ungefähr 47 Flaggen, von denen jede eine Abhängigkeit mit einem nicht offensichtlichen Namen erforderte, also gab ich diese Idee auf.
  • ( 16. Dezember 2016 ) Ich habe einen Weg gefunden, Tupel mit ungültigen Zeitstempeln durch Nullstellen relevanter Seiten loszuwerden. Ich habe zuerst folgende Optionen in psql festgelegt:

    \set FETCH_COUNT 1
    \pset pager off
    

    Ich mache dann SELECT ctid, * FROM comments;. Auf diese Weise spuckt es das ctid eines fehlerhaften Tupels aus, bevor die Abfrage stirbt. Ich fülle diese Seite dann mit Nullen: dd if=/dev/zero of=/var/lib/postgresql/9.5/main/base/16385/16443 bs=8K seek=92803 count=1 conv=notrunc Aber jede Seite, die auf diese Weise auf Null gesetzt wurde, bricht die vorherige Seite, was dazu führt, dass Seite 16442 Jetzt ein Tupel mit ungültigem Zeitstempel hat. Ich bin mir nicht sicher, was ich hier falsch mache.

  • ( 16. Dezember 2016 ) Der Versuch pg_dump -Fc --table photos vw > photos.bak Führt nach 1,3 GB ( wahrscheinlich zu einem Segmentierungsfehler 800 GB ) geschrieben. Hier ist das Serverprotokoll:

    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
    

    Hier ist eine kurze Stapelspur:

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

    Ich habe keine Ahnung, wie ich das umgehen soll.

  • ( 29. Dezember 2016 ) Ich habe ein Dienstprogramm geschrieben, das SELECT * FROM tablename LIMIT 10000 OFFSET 0 Ausführt, den Offset erhöht und um tote Tupel herum eingrenzt, und das hat es erfolgreich duplizierte Daten auf meinem lokalen Computer mit Ausnahme der Tupel ( Ich hoffe, die einzigen ), die ich manuell beschädigt habe. Es soll auch warten, wenn der Server neu startet. Ich hatte jedoch nicht mehr genügend Speicherplatz auf meinem RAID und habe einen Tablespace slowdisk auf einer 8-TB-Festplatte erstellt. Wenn ich versuche CREATE DATABASE vwslow WITH TABLESPACE slowdisk, Wird dies nicht mit Fehlern geschehen:

    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] [email protected] ERROR:  checkpoint request failed
    2016-12-29 02:34:13 EET [30005-44213] [email protected] HINT:  Consult recent messages in the server log for details.
    2016-12-29 02:34:13 EET [30005-44214] [email protected] STATEMENT:  CREATE DATABASE vwslow WITH TABLESPACE slowdisk;
    

    Das Handbuch CHECKPOINT führte zu denselben Fehlern.

    Durch einen Neustart des Servers wurde der Checkpoint-Fehler behoben und ich konnte mein Tool ausführen. Beantwortet meine Frage und veröffentlicht den Code, wenn er funktioniert.

25
Kai

Nun, ich habe es geschafft, den Wiederherstellungsprozess von SELECT und INSERT INTO Zu automatisieren, Bereiche zu überspringen und zu warten, wenn der Server abstürzt. Ich habe es zuerst in Node - es hat unbeschädigte Daten von comments gerippt und läuft noch.

Gestern habe ich beschlossen, Golang auszuprobieren, und hier ist ein Repo mit Go-Code: https://github.com/kaivi/pg_ripper Ich werde es bald aktualisieren, damit es wirklich um schlechte Tupel herum funktioniert, und gibt nicht nur den gesamten Bereich auf, der einen enthält.

2
Kai