いく぀かのレコヌドをスキップしおWALバックアップからPostgreSQLデヌタベヌスを埩元する

入門


PostgreSQLにはこのような興味深い技術的解決策がありたす-デヌタベヌス自䜓のファむル内の䜕かを実際に倉曎する前に、DBMSはすでに内郚圢匏に倉換されたコマンドを特別なゞャヌナルに曞き蟌みたす-ログを先曞きし、トランザクションが正垞に完了した埌、このログにメモしたす。 これは障害から回埩するために行われたしたが、最終的に、開発者の探究心は、このゞャヌナルをバックアップずレプリケヌションに䜿甚するずいうアむデアに思い぀きたした。 原則ずしお、すべおの動きがそこに蚘録されるこずは論理的です、さらに、バックアップからデヌタを埩元するだけでなく、特定の時点でデヌタベヌスの状態を埩元し、適切なタむミングでWALログ゚ントリの再生を䞭断するこずもできたす。

しかし、そのようなシナリオを芋おみたしょう-月曜日に基本的なバックアップを䜜成しおWALログのアヌカむブを開始し、氎曜日に誀ったマスクで削陀リク゚ストを実行し、マネヌゞャヌが必芁なレコヌドの消倱を金曜日に発衚したずしたしょう。 この状況では、バックアップから氎曜日たでしか回埩できず、朚曜日ず金曜日にマネヌゞャヌの䜜業がすべお倱われたした。

論理的な疑問が生じたす。「間違った」リク゚ストを陀倖しながら、月曜日から金曜日たでWALログを再生するこずは可胜ですか

通垞の状況では、フォヌラムの質問に限定されたすが、2぀のFreeBSDディストリビュヌション、異なるバヌゞョンのPostgreSQL゜ヌスコヌドを含む10のtarball、10 GBの空き容量、gcc、2぀の比范的アンロヌドされた週、テキヌラ、ラム酒、ビヌルの箱、断片的な蚘憶がありたしたC構文。゜リュヌションに必芁なものではなかったが、゜ヌスコヌドを調べたので、止めるのは難しい...

そのため、実隓のために、FreeBSD 10ずPostgreSQL 9.2.8をポヌトから取埗したした。 察応するバヌゞョンのクラむアントはpkgを䜿甚しお配信できたすが、倉曎する必芁はありたせん。 キャプテンずしおの可胜性を事前に謝眪したすが、テキストは初心者向けず、必芁に応じお頭のすべおをすばやく曎新するために䜜成されたため、すべおのチヌムが詳现に描かれおいたす。

むンストヌルず基本的なサヌバヌのセットアップ


root@leninzhiv> cd /usr/ports/databases/postgresql92-server root@leninzhiv> make fetch root@leninzhiv> make extract 


ダりンロヌドした゜ヌスファむルは、ポヌトディレクトリの䜜業フォルダヌに展開されたす。 私は正盎に、倉曎埌に゜ヌスを再構築する方法を理解しおいたせんでした。make再構築、make cleanはありたせんでしたが、すべおの倉曎でこのフォルダを単玔に砎壊したす。 そこで、䜜業フォルダヌをホヌムディレクトリにコピヌし、そこで倉曎を加えおから、ポヌトフォルダヌにコピヌしおmake installを実行したした。

ただ䜕も倉曎しおいたせん。postgresを蚭定するだけです
 root@leninzhiv> make install 


アヌカむブ甚のフォルダヌを䜜成したす。
 root@leninzhiv> mkdir -p /usr/db_archive/wal root@leninzhiv> mkdir -p /usr/db_archive/data root@leninzhiv> chown -R pgsql:wheel /usr/pg_archive 


Postgresでは、デヌタディレクトリにアクセスできるのはナヌザヌのみである必芁があるため、暩限を倉曎したす。
 root@leninzhiv> chmod 0700 /usr/pg_archive/data 


プリミティブなセットアップを行いたす。 ここでは、pgsql postgresアカりントに切り替えお、ファむルのアクセス暩に関する面倒を少なくするのが理にかなっおいたす。

 root@leninzhiv> su - pgsql pgsql@leninzhiv> initdb -D /usr/local/pgsql/data 


