- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2019-10-23T14:49:11+00:00","default:haikikyou","haikikyou")
#author("2019-11-06T12:46:54+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};};