PostgreSQL DELETE FROM non riesce con `Errore: tentativo di eliminare la tupla invisibile`


25

L'errore

Tentativo di eliminare tuple contenenti timestamp non validi con

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

finisce in

ERROR:  attempted to delete invisible tuple

Esiste una mailing list del 2009 che parla dello stesso identico messaggio di errore, in cui OP lo ha risolto, ma non trovo alcuna spiegazione di come lo abbia fatto o di cosa potrebbe aver portato a questo errore.

Sono impotente a causa della mancanza di hit su Google e della mia conoscenza limitata di PostgreSQL.

Cosa ha portato alla corruzione

Ho avuto un server PostgreSQL 9.5.5 ( ~ 4TB di dati, tutte le impostazioni predefinite, tranne i limiti di memoria aumentati ) in esecuzione su Debian 8, quando il kernel del sistema operativo è andato nel panico - probabilmente durante la ricostruzione / dev / md1 dove si trovava lo swap. Prima di ciò, PostgreSQL ha assorbito quasi tutto lo spazio su disco con un file di registro da 400 GB. Il sistema operativo non si è mai riavviato, i controlli del disco erano OK, quindi ho avviato da un LiveCD e ho eseguito il backup di ogni dispositivo a blocchi sulle immagini, per ogni evenienza. Ho ricostruito con successo la directory / da / dev / md2, fsck ha mostrato un filesystem pulito e ho eseguito il backup della cartella PGDATA su un HDD esterno.

Cosa ho fatto per tentare il recupero

Dopo aver formattato i dispositivi md e reinstallato il sistema operativo insieme a un nuovo postgresql-9.5, ho fermato il server PostgreSQL, spostato e aggiunto la cartella PGDATA all'utente postgres e avviato il server: tutto sembrava a posto, non c'erano errori.

Non appena ho iniziato pg_dumpall, è morto con

Error message from server: ERROR:  timestamp out of range

Ho naturalmente cercato di eliminare le tuple offensive, solo per finire con lo stesso invisible tupleerrore ancora e ancora.

Cose che ho provato

Prima di tutto, le query DELETE non sono riuscite a causa di pagine danneggiate, quindi ho impostato le seguenti impostazioni:

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

Ora ho notato che quando eseguo di nuovo le stesse query, il server azzera continuamente le stesse pagine, non sono sicuro del significato:

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

Ho provato a seguire in un ordine indefinito:

  • pg_resetxlog -D $PGDATA ha fatto il suo lavoro senza errori o messaggi
  • Eliminati tutti gli indici inclusi i vincoli pkey
  • CREATE TABLE aaa AS (SELECT * FROM comments);porta a Segmentation faulton

    heap_deform_tuple (tuple=tuple@entry=0x7f0d1be29b08, tupleDesc=tupleDesc@entry=0x7f0d1a35abe0, values=values@entry=0x7ffd57a5beb0, isnull=isnull@entry=0x7ffd57a65af0 "\001\001") È riproducibile e lascia un core dump di ~ 9GB.

  • SELECT COUNT(*) from comments;permesso VACUUM comments;di completare, lo stesso trucco non funziona su altri tavoli.
  • SELECT COUNT(*) from photos;e VACUUM photos;ora muore con ERROR: MultiXactId 302740528 has not been created yet -- apparent wraparound- questo tormenta ogni tavolo, dove altri errori non compaiono più.

Pensieri

  • DB stava venendo martellato da molte ( forse duplicate ) scritture con la ON CONFLICTclausola che DB stava facendo VACUUMquando si è verificato il panico del kernel, credo che sia ciò che ne è rimasto a causare problemi nonexistent MultiXactIdseinvisible tuple
  • I dati sono stati raccolti con il crawler per un arco di oltre 2 anni e sono del tutto a posto con la perdita di alcuni di essi
  • Ora eseguo backup
  • Non ci sono stati vincoli relazionali tra le tabelle né trigger

Ecco l'output di pg_controldata a partire da ora:

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

aggiornamenti

  • ( 9 dicembre 2016 ) Durante la lettura di MultiXactIds inesistenti , ho ricordato che il mio database non era sotto carico operativo al momento del crash, ma stava elaborando una VACUUMrichiesta manuale . Ho portato offline i server web e i crawler dopo aver realizzato che sui dischi era rimasto solo il 3% di spazio. Avrei dovuto /var/logcercare file di grandi dimensioni, ma ho erroneamente incolpato PostgreSQL e provato VACUUM FULL, solo per trovarlo interrotto a causa del poco spazio lasciato sul dispositivo. Quindi ho iniziato il normale VUOTO e l'ho lasciato a quello.
  • ( 14 dicembre 2016 ) Scaricato un ramo 9.5 di fonti PostgreSQL da Github, commentato blocchi in heapam.c e multixact.c e compilato con la speranza che non generasse questi errori. Ma il server non si avviava, perché doveva essere configurato con gli stessi flag utilizzati in uno che avevo da APT. C'erano circa 47 bandiere, ognuna delle quali richiedeva una dipendenza con un nome non ovvio, quindi ho rinunciato a quell'idea.
  • ( 16 dicembre 2016 ) Ho trovato un modo per sbarazzarmi di tuple con timestamp non validi annullando le pagine pertinenti. Ho prima impostato le seguenti opzioni in psql:

    \set FETCH_COUNT 1
    \pset pager off

    Allora lo faccio SELECT ctid, * FROM comments;. In questo modo sputa fuori ctiduna brutta tupla prima che la query muoia. Procedo quindi a riempire quella pagina con zeri: dd if=/dev/zero of=/var/lib/postgresql/9.5/main/base/16385/16443 bs=8K seek=92803 count=1 conv=notruncma ogni pagina, azzerata in questo modo, rompe la pagina precedente, risultando in una pagina 16442ora con una tupla con timestamp non valido. Non sono sicuro di cosa sto facendo di sbagliato qui.

  • ( 16 dicembre 2016 ) Tentativo pg_dump -Fc --table photos vw > photos.bakdi errore di segmentazione dopo 1,3 GB ( su probabilmente 800 GB ) scritto. Ecco il registro del server:

    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

    Ecco una breve stack stack:

    #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)

    Non ho idea di come aggirare questo.

  • ( 29 dicembre 2016 ) Ho scritto un'utilità che lo fa SELECT * FROM tablename LIMIT 10000 OFFSET 0, incrementando l'offset e restringendolo intorno alle tuple morte, e ha duplicato con successo i dati sul mio computer locale tranne le tuple ( spero le uniche ) che ho corrotto manualmente. Dovrebbe inoltre attendere il riavvio del server. Tuttavia non avevo abbastanza spazio sul mio RAID e ho creato un tablespace slowdisksu un HDD da 8 TB. Quando provo a CREATE DATABASE vwslow WITH TABLESPACE slowdiskfarlo, non lo farà con errori:

    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;

    Il manuale ha CHECKPOINTprodotto gli stessi errori.

    Un riavvio del server ha fatto scomparire l'errore del checkpoint e mi ha permesso di eseguire il mio strumento. Risponderà alla mia domanda e pubblicherà il codice se funziona.


Leggi questo e agisci prima di provare a fare qualsiasi altra cosa: wiki.postgresql.org/wiki/Corruption . Anche se sembra che sia un po 'tardi. Sospetto fortemente che i problemi del disco / ricostruzione RAID siano la causa principale qui.
Craig Ringer l'

Hai conservato una copia della directory dei dati prima di eseguire un resetxlog ecc?
Craig Ringer l'

Non il datadir stesso, ma ho spostato le immagini dei dischi grezzi fuori sede dove sono più sicure. Stanno bene, perché ho ricostruito il mio RAID da loro.
Kai

1
@CraigRinger scriverai una risposta su questo? Probabilmente sei uno dei pochi utenti che rispondono nel tag Postgres e potrebbero dire qualcosa di utile sul problema. Sembra che si possa fare molto poco.
ypercubeᵀᴹ

4
Non troverai la risposta su questo. Erwin potrebbe essere in grado di aiutarti. Timido, cerca David Fetter o AndrewSW / RhodiumToad su irc.freenode.net/#postgresql. Dicci (dba.se) cosa trovano. Ho la sensazione che questo sarà un lavoro di consulenza a pagamento che richiederà l'accesso totale al tuo database. developer.postgresql.org/~adunstan linkedin.com/in/davidfetter Non ho alcuna affiliazione con nessuno di questi ragazzi o le loro compagnie. Ma sono gli unici di cui mi fiderei personalmente per uscire da quel problema.
Evan Carroll,

Risposte:


2

Bene, sono riuscito ad automatizzare il processo di recupero di SELECTe INSERT INTO, saltando gli intervalli e aspettando che il server si blocchi. L'ho codificato per la prima volta in Node: ha strappato dati non danneggiati commentse continua a funzionare.

Ieri ho deciso di provare Golang, ed ecco un repository con il codice Go: https://github.com/kaivi/pg_ripper Lo aggiornerò presto in modo da aggirare tuple cattive e non rinunciare al tutto intervallo contenente uno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.