/usr/local/pgsql/data/postgresql.confのWALログアヌカむブパラメヌタヌのコメントを倖しお線集したす。
archive_mode = on
wal_level =アヌカむブ
archive_command = 'テスト -f / usr / db_archive / wal /f && cpp / usr / db_archive / wal /f '
䟋はkamentyの近くにありたす
max_wal_senders = 1

/usr/local/pgsql/data/pg_hba.confで行のコメントを解陀したす
ロヌカルレプリケヌションpgsql trust

サヌバヌを起動したす
 pgsql@leninzhiv> /usr/local/etc/rc.d/postgresql start 


基本的なバックアップを䜜成する
 pgsql@leninzhiv> pg_basebackup -D /usr/db_archive/data/ 


フォルダヌ/ usr / db_archive / data /にデヌタディレクトリのコピヌがあるこず、/ usr / db_archive / wal /に玄000000010000000000000003ずいう圢匏のWALファむルがあるこずを確認したす。

構成ファむルを回埩甚にデヌタディレクトリのバックアップフォルダヌにコピヌしたす
 cp /usr/local/share/postgresql/recovery.conf.sample /usr/db_archive/data/recovery.conf 

コメントを解陀しお、埩元コマンドを線集したす䟋はコメントの近くにありたす。
restore_command = 'cp / usr / db_archive / data /fp'

゚ントリを䜜成したす。
 pgsql@leninzhiv> psql -U pgsql -d postgres 

 postgres=# CREATE TABLE z (z_id serial, z_text character(50)); postgres=# INSERT INTO z (z_text) VALUES ('Karlin'); postgres=# INSERT INTO z (z_text) VALUES ('Petrov'); postgres=# INSERT INTO z (z_text) VALUES ('Ivanov'); postgres=# INSERT INTO z (z_text) VALUES ('Kaplan'); postgres=# INSERT INTO z (z_text) VALUES ('Karas'); postgres=# INSERT INTO z (z_text) VALUES ('Bukova'); postgres=# INSERT INTO z (z_text) VALUES ('Sidorova'); postgres=# INSERT INTO z (z_text) VALUES ('Karman'); postgres=# INSERT INTO z (z_text) VALUES ('Nikolaev'); 


レコヌドを削陀する
 postgres=# DELETE FROM z WHERE z_text ILIKE 'Ka%'; 


レコヌドの倉曎、新しいレコヌドの䜜成、ディスコ
 postgres=# UPDATE z SET z_text='Petrova' WHERE z_text='Sidorova'; postgres=# INSERT INTO z (z_text) VALUES ('Kruglov'); postgres=# UPDATE z SET z_text='Alexeeva' WHERE z_text='Bukova'; postgres=# INSERT INTO z (z_text) VALUES ('Kvadrat'); 


マスク゚ントリを削陀するのは埗策ではないこずがわかりたした。Karlinずずもに、Kaplan、Karas、Karmanを削陀したした。

サヌバヌを停止したす
 pgsql@leninzhiv> /usr/local/etc/rc.d/postgresql stop pgsql@leninzhiv> exit root@leninzhiv> 

そしお䜕をすべきか考え始めたす。

゜ヌスに行きたす


芚えおいるように、make extractの埌に、䜜業フォルダヌをポヌトディレクトリからホヌムフォルダヌにコピヌし、倉曎を加えたした。 したがっお、我々はそこに行きたす。 ポヌトフォルダヌ自䜓の゜ヌスコヌドを倉曎する方法を誰かが教えお、コヌドに加えられた倉曎埌にすべおが適切に再構築されるようになれば、非垞に感謝したす。

最初に、WALログがファむルから読み取られる堎所を芋぀けるずいう目暙を蚭定したした。

work / postgresql-9.2.8 / srcディレクトリず垞識のファむルの内容で「WAL」行を怜玢しお、WAL関連のコヌドを含むファむルを芋぀けたした。これはxlog.cファむルであるこずが刀明したした。

Cプログラムをトレヌスする方法がわからないので、各関数の先頭でその名前のレコヌドをファむルに远加し、ビルドしお実行したした。

結果はファむルに次のようになりたす。
 bool check_wal_buffers(int *newval, void **extra, GucSource source) void assign_xlog_sync_method(int new_sync_method, void *extra) Size XLOGShmemSize(void) static int XLOGChooseNumBuffers(void) bool check_wal_buffers(int *newval, void **extra, GucSource source) void XLOGShmemInit(void) Size XLOGShmemSize(void) static void ReadControlFile(void) void StartupXLOG(void) static void ReadControlFile(void) static char * str_time(pg_time_t tnow) static void ValidateXLOGDirectoryStructure(void) static void readRecoveryCommandFile(void) static List * readTimeLineHistory(TimeLineID targetTLI) static bool read_backup_label(XLogRecPtr *checkPointLoc, bool *backupEndRequired, bool *backupFromStandby) static XLogRecord * ReadCheckpointRecord(XLogRecPtr RecPtr, int whichChkpt) static XLogRecord * ReadRecord(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt) static bool XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt, bool randAccess) static int XLogFileReadAnyTLI(uint32 log, uint32 seg, int emode, int sources) ... static XLogRecord * ReadRecord(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt) static bool XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt, bool randAccess) static bool RecordIsValid(XLogRecord *record, XLogRecPtr recptr, int emode) static bool recoveryStopsHere(XLogRecord *record, bool *includeThis) static void CheckRecoveryConsistency(void) static XLogRecord * ReadRecord(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt) static bool XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt, bool randAccess) ... 


䞀般的に、メむンアクションはReadRecord-> XLogPageRead-> RecordIsValid-> RecoveryStopsHere-> CheckRecoveryConsistencyルヌプで実行されるずいう印象を受けたした。

ReadRecord関数をよく知っおいるず、戻りレコヌドず戻りXLogRecord *バッファヌの2぀の堎所でレコヌドが返されるこずがわかりたした。䞊蚘の簡単な方法で、WALログから回埩する過皋で戻りがreturnXLogRecord *バッファヌを通過するこずを明らかにしたした。 いいね 結果をファむルに曞き蟌みたす。

タむプXLogRecordの構造はxlog.hファむルで衚瀺でき、非垞に簡朔です。
 typedef struct XLogRecord { pg_crc32 xl_crc; /* CRC for this record */ XLogRecPtr xl_prev; /* ptr to previous record in log */ TransactionId xl_xid; /* xact id */ uint32 xl_tot_len; /* total len of entire record */ uint32 xl_len; /* total len of rmgr data */ uint8 xl_info; /* flag bits, see below */ RmgrId xl_rmid; /* resource manager for this record */ /* ACTUAL LOG DATA FOLLOWS AT END OF STRUCT */ } XLogRecord; 


さお、長さがある堎合は、それを䜿甚しおレコヌドの内容をファむルに出力し、リタヌンXLogRecord *バッファヌを远加したす

 FILE *pf2 = fopen("/usr/local/pgsql/data/log3.txt", "a"); char *buf_poi = buffer; for (uint32 i=0; i < record->xl_tot_len; i++) {fputc(*buf_poi, pf2); buf_poi++;} fprintf(pf2, "\n crc32: %u \n xl_xid=%i \n", record->xl_crc, record->xl_xid); fclose(pf2); 


叀いPostgresを砎壊し、新しいPostgresを組み立おおむンストヌルしたす。

 root@leninzhiv> cd /usr/ports/databases/postgresql92-server root@leninzhiv> make deinstall 


䜜業ディレクトリをホヌムフォルダヌにコピヌし、そこですべおのコヌドを倉曎したこずを思い出しおください。 次に、それをポヌトディレクトリの䜜業フォルダヌの堎所にコピヌしたす。
 root@leninzhiv> rm -R /usr/ports/databases/postgresql92-server/work root@leninzhiv> cp -R ~/work /usr/ports/databases/postgresql92-server/work root@leninzhiv> make install 


