PostgreSQL DELETE FROM 失敗並出現“錯誤:試圖刪除不可見的元組”
錯誤
嘗試刪除包含無效時間戳的元組
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';
結束於
ERROR: attempted to delete invisible tuple
有一個 2009 年的郵件列表討論完全相同的錯誤消息,OP 已將其修復,但我沒有找到關於他如何做到這一點或可能導致此錯誤的原因的解釋。
由於在 Google 上沒有點擊率以及我對 PostgreSQL 的了解有限,我很無助。
是什麼導致了腐敗
我有一個 PostgreSQL 9.5.5 伺服器(~4TB 數據,所有預設設置,除了提高的記憶體限制)在 Debian 8 上執行,當作業系統核心恐慌時——可能是在重建交換所在的 /dev/md1 時。在此之前,PostgreSQL 用一個 400GB 的日誌文件佔用了幾乎所有的磁碟空間。作業系統不再啟動,磁碟檢查正常,所以我從 LiveCD 啟動並將每個塊設備備份到映像,以防萬一。我已經成功地從 /dev/md2 重建了 / 目錄,fsck 顯示了一個乾淨的文件系統,並且我已經將 PGDATA 文件夾備份到了外部硬碟上。
我為嘗試恢復所做的事情
在我格式化 md 設備並重新安裝作業系統以及新的 postgresql-9.5 之後,我停止了 PostgreSQL 伺服器,將 PGDATA 文件夾移動並更改為 postgres 使用者,然後啟動伺服器——一切似乎都很好,沒有錯誤。
我一開始
pg_dumpall
,它就死了Error message from server: ERROR: timestamp out of range
我自然而然地嘗試刪除有問題的元組,結果卻
invisible tuple
一遍又一遍地遇到同樣的錯誤。我嘗試過的事情
首先,由於頁面損壞,DELETE 查詢失敗,所以我設置了以下設置:
zero_damaged_pages = on ignore_system_indexes = on enable_indexscan = off enable_bitmapscan = off enable_indexonlyscan = off
現在我注意到,當我再次執行相同的查詢時,伺服器一遍又一遍地將相同的頁面歸零,不確定這意味著什麼:
invalid page in block 92800 of relation base/16385/16443; zeroing out page
我嘗試以未定義的順序進行以下操作:
pg_resetxlog -D $PGDATA
完成工作沒有任何錯誤或消息- 刪除了所有索引,包括 pkey 約束
CREATE TABLE aaa AS (SELECT * FROM comments);
導致Segmentation fault
開啟
heap_deform_tuple (tuple=tuple@entry=0x7f0d1be29b08, tupleDesc=tupleDesc@entry=0x7f0d1a35abe0, values=values@entry=0x7ffd57a5beb0, isnull=isnull@entry=0x7ffd57a65af0 "\001\001")
它是可重現的,並留下約 9GB 的核心轉儲。
SELECT COUNT(*) from comments;
允許VACUUM comments;
完成,相同的技巧不適用於其他表。SELECT COUNT(*) from photos;
現在VACUUM photos;
死了ERROR: MultiXactId 302740528 has not been created yet -- apparent wraparound
- 這個問題困擾著每張桌子,其他錯誤不再出現。想法
DB受到大量(影響~~ON CONFLICT
可能重複)~~寫入VACUUM
的nonexistent MultiXactIds``invisible tuple
- 數據是用爬蟲在 2 年多的時間裡收集的,我完全可以失去一些數據
- 現在我做備份
- 表之間沒有關係約束,也沒有任何觸發器
這是截至目前的 pg_controldata 輸出:
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
更新
- (2016 年 12 月 9 日)在閱讀有關不存在的 MultiXactIds的資訊時,我記得我的數據庫在崩潰時並未處於操作負載下,但它正在處理手動
VACUUM
請求。在我意識到磁碟上只剩下 3% 的空間後,我讓網路伺服器和爬蟲離線。我應該檢查/var/log
大文件,但我錯誤地歸咎於 PostgreSQL 並嘗試VACUUM FULL
了,結果發現它由於設備上剩餘的空間太少而中止。所以我開始了普通的 VACUUM 並留在那裡。- (2016 年 12 月 14 日)從 Github 下載 PostgreSQL 源的 9.5 分支,註釋掉heapam.c和multixact.c中的塊並編譯它,希望它不會拋出這些錯誤。但是伺服器無法啟動,因為它必須使用與我從 APT 獲得的相同的標誌進行配置。大約有 47 個標誌,每個標誌都需要一個名稱不明顯的依賴項,所以我放棄了這個想法。
- (2016 年 12 月 16 日)我找到了一種方法,可以通過清零相關頁面來消除時間戳無效的元組。我首先在以下選項中設置
psql
:\set FETCH_COUNT 1 \pset pager off
然後我做
SELECT ctid, * FROM comments;
。這樣,它會在查詢結束之前吐出ctid
一個壞元組。然後我繼續用零填充該頁面:dd if=/dev/zero of=/var/lib/postgresql/9.5/main/base/16385/16443 bs=8K seek=92803 count=1 conv=notrunc
但是以這種方式清零的每一頁都會破壞前一頁,導致頁面16442
現在有一個帶有無效時間戳的元組。不知道我在這裡做錯了什麼。
- (2016 年 12 月 16 日)在寫入1.3GB(可能是 800GB )後嘗試
pg_dump -Fc --table photos vw > photos.bak
導致分段錯誤。這是伺服器日誌: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
這是一個簡短的堆棧跟踪:
#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)
我不知道如何解決這個問題。
- (2016 年 12 月 29 日)我編寫了一個實用程序,它可以
SELECT * FROM tablename LIMIT 10000 OFFSET 0
增加偏移量並縮小死元組的範圍,並且它已經成功地在我的本地機器上複製了數據,除了元組(我希望唯一的)我手動損壞了。如果伺服器重新啟動,它也應該等待。但是我的 RAID 上沒有足夠的空間,我slowdisk
在 8TB 硬碟上創建了一個表空間。當我嘗試時CREATE DATABASE vwslow WITH TABLESPACE slowdisk
,它不會出現錯誤: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;
手動
CHECKPOINT
導致相同的錯誤。伺服器重新啟動使檢查點錯誤消失,讓我執行我的工具。如果可行,將回答我的問題並發布程式碼。
好吧,我已經設法使 and 的恢復過程自動化
SELECT
,INSERT INTO
跳過範圍並等待伺服器崩潰。我首先在 Node 中對其進行了編碼 - 它從 中提取了未損壞的數據comments
,並且仍在繼續。昨天我決定嘗試 Golang,這裡是一個帶有 Go 程式碼的倉庫:https ://github.com/kaivi/pg_ripper我會盡快更新它,所以它真的可以解決壞元組,而不僅僅是放棄整體包含一個的範圍。