PostgreSQL DELETE FROM gagal dengan `Kesalahan: berupaya menghapus tuple tak terlihat`

25

Kesalahannya

Mencoba menghapus tupel berisi cap waktu yang tidak valid dengan

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

berakhir di

ERROR:  attempted to delete invisible tuple

Ada milis dari 2009 yang membahas pesan kesalahan yang sama persis, di mana OP memperbaikinya, tapi saya tidak menemukan penjelasan bagaimana dia melakukannya atau apa yang mungkin menyebabkan kesalahan ini.

Saya tidak berdaya karena kurangnya hit di Google dan pengetahuan saya yang terbatas tentang PostgreSQL.

Apa yang menyebabkan korupsi

Saya memiliki server PostgreSQL 9.5.5 ( ~ data 4TB, semua pengaturan default, kecuali untuk batas memori yang ditingkatkan ) yang berjalan di Debian 8, ketika kernel OS panik - mungkin saat membangun kembali / dev / md1 di mana swap berada. Sebelum itu, PostgreSQL menghabiskan hampir semua ruang disk dengan file log 400GB. OS tidak pernah boot lagi, cek disk OK, jadi saya sudah boot dari LiveCD dan mencadangkan setiap perangkat blok ke gambar, untuk berjaga-jaga. Saya telah berhasil membangun kembali / direktori dari / dev / md2, fsck menunjukkan sistem file yang bersih, dan saya telah membuat cadangan folder PGDATA ke HDD eksternal.

Apa yang saya lakukan untuk mencoba pemulihan

Setelah saya memformat perangkat md dan menginstal ulang OS bersama dengan postgresql-9.5 yang baru, saya telah menghentikan server PostgreSQL, memindahkan dan membagi folder PGDATA ke pengguna postgres, dan memulai server - semuanya tampak baik-baik saja, tidak ada kesalahan.

Begitu saya mulai pg_dumpall, ia mati bersama

Error message from server: ERROR:  timestamp out of range

Saya secara alami mencoba untuk menghapus tuple yang menyinggung, hanya untuk berakhir dengan invisible tuplekesalahan yang sama berulang kali.

Hal yang saya coba

Pertama, DELETE kueri gagal karena laman rusak, jadi saya telah menetapkan pengaturan berikut:

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

Sekarang saya perhatikan bahwa ketika saya menjalankan kueri yang sama lagi, server nol keluar halaman yang sama berulang-ulang, tidak yakin apa artinya:

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

Saya sudah mencoba mengikuti dalam urutan yang tidak ditentukan:

  • pg_resetxlog -D $PGDATA melakukan tugasnya tanpa kesalahan atau pesan
  • Menghapus semua indeks termasuk batasan pkey
  • CREATE TABLE aaa AS (SELECT * FROM comments);mengarah ke Segmentation faultatas

    heap_deform_tuple (tuple=tuple@entry=0x7f0d1be29b08, tupleDesc=tupleDesc@entry=0x7f0d1a35abe0, values=values@entry=0x7ffd57a5beb0, isnull=isnull@entry=0x7ffd57a65af0 "\001\001") Ini dapat direproduksi dan meninggalkan dump inti ~ 9GB.

  • SELECT COUNT(*) from comments;diizinkan VACUUM comments;untuk menyelesaikan, trik yang sama tidak berfungsi di tabel lain.
  • SELECT COUNT(*) from photos;dan VACUUM photos;sekarang mati dengan ERROR: MultiXactId 302740528 has not been created yet -- apparent wraparound- yang satu ini menghantui setiap tabel, di mana kesalahan lain tidak muncul lagi.

Pikiran

  • DB dipalu oleh banyak ( mungkin duplikat ) menulis dengan ON CONFLICTklausa DB sedang melakukan VACUUMketika kernel panik terjadi, saya percaya itu adalah apa yang tersisa yang menyebabkan masalah dengan nonexistent MultiXactIdsdaninvisible tuple
  • Data dikumpulkan dengan perayap selama rentang 2+ tahun, dan saya benar-benar baik-baik saja dengan kehilangan sebagiannya
  • Sekarang saya melakukan backup
  • Tidak ada kendala relasional antara tabel atau pemicu

Berikut ini adalah output pg_controldata seperti yang sekarang:

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

Pembaruan

  • ( 9 desember 2016 ) Ketika membaca tentang MultiXactIds yang tidak ada , saya ingat bahwa database saya tidak sedang dimuat pada saat crash, tetapi sedang memproses VACUUMpermintaan manual . Saya telah mengambil webserver dan crawler secara offline setelah saya menyadari bahwa hanya ada 3% ruang yang tersisa di disk. Saya seharusnya memeriksa /var/logfile-file besar, tetapi saya keliru menyalahkan PostgreSQL dan mencoba VACUUM FULL, hanya untuk menemukannya dibatalkan karena ruang yang tersisa sedikit pada perangkat. Jadi saya sudah memulai VACUUM biasa dan membiarkannya begitu.
  • ( 14 Desember 2016 ) Mengunduh 9,5 cabang sumber PostgreSQL dari Github, mengomentari blok di heapam.c dan multixact.c dan mengompilasinya dengan harapan tidak akan membuang kesalahan ini. Tetapi server tidak mau memulai, karena harus dikonfigurasi dengan flag yang sama dengan yang saya gunakan di APT. Ada sekitar 47 bendera, masing-masing membutuhkan ketergantungan dengan nama yang tidak jelas, jadi saya menyerah pada ide itu.
  • ( 16 desember 2016 ) Saya telah menemukan cara untuk menghilangkan tupel dengan stempel waktu yang tidak valid dengan mem-posting halaman yang relevan. Saya pertama-tama mengatur opsi berikut di psql:

    \set FETCH_COUNT 1
    \pset pager off

    Saya kemudian melakukannya SELECT ctid, * FROM comments;. Dengan cara itu memuntahkan ctidtuple buruk sebelum permintaan mati. Saya kemudian melanjutkan untuk mengisi halaman itu dengan angka nol: dd if=/dev/zero of=/var/lib/postgresql/9.5/main/base/16385/16443 bs=8K seek=92803 count=1 conv=notruncTetapi setiap halaman, yang dihilangkan dengan cara ini, memecah halaman sebelumnya, menghasilkan halaman yang 16442sekarang memiliki sebuah tuple dengan cap waktu yang tidak valid. Tidak yakin apa yang saya lakukan salah di sini.

  • ( 16 desember 2016 ) Mencoba pg_dump -Fc --table photos vw > photos.bakmenghasilkan kesalahan segmentasi setelah 1.3GB ( dari mungkin 800GB ) tertulis. Berikut adalah log 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

    Berikut ini adalah stacktrace pendek:

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

    Saya tidak tahu bagaimana cara mengatasinya.

  • ( 29 desember 2016 ) Saya telah menulis sebuah utilitas yang berfungsi SELECT * FROM tablename LIMIT 10000 OFFSET 0, menambah offset dan mempersempit tupel yang mati, dan telah berhasil menggandakan data pada mesin lokal saya kecuali tupel ( saya harap satu-satunya ) yang saya korup secara manual. Seharusnya juga menunggu jika server restart. Namun saya tidak memiliki cukup ruang pada RAID saya, dan saya telah membuat tablespace slowdiskpada HDD 8TB. Ketika saya berusaha CREATE DATABASE vwslow WITH TABLESPACE slowdisk, itu tidak akan terjadi dengan kesalahan:

    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;

    Manual CHECKPOINTmenghasilkan kesalahan yang sama.

    Restart server membuat kesalahan pos pemeriksaan hilang dan biarkan saya menjalankan alat saya. Akan menjawab pertanyaan saya dan menerbitkan kode jika berfungsi.

Kai
sumber
Baca ini dan bertindak sesuai sebelum mencoba melakukan hal lain: wiki.postgresql.org/wiki/Corruption . Meskipun sepertinya sudah agak terlambat. Saya sangat curiga masalah disk / RAID membangun kembali menjadi penyebab utama di sini.
Craig Ringer
Apakah Anda menyimpan salinan direktori data dari sebelum Anda melakukan resetxlog dll?
Craig Ringer
Bukan datadir itu sendiri, tapi saya sudah memindahkan gambar disk mentah ke tempat yang lebih aman. Mereka baik-baik saja, karena saya telah membangun kembali RAID saya dari mereka.
Kai
1
@CraigRinger akankah Anda menulis jawaban tentang ini? Anda mungkin salah satu dari sedikit pengguna yang menjawab tag Postgres dan bisa mengatakan sesuatu yang bermanfaat tentang masalah ini. Sepertinya sangat sedikit yang bisa dilakukan.
ypercubeᵀᴹ
4
Anda tidak akan menemukan jawaban untuk yang ini. Erwin mungkin bisa membantu Anda. Selain itu, cari David Fetter, atau AndrewSW / RhodiumToad di irc.freenode.net/#postgresql. Beri tahu kami (dba.se) apa yang mereka temukan. Saya merasa ini akan menjadi pekerjaan konsultasi berbayar yang akan membutuhkan akses total ke database Anda. developer.postgresql.org/~adunstan linkedin.com/in/davidfetter Saya tidak memiliki afiliasi dengan salah satu dari mereka, atau perusahaan mereka. Tapi, merekalah satu-satunya yang secara pribadi saya percayai untuk keluar dari hambatan itu.
Evan Carroll

Jawaban:

2

Yah, saya telah berhasil mengotomatiskan proses pemulihan SELECTdan INSERT INTO, melewatkan rentang dan menunggu jika server crash. Saya pertama kali mengkodekannya di Node - itu merobek data yang tidak rusak comments, dan masih berjalan.

Kemarin saya telah memutuskan untuk mencoba Golang, dan ini adalah repo dengan kode Go: https://github.com/kaivi/pg_ripper Saya akan segera memperbaruinya sehingga benar-benar bekerja di sekitar tuple yang buruk, dan tidak hanya menyerah secara keseluruhan rentang yang mengandung satu.

Kai
sumber