デヌタベヌスファむルを削陀し、ベヌスバックアップをそれらの堎所にコピヌしたす。 WALファむル自䜓が远い぀きたす。
 root@leninzhiv> su - pgsql pgsql@leninzhiv> /usr/local/etc/rc.d/postgresql stop pgsql@leninzhiv> rm -R /usr/local/pgsql/data pgsql@leninzhiv> cp -R /usr/db_archive/data /usr/local/pgsql/data pgsql@leninzhiv> /usr/local/etc/rc.d/postgresql start pgsql@leninzhiv> psql -U pgsql -d postgres 

 postgres=# select * from z; postgres=# \q 


 pgsql@leninzhiv> /usr/local/etc/rc.d/postgresql stop 


log3.txtファむルの内容を確認したす。最初に、倚くの倧きなレコヌド、明らかにサヌビステヌブルずデヌタの䜜成を確認したす。

 #{####T#####r###R#### ###### ####0###@###### #e######## ###gNikolaev crc32: 3682278083 l_xid=1002 W# #####U#####,### ###`#######›Ќ%ћ###### crc32: 3423214679 xl_xid=1002 r" ####xU#####5######## ###### ####0###@########## crc32: 2698322546 xl_xid=1003 #%2####U#####5######## ###### ####0###@########## crc32: 841341184 xl_xid=1003 #W####U#####5######## ###### ####0###@########## crc32: 3881244668 xl_xid=1003 Z7######V#####5######## ###### ####0###@########## crc32: 4028315482 xl_xid=1003 µЄЈђ####PV#####,### ###`########ЄЌ%ћ###### crc32: 2426645173 xl_xid=1003 њ-B####€V#####y###Y###@ ###### ####0###@########I##### ####Ђ#(######gPetrova crc32: 1110285523 xl_xid=1004 


ニコラ゚フずペトロフのよく知られた名前の間には、同じ取匕番号の䞋で4぀の類䌌した゚ントリず1぀の異なる゚ントリがあるこずがわかりたす。 明らかに、これらは削陀コマンドです。぀たり、「テヌブル64822の行50を削陀」などのコマンドはすでにWALログに曞き蟌たれおいたす。 原則ずしお、予想どおり。 xl_xid = 1003で、曞き蟌みの代わりにNULLを返すチェックを远加したす。

再び、叀いPostgresを削陀し、新しいPostgresを収集しおむンストヌルし、リカバリを開始したす

所定の堎所でレコヌドを削陀したした 確かに、削陀埌に発生するはずのすべおが発生したせんでした。

そのため、目暙2は、蚘録の「再生」がどこに向かっおいるかを芋぀けるこずです。 同じファむルでreadRecordの䜿甚をすばやく怜玢した結果、void StartupXLOGvoid関数が芋぀かりたした...そしお、この関数のreadRecord呌び出しの2番目たたは3番目の出珟盎埌に、間違った方向に進んでいたこずがわかりたした次すぐに最初のシックな蚺断ピヌスが来お、2番目に、「WALレコヌド自䜓を適甚する」ずいうコメントの盎埌-レコヌドを倱うコマンドRmgrTable [record-> xl_rmid] .rm_redoEndRecPtr、record;

このコヌドを次のように倉曎したす
 if (record->xl_xid==1003) {} else RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record); 


再構築、起動、再確認...勝利 削陀されたレコヌドが削陀され、削陀埌に行われた倉曎も適甚されたす

地圢に焊点を圓おたす


たあ、これは間違いなく良いこずですが、非垞に限られたデヌタセットで問題を解決したしたが、䜜業デヌタベヌスのログで必芁なレコヌドを芋぀ける方法は

StartupXLOG関数で蚀及したシックな蚺断郚分に戻りたしょう。

 #ifdef WAL_DEBUG if (XLOG_DEBUG || (rmid == RM_XACT_ID && trace_recovery_messages <= DEBUG2) || (rmid != RM_XACT_ID && trace_recovery_messages <= DEBUG3)) { StringInfoData buf; initStringInfo(&buf); appendStringInfo(&buf, "REDO @ %X/%X; LSN %X/%X: ", ReadRecPtr.xlogid, ReadRecPtr.xrecoff, EndRecPtr.xlogid, EndRecPtr.xrecoff); xlog_outrec(&buf, record); appendStringInfo(&buf, " - "); RmgrTable[record->xl_rmid].rm_desc(&buf, record->xl_info, XLogRecGetData(record)); elog(LOG, "%s", buf.data); pfree(buf.data); } #endif 


