#author("2019-11-06T12:46:54+00:00","default:haikikyou","haikikyou")
#author("2019-11-24T14:14:42+00:00","default:haikikyou","haikikyou")
[[PostgreSQL/解析]]
#contents


* WAL(Write Ahead Logging)[#v52df659]

- Write Ahead Loggingの略で、トランザクションログのことである。
- データベースでは、一般にデータの書き込みの前にログに変更内容を書き出す。~
これにより、データベースが何らかの異常でダウンしても、WALログから障害発生前の状態まで復旧することができる。

* WALの構造 [#mfab8c81]

PostgreSQLでは、データベースクラスタの下のpg_wal(ver10以降、以前はpg_xlog)にWALログが書かれる。

** 論理構造 [#d74f22b3]

- WALは、通常16MBのファイルで管理される。
- WALレコードは、タイムライン+ログID+ログID内のオフセットで決まる。~
|~項目|~サイズ|h
|tli(タイムラインID)|4byte|
|xlogid(ログID)|4byte|
|xrecoff(オフセット)|4byte|
WALファイル名は、これらの値で構成される。
#geshi{{{
タイムラインID:1
 セグメント0     00000001 00000000 00000000  start xlogid=0
 セグメント1     00000001 00000000 00000001
 セグメント2     00000001 00000000 00000002
 ...
 セグメント255   00000001 00000000 000000FE  end   xlogid=0
 セグメント256   00000001 00000001 00000001  start xlogid=1
 ...

-- ログIDごとのセグメント数

XLogSegmentsPerXLogId(wal_segsz_bytes) = 256

-- WALファイル名

例:タイムライン = 1、LSN = 0/14000028 の時
  xlogid = 0
  xrecoff = 335544360
  オフセットNO = 335544360 - 1 / 16MB = 20 = 0x14
  ファイル名 = 000000010000000000000014
}}}

