如何解決Greenplum的gpcheckcat關於persistent的錯誤

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章