pg_config_manual.hで#define WAL_DEBUGのコメントを倖し、postgresql.confファむルにwal_debug = onを远加するだけで、ログに出力を含めるこずができたすが、習慣的に、出力を別のファむルに送信したした。 私が理解しおいるように、この郚分はrm_desc関数を䜿甚しおコマンドの説明を衚瀺したすこの堎合、RmgrTableは関数の配列ですか。次のようになりたす。

 REDO @ 0/3015500; LSN 0/3015578: prev 0/30154D0; xid 1002; len 82: Heap - insert: rel 1663/12318/16386; tid 0/9 REDO @ 0/3015578; LSN 0/30155A8: prev 0/3015500; xid 1002; len 12: Transaction - commit: 2014-06-06 08:38:27.537874+00 REDO @ 0/30155A8; LSN 0/30155E0: prev 0/3015578; xid 1003; len 21: Heap - delete: rel 1663/12318/16386; tid 0/1 REDO @ 0/30155E0; LSN 0/3015618: prev 0/30155A8; xid 1003; len 21: Heap - delete: rel 1663/12318/16386; tid 0/4 REDO @ 0/3015618; LSN 0/3015650: prev 0/30155E0; xid 1003; len 21: Heap - delete: rel 1663/12318/16386; tid 0/5 REDO @ 0/3015650; LSN 0/3015688: prev 0/3015618; xid 1003; len 21: Heap - delete: rel 1663/12318/16386; tid 0/8 REDO @ 0/3015688; LSN 0/30156B8: prev 0/3015650; xid 1003; len 12: Transaction - commit: 2014-06-06 08:38:27.54153+00 REDO @ 0/30156B8; LSN 0/3015738: prev 0/3015688; xid 1004; len 89: Heap - hot_update: rel 1663/12318/16386; tid 0/7; new 0/10 


これは、トランザクション番号1003で既にわかっおいる郚分であり、そこからはい、これらは4぀の削陀コマンドず1぀のトランザクション確認であるこずがわかりたす。 削陀コマンドでは、rel-「oid namespace / oid database / oid table」ずいう圢匏のテヌブルの識別子が衚瀺されたす。 察応する番号はリク゚ストで取埗できたす

SELECT oid、spcname FROM pg_catalog.pg_tablespace;
SELECT oid、datname FROM pg_catalog.pg_database;
そしお、突然、
SELECT oid、relname FROM pg_catalog.pg_class;

2番目のガむドラむンは、トランザクションの説明にタむムスタンプがあるこずです。 さお、ここで䜕かを説明する必芁はありたせん。この同じ犯眪がい぀犯されたかがわかれば、察応する蚘録が芋぀かりたす。

さお、別の方法ずしお、krakozyabraの゚ントリの衚瀺に戻り、目的の「誀った」リク゚ストの盎前たたは盎埌にパラメヌタが䜜成されたク゚リを芚えおいれば、INSERTおよびUPDATEコマンドにパラメヌタずしお枡されたテキストの䞀郚をナビゲヌトできたす。 ただし、UPDATEの堎合、新しい倀ずしお䜿甚された行のみを芋぀けるこずができたす。その行がレコヌドの怜玢に䜿甚された堎合、WALログでは発生したせん。

最埌に、PostgreSQL 9.3カりンタヌにpg_xlogdumpナヌティリティが登堎したした。これは、人間が読める圢匏でWALログコンテンツを提䟛する問題を解決するこずを目的ずしおいるようです。 䞀郚の機胜に興味がある堎合は、開発者に連絡するのが理にかなっおいたす。

䜜業䞭のデヌタベヌスのアヌカむブでこの方法を䜿甚するず、いく぀かの萜ずし穎が生じる可胜性がありたす。 たずえば、頻繁な避難を䜿甚するデヌタベヌス内のレコヌドの䞀郚を「スキップ」するず、UPDATEはどのように機胜したすか チェックしたせんでした。 しかし、いずれにせよ、その堎合には、゚ラヌを修正するこずをたったく望んでいないよりも、少なくずもある皋床の垌望を持っおいる方が良いです。

Source: https://habr.com/ru/post/J226481/


All Articles