&label(warn){参考};
- [[access/xlog_internal.h>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;f=src/include/access/xlog_internal.h;hb=HEAD]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};
- [[Write Ahead Logging — WAL>http://www.interdb.jp/pg/pgsql09.html]] - &size(11){&color(gray){on http://www.interdb.jp/pg/pgsql09.html};};
** WALログ内部構造 [#w4b2dee3]

&ref(./wal_internal.png,80%);


- ページ(通常8k)ごとに管理されている。ページは、ヘッダ+WALレコードで構成される。
- セグメントファイルの最初のヘッダは、&code(){XLogLongPageHeaderData};。以降は、&code(){XLogPageHeaderData};となる。
- WALレコードは、&code(){XLogRecord};とデータである。データは、バイト境界にアライメントされている。~
セグメントファイルをまたぐ場合もある。その場合は、レコードヘッダにデータが連続していることを示すビットがセットされる。(&code(){XLP_FIRST_IS_CONTRECORD};)
#geshi{{{
/* When record crosses page boundary, set this flag in new page's header */
#define XLP_FIRST_IS_CONTRECORD		0x0001
/* This flag indicates a "long" page header */
#define XLP_LONG_HEADER				0x0002
/* This flag indicates backup blocks starting in this page are optional */
#define XLP_BKP_REMOVABLE			0x0004
/* All defined flag bits in xlp_info (used for validity checking of header) */
#define XLP_ALL_FLAGS				0x0007
}}}
&label(warn){参考};[[src/include/access/xlog_internal.h>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/access/xlog_internal.h;h=3cc9c3d669e55be1455bab891788c91fa612d99e;hb=HEAD#l76]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE};};


** XLOGに関するマクロ [#qdf0ce9e]

XLOGのバイトポジションやセグメント、ファイル名を求めるためのマクロがある。~
以下、代表的なマクロの使用例である。

#geshi(c){{{
#define FRONTEND 1
#include "postgres.h"

#include "access/xlog_internal.h"

int
main(int argc, char **argv)
{
	uint32		xrecoff;
	XLogSegNo	segno;
	XLogRecPtr	xlogptr;
	TimeLineID	tli;
	char 		fname[MAXFNAMELEN];


	// XLogSegmentsPerXLogId(wal_segsz_bytes)
	printf("XLogSegmentsPerXLogId = %zu\n", XLogSegmentsPerXLogId(DEFAULT_XLOG_SEG_SIZE));

	// XLogSegNoOffsetToRecPtr(segno, offset, wal_segsz_bytes, dest)
	XLogSegNoOffsetToRecPtr(1, 0, DEFAULT_XLOG_SEG_SIZE, xlogptr);
	printf("XLogSegNoOffsetToRecPtr(1, 0, DEFAULT_XLOG_SEG_SIZE, xlogptr) = %zu\n", xlogptr);

	// XLogSegmentOffset(xlogptr, wal_segsz_bytes)
	xlogptr = DEFAULT_XLOG_SEG_SIZE + 24;
	xrecoff = XLogSegmentOffset(xlogptr, DEFAULT_XLOG_SEG_SIZE);
	printf("XLogSegmentOffset(DEFAULT_XLOG_SEG_SIZE + 24, DEFAULT_XLOG_SEG_SIZE): xrecoff = %u\n", xrecoff);

	// XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)
	xlogptr = DEFAULT_XLOG_SEG_SIZE * 2 - 1;
	XLByteToSeg(xlogptr, segno, DEFAULT_XLOG_SEG_SIZE);
	printf("LByteToSeg(DEFAULT_XLOG_SEG_SIZE * 2 - 1, segno, DEFAULT_XLOG_SEG_SIZE): segno = %zu\n", segno);

	xlogptr = DEFAULT_XLOG_SEG_SIZE * 2;
	XLByteToSeg(xlogptr, segno, DEFAULT_XLOG_SEG_SIZE);
	printf("XLByteToSeg(DEFAULT_XLOG_SEG_SIZE * 2, segno, DEFAULT_XLOG_SEG_SIZE): segno = %zu\n", segno);

	// XLByteToPrevSeg(xlrp, logSegNo, wal_segsz_bytes)
	xlogptr = DEFAULT_XLOG_SEG_SIZE * 2;
	XLByteToPrevSeg(xlogptr, segno, DEFAULT_XLOG_SEG_SIZE);
	printf("XLByteToPrevSeg(DEFAULT_XLOG_SEG_SIZE * 2, segno, DEFAULT_XLOG_SEG_SIZE): segno = %zu\n", segno);

	// XRecOffIsValid(xlrp);
	if (XRecOffIsValid(DEFAULT_XLOG_SEG_SIZE))
		printf("%s\n", "XRecOffIsValid(DEFAULT_XLOG_SEG_SIZE) is true");
	else
		printf("%s\n", "XRecOffIsValid(DEFAULT_XLOG_SEG_SIZE) is false");


	// XLogFileName(fname, tli, logSegNo, wal_segsz_bytes)
	XLogFileName(fname, 1, 1, DEFAULT_XLOG_SEG_SIZE);
	printf("XLogFileName(fname, 1, 1, DEFAULT_XLOG_SEG_SIZE): fname = %s\n", fname);

	XLogFileName(fname, 2, 2, DEFAULT_XLOG_SEG_SIZE);
	printf("XLogFileName(fname, 2, 2, DEFAULT_XLOG_SEG_SIZE): fname = %s\n", fname);

	// XLogFileNameById(fname, tli, log, seg)
	XLogFileNameById(fname, 1, 0, 1);
	printf("XLogFileNameById(fname, 1, 0, 1): fname = %s\n", fname);

	// IsXLogFileName(fname)
	strcpy(fname, "000000010000000000000001");
	if (IsXLogFileName(fname))
		printf("IsXLogFileName(\"%s\") is true\n", fname);
	else
		printf("IsXLogFileName(\"%s\") is false\n", fname);

	strcpy(fname, "00000001000000000000000#");
	if (IsXLogFileName(fname))
		printf("IsXLogFileName(\"%s\") is true\n", fname);
	else
		printf("IsXLogFileName(\"%s\") is false\n", fname);

	// IsPartialXLogFileName(fname)
	strcpy(fname, "000000010000000000000001.partial");
	if (IsPartialXLogFileName(fname))
		printf("IsPartialXLogFileName(\"%s\") is true\n", fname);
	else
		printf("IsPartialXLogFileName(\"%s\") is false\n", fname);

	// XLogFromFileName(fname, tli, logSegNo, wal_segsz_bytes)
	strcpy(fname, "0000000100000000000000FE");
	XLogFromFileName(fname, &tli, &segno, DEFAULT_XLOG_SEG_SIZE);
	printf("XLogFromFileName(fname, &tli, &segno, DEFAULT_XLOG_SEG_SIZE): fname = %s, tli = %u, segno = %zu\n",
		   fname, tli, segno);

	return EXIT_SUCCESS;
}





//  実行結果 ------------------
XLogSegmentsPerXLogId = 256
XLogSegNoOffsetToRecPtr(1, 0, DEFAULT_XLOG_SEG_SIZE, xlogptr) = 16777216
XLogSegmentOffset(DEFAULT_XLOG_SEG_SIZE + 24, DEFAULT_XLOG_SEG_SIZE): xrecoff = 24
LByteToSeg(DEFAULT_XLOG_SEG_SIZE * 2 - 1, segno, DEFAULT_XLOG_SEG_SIZE): segno = 1
XLByteToSeg(DEFAULT_XLOG_SEG_SIZE * 2, segno, DEFAULT_XLOG_SEG_SIZE): segno = 2
XLByteToPrevSeg(DEFAULT_XLOG_SEG_SIZE * 2, segno, DEFAULT_XLOG_SEG_SIZE): segno = 1
XRecOffIsValid(DEFAULT_XLOG_SEG_SIZE) is false
XLogFileName(fname, 1, 1, DEFAULT_XLOG_SEG_SIZE): fname = 000000010000000000000001
XLogFileName(fname, 2, 2, DEFAULT_XLOG_SEG_SIZE): fname = 000000020000000000000002
XLogFileNameById(fname, 1, 0, 1): fname = 000000010000000000000001
IsXLogFileName("000000010000000000000001") is true
IsXLogFileName("00000001000000000000000#") is false
IsPartialXLogFileName("000000010000000000000001.partial") is true
XLogFromFileName(fname, &tli, &segno, DEFAULT_XLOG_SEG_SIZE): fname = 0000000100000000000000FE, tli = 1, segno = 254
}}}

&label(sample){サンプル}; &ref(./xlog_macro_test.zip);
* WALレコードの読み取り [#t76c57a5]

-WALレコードの読み取りは、xlogreader.cの関数群を使う。~
XLogReadRecord()関数で実際に、1レコードを読み取る。
-この関数は、redoやレプリケーション(継続的にredoしていることと同様)でも使われる。


* スタンバイサーバ [#zb39953e]

** レプリケーション [#webcfbcd]

** walreceiver [#cd264d1f]



* 参考リンク [#x2cc3c2f]

- 学習用に便利なWALの中身をSQLで出力する拡張機能
-- [[walreader >https://github.com/moritetu/walreader]] - &size(11){&color(gray){on https://github.com/moritetu/walreader};};

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP