Greenplum的gpcheckcat是用來檢查system catalog的一致性的命令,其中有專門針對磁盤文件的檢查($GPHOME/bin/lib/gpcheckcat -R persistent -p 5432 databasename),主要是對於system catalog裏關於磁盤文件的記錄與實際磁盤文件的情況的一致性檢查。本文結合一個實際錯誤介紹如何解決這類元數據錯誤。
現象
gpcheckcat的日誌文件(比如/home/gpadmin/gpAdminLogs/gpcheckcat_20150513.log)裏,最後有如下報錯:
SUMMARY REPORT
===================================================================
Total runtime for 14 test(s): 0:44:02.24
Failed test(s) that are not reported here: persistent
See /home/gpadmin/gpAdminLogs/gpcheckcat_20150513.log for detail
在檢查persistent的部分,可以發現如下錯誤:
20150513:15:49:03:007025 gpcheckcat:mdw:gpadmin-[INFO]:-[FAIL] gp_persistent_relation_node <=> filesystem
20150513:15:49:03:007025 gpcheckcat:mdw:gpadmin-[ERROR]:-gp_persistent_relation_node <=> filesystem found 22896 issue(s)
20150513:15:49:03:007025 gpcheckcat:mdw:gpadmin-[ERROR]:-
SELECT coalesce(a.tablespace_oid, b.tablespace_oid) as tablespace_oid,
coalesce(a.database_oid, b.database_oid) as database_oid,
coalesce(a.relfilenode_oid, b.relfilenode_oid) as relfilenode_oid,
coalesce(a.segment_file_num, b.segment_file_num) as segment_file_num,
a.relfilenode_oid is null as filesystem,
b.relfilenode_oid is null as persistent,
b.relkind, b.relstorage
FROM gp_persistent_relation_node a
FULL OUTER JOIN (
SELECT p.*, c.relkind, c.relstorage
FROM gp_persistent_relation_node_check() p
LEFT OUTER JOIN pg_class c
ON (p.relfilenode_oid = c.relfilenode)
WHERE (p.segment_file_num = 0 or c.relstorage != 'h')
) b ON (a.tablespace_oid = b.tablespace_oid and
a.database_oid = b.database_oid and
a.relfilenode_oid = b.relfilenode_oid and
a.segment_file_num = b.segment_file_num)
WHERE (a.relfilenode_oid is null OR
(a.persistent_state = 2 and b.relfilenode_oid is null)) and
coalesce(a.database_oid, b.database_oid) in (
SELECT oid FROM pg_database WHERE datname = current_database()
UNION ALL
SELECT 0
);
20150513:15:49:03:007025 gpcheckcat:mdw:gpadmin-[ERROR]:---------
20150513:15:49:03:007025 gpcheckcat:mdw:gpadmin-[ERROR]:-mdw:5432:/data/master/gpseg-1
20150513:15:49:03:007025 gpcheckcat:mdw:gpadmin-[ERROR]:- tablespace_oid | database_oid | relfilenode_oid | segment_file_num | filesystem | persistent | relkind | relstorage
20150513:15:49:03:007025 gpcheckcat:mdw:gpadmin-[ERROR]:- 1663 | 30408 | 744321 | 0 | t | f | None | None
......
日誌後面將緊接着22896條記錄,分佈在不同的若干seg node上。
分析
對問題的分析主要就是對那段sql語句分析,嘗試將這個sql簡化,得到自然語言描述的含義:
- 由於結果中b.relkind和b.relstorage都是None(等價於null),這意味下面拆解出來語句中最終被選中的都是c.relkind和c.relstorage爲null的記錄,因此c.relstorage != ‘h’這個條件並不會貢獻給整個大sql,所以這個子sql可以被簡化,簡化前後順序如下:
SELECT p.*, c.relkind, c.relstorage
FROM gp_persistent_relation_node_check() p
LEFT OUTER JOIN pg_class c
ON (p.relfilenode_oid = c.relfilenode)
WHERE (p.segment_file_num = 0 or c.relstorage != 'h')
SELECT p.*
FROM gp_persistent_relation_node_check() p
WHERE p.segment_file_num = 0
- 在此實際錯誤中,filesystem都是true並且persistent都是false,這意味着a.relfilenode_oid 永遠是null,b.relfilenode_oid永遠不是null,由於a和b是FULL OUTER JOIN,所以這意味着在此案例中,此sql得到是在b中有,而在a中沒有的記錄
- 根據上述兩條,這個sql在這個案例中查到的其實就是函數gp_persistent_relation_node_check()中segment_file_num=0的記錄裏,沒有在gp_persistent_relation_node中存在的部分,由於本案例中沒有append-optimized表,所以segment_file_num=0這個條件也可以忽略,(猜想後經官方證實)實際上這sql找到就是磁盤上有而system catalog中沒有記錄的數據文件,gp_persistent_relation_node_check()這個內置的c函數就是在掃描那些數據路徑:
select b.database_oid,b.relfilenode_oid,b.segment_file_num
from gp_persistent_relation_node_check() b
where b.relfilenode_oid not in (select relfilenode_oid from gp_persistent_relation_node);
解決
知道了這錯誤其實就是“存在磁盤上有而system catalog中沒有記錄的數據文件”,所以辦法很簡單,刪除掉它們就好了:
- 編寫一個在每個seg node上查找並刪除文件的腳本,其中872368是sql查到的relfilenode_oid,需要把所有的文件名都寫一行:
#!/bin/sh
hostname="$1"
directory="$2"
gpssh -h ${hostname} -v -e "find ${directory} -name 872368 >> /home/gpadmin/position.log"
......
- 用gpscp命令將這個腳本cp到所有的出錯的seg node所在的seg host上
gpscp -h mdw -h sdw1 -hsdwx single.sh =:/home/gpadmin
- 用gpssh在每個出錯seg host上爲每個出錯seg node創建一個暫存這些錯誤文件的文件夾,例如:
gpssh -h sdw10 'mkdir /home/gpadmin/20150514errordatafile/gpseg32 /home/gpadmin/20150514errordatafile/gpseg33 /home/gpadmin/20150514errordatafile/gpseg34 /home/gpadmin/20150514errordatafile/gpseg35'
- 編寫一個master上的腳本batch.sh調用所有出錯seg host上的single.sh,其中,mdw爲出錯的seg host,/data/master/gpseg-1爲錯誤信息中的seg node文件路徑,/home/gpadmin/20150514errordatafile/gpseg-1爲上一步中爲此seg node創建的暫存路徑,爲每個seg node要寫一行:
#!/bin/bash
gpssh -h mdw -v -e '/home/gpadmin/single.sh /data/master/gpseg-1 /home/gpadmin/20150514errordatafile/gpseg-1'
......
- 在master上執行上一步中batch.sh,done