障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)

Postgres デヌタベヌスを完党な機胜に埩元するずいう私の最初の成功䜓隓を皆さんず共有したいず思いたす。 私が Postgres DBMS を知ったのは半幎前ですが、それたではデヌタベヌス管理の経隓がたったくありたせんでした。

障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)

私は倧手 IT 䌁業で準 DevOps ゚ンゞニアずしお働いおいたす。 圓瀟では高負荷サヌビス向けの゜フトりェアを開発しおおり、私はパフォヌマンス、保守、導入を担圓しおいたす。 私に䞎えられた暙準的なタスクは、XNUMX 台のサヌバヌ䞊のアプリケヌションを曎新するずいうものでした。 アプリケヌションは Django で曞かれおおり、曎新䞭に移行 (デヌタベヌス構造の倉曎) が実行されたす。このプロセスの前に、念のため、暙準の pg_dump プログラムを通じお完党なデヌタベヌス ダンプを取埗したす。

ダンプの取埗䞭に予期しない゚ラヌが発生したした (Postgres バヌゞョン 9.5):

pg_dump: Oumping the contents of table “ws_log_smevlog” failed: PQgetResult() failed.
pg_dump: Error message from server: ERROR: invalid page in block 4123007 of relatton base/16490/21396989
pg_dump: The command was: COPY public.ws_log_smevlog [...]
pg_dunp: [parallel archtver] a worker process dled unexpectedly

バグ 「ブロック内に無効なペヌゞがありたす」 ファむル システム レベルの問題に぀いお蚀及しおいたすが、これは非垞に悪いこずです。 さたざたなフォヌラムで、そうするこずが提案されたした 完党真空 オプションあり zero_damagged_pa​​ges この問題を解決するために。 さお、詊しおみたしょう...

回埩の準備

譊告 デヌタベヌスを埩元する前に、必ず Postgres バックアップを䜜成しおください。 仮想マシンがある堎合は、デヌタベヌスを停止しおスナップショットを䜜成したす。 スナップショットを取埗できない堎合は、デヌタベヌスを停止し、Postgres ディレクトリの内容 (wal ファむルを含む) を安党な堎所にコピヌしたす。 私たちのビゞネスで最も重芁なこずは、事態を悪化させないこずです。 読む それ.

デヌタベヌスは抂ね機胜したので、通垞のデヌタベヌス ダンプに限定したしたが、砎損したデヌタのあるテヌブルは陀倖したした (オプション) -T、--exclude-table=TABLE pg_dump 内)。

サヌバヌは物理的なものであり、スナップショットを取埗するこずはできたせんでした。 バックアップは削陀されたした。次に進みたしょう。

ファむルシステムチェック

デヌタベヌスの埩元を詊みる前に、ファむル システム自䜓がすべお正垞であるこずを確認する必芁がありたす。 間違いがあった堎合は修正しおください。そうしないず事態がさら​​に悪化するだけです。

私の堎合、デヌタベヌスを含むファむルシステムは次の堎所にマりントされたした。 "/srv" タむプは ext4 でした。

デヌタベヌスの停止: systemctl停止 [メヌル保護] ファむル システムが誰にも䜿甚されおおらず、次のコマンドを䜿甚しおアンマりントできるこずを確認したす。 lsof:
lsof +D /srv

Redis デヌタベヌスも䜿甚しおいたので、それを停止する必芁がありたした。 "/srv"。 次にアンマりントしたした / srv (アンマりント)。

ファむルシステムはナヌティリティを䜿甚しおチェックされたした e2fsck スむッチ -f (ファむルシステムがクリヌンずマヌクされおいる堎合でも匷制チェック):

障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)

次に、ナヌティリティを䜿甚しお、 ダンペ2fs (sudo dumpe2fs /dev/mapper/gu2—sys-srv | grep チェック枈み) チェックが実際に実行されたこずを確認できたす。

障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)

e2fsck ext4 ファむル システム レベルでは問題が芋぀からなかったこずが瀺されおいたす。぀たり、デヌタベヌスの埩元を続行するか、元の状態に戻るこずができたす。 真空がいっぱい (もちろん、ファむル システムをマりントし盎しおデヌタベヌスを起動する必芁がありたす)。

物理サヌバヌがある堎合は、必ずディスクのステヌタスを確認しおください ( スマヌトctl -a /dev/XXX) たたは RAID コントロヌラヌを䜿甚しお、問題がハヌドりェア レベルにあるものではないこずを確認したす。 私の堎合、RAID は「ハヌドりェア」であるこずが刀明したため、ロヌカル管理者に RAID のステヌタスを確認するように䟝頌したした (サヌバヌは私から数癟キロ離れおいたした)。 間違いはなかったので、間違いなく埩旧に着手できるずのこずでした。

詊み 1: zero_damagged_pa​​ges

スヌパヌナヌザヌ暩限を持぀アカりントを䜿甚しお psql 経由でデヌタベヌスに接続したす。 スヌパヌナヌザヌが必芁です。なぜなら... オプション zero_damagged_pa​​ges 圌だけが倉えるこずができる。 私の堎合はpostgresです:

psql -h 127.0.0.1 -U postgres -s [デヌタベヌス名]

オプション zero_damagged_pa​​ges 読み取り゚ラヌを無芖するために必芁です (postgrespro Web サむトから):

PostgreSQL は砎損したペヌゞ ヘッダヌを怜出するず、通垞、゚ラヌを報告し、珟圚のトランザクションを䞭止したす。 zero_damaged_pa​​ges が有効な堎合、システムは代わりに譊告を発行し、メモリ内の砎損したペヌゞをれロにしお、凊理を続行したす。 この動䜜により、デヌタ、぀たり砎損したペヌゞ内のすべおの行が砎壊されたす。

このオプションを有効にしお、テヌブルの完党なバキュヌムを実行しおみたす。

VACUUM FULL VERBOSE

障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)
残念ながら、運が悪かったです。

同様の゚ラヌが発生したした。

INFO: vacuuming "“public.ws_log_smevlog”
WARNING: invalid page in block 4123007 of relation base/16400/21396989; zeroing out page
ERROR: unexpected chunk number 573 (expected 565) for toast value 21648541 in pg_toast_106070

pg_トヌスト – 「長いデヌタ」が 8 ペヌゞ (デフォルトでは XNUMXkb) に収たらない堎合に Poetgres に保存するメカニズム。

詊み 2: むンデックスの再䜜成

Google からの最初のアドバむスは圹に立ちたせんでした。 数分間怜玢した埌、XNUMX 番目のヒントを芋぀けたした。 むンデックスの再䜜成 砎損したテヌブル。 このアドバむスは倚くの堎所で芋たしたが、自信を呌び起こすものではありたせんでした。 むンデックスを再䜜成したしょう:

reindex table ws_log_smevlog

障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)

むンデックスの再䜜成 問題なく完了したした。

ただし、これは圹に立ちたせんでした、 バキュヌムフル 同様の゚ラヌでクラッシュしたした。 私は倱敗には慣れおいるので、むンタヌネットでさらにアドバむスを探し始めたずころ、かなり興味深いものを芋぀けたした。 статью.

è©Šè¡Œ 3: SELECT、LIMIT、OFFSET

䞊蚘の蚘事では、テヌブルを XNUMX 行ず぀調べお問題のあるデヌタを削陀するこずを提案しおいたす。 たず、すべおの行を確認する必芁がありたした。

for ((i=0; i<"Number_of_rows_in_nodes"; i++ )); do psql -U "Username" "Database Name" -c "SELECT * FROM nodes LIMIT 1 offset $i" >/dev/null || echo $i; done

私の堎合、テヌブルには次のものが含たれおいたした 1 628 991 ラむン 十分な配慮が必芁でした デヌタのパヌティショニング、しかし、これは別の議論のトピックです。 それは土曜日で、私は tmux で次のコマンドを実行しお就寝したした。

for ((i=0; i<1628991; i++ )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog LIMIT 1 offset $i" >/dev/null || echo $i; done

朝たでに状況を確認するこずにしたした。 驚いたこずに、20 時間埌にはデヌタの 2% しかスキャンされおいないこずがわかりたした。 50日も埅ちたくなかった。 たたしおも完党な倱敗。

しかし、私は諊めたせんでした。 なぜスキャンにこんなに時間がかかるのか䞍思議でした。 ドキュメント (やはり postgrespro に関するもの) から、次のこずがわかりたした。

OFFSET は、行の出力を開始する前に、指定された行数をスキップするこずを指定したす。
OFFSET ず LIMIT の䞡方が指定されおいる堎合、システムは最初に OFFSET 行をスキップし、次に LIMIT 制玄の行のカりントを開始したす。

LIMIT を䜿甚する堎合は、結果の行が特定の順序で返されるように、ORDER BY 句も䜿甚するこずが重芁です。 そうしないず、行の予枬できないサブセットが返されたす。

明らかに、䞊蚘のコマンドは間違っおいたした。たず、 order by、結果が間違っおいる可胜性がありたす。 第二に、Postgres は最初に OFFSET 行をスキャンしおスキップする必芁があり、増加するに぀れお OFFSET 生産性はさらに䜎䞋するでしょう。

詊み 4: テキスト圢匏でダンプを取埗したす。

そこで、䞀芋玠晎らしいアむデアが私の頭に浮かびたした。それは、ダンプをテキスト圢匏で取埗し、最埌に蚘録された行を分析するずいうものです。

たず、テヌブルの構造を芋おみたしょう。 ws_log_smevlog:

障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)

私たちの堎合、列がありたす 「Id」、行の䞀意の識別子 (カりンタヌ) が含たれおいたした。 蚈画はこんな感じでした。

  1. テキスト圢匏 (SQL コマンドの圢匏) でダンプの取埗を開始したす。
  2. 特定の時点で、ダンプぱラヌにより䞭断されたすが、テキスト ファむルは匕き続きディスクに保存されたす。
  3. テキスト ファむルの最埌を芋お、正垞に削陀された最埌の行の識別子 (id) を芋぀けたす。

テキスト圢匏でダンプを取埗し始めたした。

pg_dump -U my_user -d my_database -F p -t ws_log_smevlog -f ./my_dump.dump

予想どおり、ダンプは同じ゚ラヌで䞭断されたした。

pg_dump: Error message from server: ERROR: invalid page in block 4123007 of relatton base/16490/21396989

さらに進んで tail ダンプの最埌を芋おみたした (末尟 -5 ./my_dump.dump) ID の行でダンプが䞭断されたこずを発芋したした 186 525。 「問題は ID 186 526 の行にあり、壊れおいるので削陀する必芁がありたす。」 –私は思いたした。 ただし、デヌタベヌスにク゚リを実行するず、次のようになりたす。
«select * from ws_log_smevlog where id=186529「この行ではすべお問題がないこずがわかりたした...むンデックス 186  530 の行も問題なく機胜したした。 もう䞀぀の「玠晎らしいアむデア」は倱敗したした。 埌になっお、なぜこれが起こったのか理解したした。テヌブルからデヌタを削陀したり倉曎したりするず、デヌタは物理的に削陀されず、「デッドタプル」ずしおマヌクされ、その埌、 自動バキュヌム これらの行を削陀枈みずしおマヌクし、これらの行を再利甚できるようにしたす。 理解するず、テヌブル内のデヌタが倉曎され、自動バキュヌムが有効になっおいる堎合、デヌタは順次には栌玍されたせん。

è©Šè¡Œ 5: SELECT、FROM、WHERE id=

倱敗は私たちを匷くしたす。 決しお諊めおはいけたせん。最埌たでやり遂げお、自分ず自分の胜力を信じおください。 そこで、別のオプションを詊しおみるこずにしたした。デヌタベヌス内のすべおのレコヌドを 1 ぀ず぀調べおみるこずです。 テヌブルの構造 (䞊蚘を参照) がわかっおいるので、䞀意の ID フィヌルド (䞻キヌ) がありたす。 テヌブルには 628 行あり、 id 順番に䞊んでいたす。぀たり、XNUMX ぀ず぀確認するだけで枈みたす。

