PostgreSQL/解析/WAL

更新日: 2020-05-10 (日) 00:08:46 (506d)

PostgreSQL/解析

WAL(Write Ahead Logging)

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

WALの構造

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

論理構造

  • WALは、通常16MBのファイルで管理される。
  • WALレコードは、タイムライン+ログID+ログID内のオフセットで決まる。
    項目サイズ
    tli(タイムラインID)4byte
    xlogid(ログID)4byte
    xrecoff(オフセット)4byte
    WALファイル名は、これらの値で構成される。
    タイムライン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

参考

WALログ内部構造

wal_internal.png

  • ページ(通常8k)ごとに管理されている。ページは、ヘッダ+WALレコードで構成される。
  • セグメントファイルの最初のヘッダは、XLogLongPageHeaderData。以降は、XLogPageHeaderDataとなる。
  • WALレコードは、XLogRecordとデータである。データは、バイト境界にアライメントされている。
    セグメントファイルをまたぐ場合もある。その場合は、レコードヘッダにデータが連続していることを示すビットがセットされる。(XLP_FIRST_IS_CONTRECORD
    /* 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
    参考src/include/access/xlog_internal.h - https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;h=refs/heads/REL_12_STABLE

XLOGに関するマクロ

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

#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

サンプル filexlog_macro_test.zip

WALレコードの読み取り

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

参考リンク


添付ファイル: filewal_internal.png 142件 [詳細] filexlog_macro_test.zip 98件 [詳細]

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