新しいメガ機能pg_filedumpでPostgreSQLテーブルを復元する例





Postgres Proの同僚が最近見たpg_filedumpユーティリティのすばらしい機能について教えてください。 この機能により、データベースがひどく破損しており、そのようなデータベースでPostgreSQLインスタンスを起動できない場合でも、データベースからデータを部分的に復元できます。 もちろん、私はそのような機能の必要性は非常にまれだと信じたいです。 ただし、念のため、似たようなものを手元に用意したいと思います。 続きを読むと、この機能が実際にどのように見えるかがわかります。



コミット52fa0201で部分的なデータ回復が導入されました



commit 52fa0201f97808d518c64bcb9696f2a350678aa5

Author: Teodor Sigaev <teodor@sigaev.ru>

Date: Tue Jan 17 16:01:12 2017 +0300



Partial data recovery (-D flag).



This feature allows to partially recover data from a given segment file

in format suitable for using in COPY FROM statement. List of supported

data types is currently not full and TOAST is not yet supported, but

it's better than nothing. Hopefully data recovery will be improved in

the future.



Implemented by Aleksander Alekseev, reviewed by Dmitry Ivanov, tested

by Dmitry Ivanov and Grigoriy Smolkin.








テーブルがあるとしましょう:



 create table tt (x int, y bool, z text, w timestamp);
      
      





...いくつかのデータで満たされています:



 insert into tt values(123, true, 'Text test test', now()); insert into tt values(456, null, ' ', null); checkpoint;
      
      





ここでは、データが必ずディスクに到達するようにチェックポイントを指定します。 それ以外の場合は、WALに移動しますが、バッファーマネージャーは、tapla(タプル、タプル、テーブル内の行)が新しいまたは頻繁に使用されるtaplaに置き換わるまで、メモリに保持します。 または、タイムアウト/累積max_walのチェックポイント。 これは、ページをディスクに同期するための最も一般的なシナリオだと思います。 -約 Stas Kelvich



また、テーブルに対応するセグメントの名前を見つけます。



 select relfilenode from pg_class where relname = 'tt';
      
      





私の場合、テーブルのrelfilenodeは16393でした。ディスク上にこのセグメント(テーブルが1 GBより大きい場合はセグメント)が見つかります。



 find /path/to/db/ -type f | grep 16393
      
      





どこかにコピーして、手元にあるセグメントファイルのみでデータを復元したいと想像します。



これを行うには、pg_filedumpの最新バージョンを収集します。



 git clone git://git.postgresql.org/git/pg_filedump.git cd pg_filedump make
      
      





通常、ベーススキームは既知のものです。それと連携するアプリケーションが手元にあるからです。 そのため、テーブル内の列のタイプがわかっており、次のようにデコードできます。



 ./pg_filedump -D int,bool,text,timestamp /path/to/db/base/16384/16393
      
      





出力例:



 ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility - Version 9.6.0 * * File: /home/eax/work/postgrespro/postgresql-install/data-master/base/16384/16393 * Options used: -D int,bool,text,timestamp * * Dump created on: Tue Jan 17 16:28:07 2017 ******************************************************************* Block 0 ******************************************************** <Header> ----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8080 (0x1f90) LSN: logid 0 recoff 0x0301e4c0 Special 8192 (0x2000) Items: 2 Free Space: 8048 Checksum: 0x0000 Prune XID: 0x00000000 Flags: 0x0000 () Length (including item array): 32 <Data> ------ Item 1 -- Length: 56 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: 123 t Text test test 2017-01-17 16:25:03.448488 Item 2 -- Length: 52 Offset: 8080 (0x1f90) Flags: NORMAL COPY: 456 \N   \N *** End of File Encountered. Last Block Read: 0 ***
      
      





pg_filedumpはセグメント内の各ページに関する情報を表示し、各タプラのタイトルをデコードするため、非常に多くのデータがあります。 幸いなことに、次のように、ハエをカツレツから簡単に分離できます。



 pg_fiedump -D ........ | grep COPY | perl -lne 's/^COPY: //g; print;' > /tmp/copy.txt cat /tmp/copy.txt
      
      





copy.txtファイルの内容:



 123 t Text test test 2017-01-17 16:25:03.448488 456 \N   \N
      
      





これは、COPY FROMクエリでの使用に適した形式のテーブルデータです。 私たちはチェックします:



 create table tt2 (x int, y bool, z text, w timestamp); copy tt2 from '/tmp/copy.txt'; select * from tt2;
      
      





結果:



  x | y | z | w -----+---+----------------+---------------------------- 123 | t | Text test test | 2017-01-17 16:25:03.448488 456 | |   | (2 rows)
      
      





ご覧のとおり、すべてのデータが正常に復元されました。



当然、これはやや単純化された例であり、実際にはすべてがより複雑です。 まず、サポートされているタイプのリストは現在多少制限されています。



 static ParseCallbackTableItem callback_table[] = { { "smallserial", &decode_smallint }, { "smallint", &decode_smallint }, { "int", &decode_int }, { "serial", &decode_int }, { "bigint", &decode_bigint }, { "bigserial", &decode_bigint }, { "time", &decode_time }, { "timetz", &decode_timetz }, { "date", &decode_date }, { "timestamp", &decode_timestamp }, { "float4", &decode_float4 }, { "float8", &decode_float8 }, { "float", &decode_float8 }, { "bool", &decode_bool }, { "uuid", &decode_uuid }, { "macaddr", &decode_macaddr }, /* internally all string types are stored the same way */ { "char", &decode_string }, { "varchar", &decode_string }, { "text", &decode_string }, { "json", &decode_string }, { "xml", &decode_string }, { NULL, NULL}, };
      
      





第二に、TOASTは現在サポートされていません。 文字列が非圧縮またはインプレースページに圧縮されて保存されている場合、pg_filedumpはそれを正常に復元します(圧縮データが破損していない場合)。 ただし、行が外部TOASTテーブルに転送された場合、行の代わりに「(TOASTED)」だけが表示されます。 原則として、TOASTサポートは解決不可能なタスクではありません。 ディレクトリを解析して適切なTOASTテーブルを見つけるようにpg_filedumpに教えるだけです。 まだ誰もこれを行っていないというだけです。 おそらく、TOASTサポートはpg_filedumpの将来のバージョンで追加されるでしょう。



最後に、実際には、データベーススキーマが変更されることがあり、テーブルの列が表示されたり消えたりします。 列を削除することはそれほど大きな問題ではありません。物理的にこの列はtaplaに残っているため、常にnullです。 そのため、同じテーブル内のtaplaには可変数の属性を設定できるため、追加はもう少し複雑です。 taplaの属性の数がユーザーが指定した属性の数と一致しない場合、pg_filedumpは単に部分的にデコードされたデータとともに警告を表示し、次のtaplaに進みます。 つまり、実際にはpg_filedump出力の解析はもう少し複雑になるか、異なる属性リストを使用して数回実行する必要があります。



私の意見では、データ回復の極端な手段として、私のものだけでなく、持っているよりも少なくとも1つ持っている方が良いです:)提示された機能のさらなる改善のためのアイデア、実際にコメントや追加がある場合、私は非常になりますコメントでそれらを読むのは面白いです!



次の記事にも興味があるかもしれません。









継続- 別の新機能pg_filedump:PostgreSQLディレクトリを復元します



All Articles