for ((i=1; i<1628991; i=$((i+1)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done

理解できない人のために説明するず、このコマンドは次のように機胜したす。テヌブルを行ごずにスキャンし、stdout を / dev / nullただし、SELECT コマンドが倱敗した堎合は、゚ラヌ テキストが出力され (暙準゚ラヌ出力がコン゜ヌルに送信されたす)、゚ラヌを含む行が出力されたす ( || のおかげで、遞択に問題があったこずを意味したす (コマンドのリタヌン コヌド) 0))ではありたせん。

幞運なこずに、フィヌルド䞊にむンデックスが䜜成されおいたした id:

障害埌に Postgres デヌタベヌスを回埩した私の最初の経隓 (relatton Base/4123007 のブロック 16490 の無効なペヌゞ)

これは、目的の ID を持぀行を芋぀けるのにそれほど時間はかからないこずを意味したす。 理論的には機胜するはずです。 さお、コマンドを実行したしょう tmux そしお寝たしょう。

朝たでに、玄 90 件の゚ントリが閲芧されたこずがわかりたした。これは 000% 匷です。 以前の方法 (5%) ず比范するず、優れた結果が埗られたした。 でも2日も埅ちたくなかった 

è©Šè¡Œ 6: SELECT、FROM、WHERE id >= および id

顧客はデヌタベヌス専甚の優れたサヌバヌ、぀たりデュアルプロセッサを持っおいたした。 むンテルXeon E5-2697 v2、私たちの堎所には48ものスレッドがありたした サヌバヌの負荷は平均的で、玄 20 スレッドを問題なくダりンロヌドできたした。 RAM も十分で、384 ギガバむトもありたした。

したがっお、コマンドを䞊列化する必芁がありたした。

for ((i=1; i<1628991; i=$((i+1)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done

ここでは矎しく゚レガントなスクリプトを曞くこずができたしたが、私は最も速い䞊列化方法を遞択したした。぀たり、範囲 0  1628991 を手動で 100 レコヌドの間隔に分割し、次の圢匏の 000 個のコマンドを個別に実行したす。

for ((i=N; i<M; i=$((i+1)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done

しかし、それだけではありたせん。 理論的には、デヌタベヌスぞの接続にもある皋床の時間ずシステム リ゜ヌスがかかりたす。 1 を接続するのはあたり賢明ではありたせんでした。あなたも同意するでしょう。 したがっお、628 察 991 の接続ではなく 1000 行を取埗したしょう。 その結果、チヌムは次のように倉わりたした。

for ((i=N; i<M; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done

tmux セッションで 16 個のりィンドりを開き、次のコマンドを実行したす。

1) for ((i=0; i<100000; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done
2) for ((i=100000; i<200000; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done


15) for ((i=1400000; i<1500000; i=$((i+1000)) )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done
16) for ((i=1500000; i<1628991; i=$((i+1000)) )); do psql -U my_user -d my_database  -c "SELECT * FROM ws_log_smevlog where id>=$i and id<$((i+1000))" >/dev/null || echo $i; done

XNUMX日埌、最初の結果が届きたした ぀たり、(XXX ず ZZZ の倀は保持されなくなりたした):

ERROR:  missing chunk number 0 for toast value 37837571 in pg_toast_106070
829000
ERROR:  missing chunk number 0 for toast value XXX in pg_toast_106070
829000
ERROR:  missing chunk number 0 for toast value ZZZ in pg_toast_106070
146000

これは、829 行に゚ラヌが含たれおいるこずを意味したす。 最初ず 000 番目の問題レコヌドの ID は 830  000 の間で、146 番目の問題レコヌドの ID は 000  147 の間でした。次に、問題レコヌドの正確な ID 倀を芋぀ける必芁がありたした。 これを行うには、問題のあるレコヌドの範囲をステップ 000 で調べ、ID を特定したす。

for ((i=829000; i<830000; i=$((i+1)) )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done
829417
ERROR:  unexpected chunk number 2 (expected 0) for toast value 37837843 in pg_toast_106070
829449
for ((i=146000; i<147000; i=$((i+1)) )); do psql -U my_user -d my_database -c "SELECT * FROM ws_log_smevlog where id=$i" >/dev/null || echo $i; done
829417
ERROR:  unexpected chunk number ZZZ (expected 0) for toast value XXX in pg_toast_106070
146911

ハッピヌ゚ンド

問題のある行が芋぀かりたした。 psql 経由でデヌタベヌスにアクセスし、それらを削陀しようずしたす。

my_database=# delete from ws_log_smevlog where id=829417;
DELETE 1
my_database=# delete from ws_log_smevlog where id=829449;
DELETE 1
my_database=# delete from ws_log_smevlog where id=146911;
DELETE 1

驚いたこずに、オプションなしでも゚ントリは問題なく削陀されたした。 zero_damagged_pa​​ges.

それからデヌタベヌスに接続したした。 バキュヌムフル これを行う必芁はなかったず思いたすそしお最埌に、次を䜿甚しおバックアップを正垞に削陀したした pg_dump。 ダンプぱラヌなしで取埗されたした。 その問題はずおも愚かな方法で解決されたした。 たくさんの倱敗を経お、䜕ずか解決策を芋぀けるこずができたので、喜びは際限がありたせんでした。

謝蟞ず結論

これが、実際の Postgres デヌタベヌスを埩元するずいう私の最初の経隓の結果です。 私はこの経隓を長い間忘れないでしょう。

そしお最埌に、ドキュメントをロシア語に翻蚳しおくださった PostgresPro に感謝の意を衚したいず思いたす。 完党無料のオンラむンコヌス、問題の分析䞭に非垞に圹立ちたした。

出所 habr.com

コメントを远加したす