PostgreSQL 源码解读(111)- WAL#7(Insert&WAL - XLogRecordAssemble-FPW)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PostgreSQL 源码解读(111)- WAL#7(Insert&WAL - XLogRecordAssemble-FPW)相关的知识,希望对你有一定的参考价值。
参考技术A本节重点跟踪分析了XLogRecordAssemble函数中对FPW(full-page-write)的处理过程。
全局静态变量
XLogRecordAssemble使用的全局变量包括hdr_rdt/hdr_scratch/rdatas等.
宏定义
XLogRegisterBuffer函数使用的flags
XLogRecData
xloginsert.c中的函数构造一个XLogRecData结构体链用于标识最后的WAL记录
registered_buffer
对于每一个使用XLogRegisterBuffer注册的每个数据块,填充到registered_buffer结构体中
rmid(Resource Manager ID)的定义
0 XLOG
1 Transaction
2 Storage
3 CLOG
4 Database
5 Tablespace
6 MultiXact
7 RelMap
8 Standby
9 Heap2
10 Heap
11 Btree
12 Hash
13 Gin
14 Gist
15 Sequence
16 SPGist
XLogRecordAssemble函数从已注册的数据和缓冲区中组装XLOG record到XLogRecData链中,组装完成后可以使用XLogInsertRecord()函数插入到WAL buffer中.
详见 上一小节
场景二:在数据表中插入第n条记录
测试脚本如下:
设置断点,进入XLogRecordAssemble
输入参数:
rmid=10即0x0A --> Heap
info=0 \'\\000\'
RedoRecPtr=5509173376,十六进制值为0x00000001485F5080
doPageWrites=true,需要full-page-write
fpw_lsn=0x7ffd7eb51408(fpw是full-page-write的简称,注意该变量仍未赋值)
接下来是变量赋值.
hdr_scratch的定义为:static char *hdr_scratch = NULL;
hdr_rdt的定义为:static XLogRecData hdr_rdt;
配置hdr_rdt(用于存储header信息)
full-page-write\'s LSN初始化
开始循环,获取regbuf.
regbuf是使用
注意:
在内存中,main data已由函数XLogRegisterData注册,由mainrdata_head和mainrdata_last指针维护,本例中,填充了xl_heap_insert结构体.
block data由XLogRegisterBuffer初始化,通过XLogRegisterBufData注册(填充)数据,分别是xl_heap_header结构体和实际数据(实质上是char *指针,最终填充的数据,由组装器确定).
1.main data
2.block data -> xl_heap_header
3.block data -> tuple data
查看地址0x1feb07f开始的21个字节数据,指向实际的数据.
第一个字段:首字节0 \'\\000\'是类型标识符(INT,注:未最终确认),后续4个字节是32bit整型0x00002000,即整数8192.
第二个字段:首字节 17 \'\\021\'是类型标识符(VARCHAR,注:未最终确认),后续是实际数据,即C2-8192
第三个字段:与第二个字段类似(C3-8192)
4.block data -> backup block image
这部分内容由XLogRecordAssemble()函数填充.
继续执行后续逻辑,regbuf->flags=0x08表示REGBUF_STANDARD(标准的page layout)
doPageWrites = T,需要执行full-page-write.
page_lsn = 5509173336,十六进制值为0x00000001485F5058,逻辑ID为0x00000001,物理ID为0x00000048,segment file文文件名称为00000001 00000001 00000048(时间线为0x00000001),文件内偏移为0x5F5058.
需要执行full-page-write
判断是否需要tuple data(needs_data标记)
配置BlockHeader
要求包含块镜像(FPI),获取相应的page(64bit的指针)
查看page内容
换用字符格式查看0x7f391f91d330开始的数据
标准块(REGBUF_STANDARD),获取lower&upper,并设置hole信息
没有启用压缩,则不尝试压缩block
设置标记BKPBLOCK_HAS_IMAGE
设置相关信息
设置标记BKPIMAGE_APPLY
没有压缩存储,设置相关信息
设置rdt_datas_last变量,构建hdr_rdt链表,分别指向hole前面部分hole后面部分
查看hdr_rdt相关信息
设置大小
不需要处理tuple data,不是同一个REL
拷贝头部信息到scratch缓冲区中
包含FPI,追加SizeOfXLogRecordBlockImageHeader
不是同一个REL,追加RelFileNode
后跟blocknumber
继续下一个block
已处理完毕,接下来,是XLOG Record origin(不需要)
接下来是main data,使用XLR_BLOCK_ID_DATA_SHORT格式
调整rdt_datas_last变量信息,调整总大小
调整hdr_rdt信息
计算CRC
填充记录头部信息的其他域字段.
返回hdr_rdt
DONE!
Write Ahead Logging — WAL
PostgreSQL 源码解读(4)- 插入数据#3(heap_insert)
PostgreSQL 事务日志WAL结构浅析
PostgreSQL 源码解读(110)- WAL#6(Insert&WAL - XLogRecordAssemble记录组装函数)
PG Source Code
一款PostgreSQL WAL日志解析工具: wal2json
译者简介
钟硕, 现供职于迪卡侬,PostgreSQL & Oracle DBA
Debezium 属于红帽开源项目, wal2json作为其组成部分可以提供基于PG库级别的DML日志挖掘工作。
Debezium针对几类主流数据库的连接器:
https://debezium.io/docs/connectors/
Wal2json安装页面:
https://debezium.io/docs/install/postgres-plugins/
Debezium 目地在于提供一个分布式的平台,将数据库日志中的事件记录转化为事件流,使得外部应用能够对数据库中的行级操作做出快速响应。Debezium 可以建立在Apache kafka的上层,为kafka connect提供可兼容的连接器用于监控和管理特定的数据库。
wal2json工具的安装
自从PostgreSQL 9.4 支持logical级别的WAL后,PostgreSQL可以通过逻辑解码的方式(基于行或语句的logical replication)来解读WAL中的内容。
Logical Decoding Output Plugins
https://www.postgresql.org/docs/9.4/logicaldecoding-output-plugin.html
WAL_LEVEL支持的四个级别
https://www.postgresql.org/docs/9.4/runtime-config-wal.html
通过slot在从节点解码WAL中的数据变更流进行重演实现数据库的逻辑复制。
安装:
export PATH="$PATH:/usr/pgsql-9.5/bin"
注意:这里一定要确认系统环境中找到正确版本的pg_config(如果安装过多个版本的PostgreSQL)
git clone https://github.com/eulerto/wal2json -b master --single-branchcd wal2jsonmake && make install
正确安装后的输出:
Cloning into 'wal2json'...
remote: Enumerating objects: 47, done.
remote: Counting objects: 100% (47/47), done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 496 (delta 26), reused 38 (delta 23), pack-reused 449
Receiving objects: 100% (496/496), 204.39 KiB | 0 bytes/s, done.
Resolving deltas: 100% (345/345), done.
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC -I. -I./ -I/usr/pgsql-9.5/include/server -I/usr/pgsql-9.5/include/internal -D_GNU_SOURCE -I/usr/include/libxml2 -I/usr/include -c -o wal2json.o wal2json.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC -L/usr/pgsql-9.5/lib -Wl,--as-needed -L/usr/lib64 -Wl,--as-needed -Wl,-rpath,'/usr/pgsql-9.5/lib',--enable-new-dtags -shared -o wal2json.so wal2json.o
/usr/bin/mkdir -p '/usr/pgsql-9.5/lib'
/usr/bin/install -c -m 755 wal2json.so '/usr/pgsql-9.5/lib/'
PostgreSQL服务端配置
shared_preload_libraries = 'wal2json'
wal_level = logical
max_wal_senders = 4
max_replication_slots = 4
创建具有Replication和Login授权的用户
CREATE ROLE <name> REPLICATION LOGIN;
修改pg_hba.conf,使该用户可以远程或本地访问数据库
############ REPLICATION ##############
local replication <name> trust
host replication <name> 127.0.0.1/32 trust host replication <name> ::1/128 trust
建立测试环境
CREATE DATABASE test;
CREATE TABLE test_table (
id char(10) NOT NULL,
code char(10),
PRIMARY KEY (id)
);
另开一个会话窗口
pg_recvlogical -d test --slot test_slot --create-slot -P wal2json
pg_recvlogical -d test --slot test_slot --start -o pretty-print=1 -f -
进行一些基本的DML操作
test=# INSERT INTO test_table (id, code) VALUES('id1', 'code1');
INSERT 0 1
test=# update test_table set code='code2' where id='id1';
UPDATE 1
test=# delete from test_table where id='id1';
DELETE 1
对应DML的输出:
INSERT
{
"change": [
{
"kind": "insert",
"schema": "mdmv2",
"table": "test_table",
"columnnames": ["id", "code"],
"columntypes": ["character(10)", "character(10)"],
"columnvalues": ["id1 ", "code1 "]
}
]
}
UPDATE
{
"change": [
{
"kind": "update",
"schema": "mdmv2",
"table": "test_table",
"columnnames": ["id", "code"],
"columntypes": ["character(10)", "character(10)"],
"columnvalues": ["id1 ", "code2 "],
"oldkeys": {
"keynames": ["id"],
"keytypes": ["character(10)"],
"keyvalues": ["id1 "]
}
}
]
}
DELETE
{
"change": [
{
"kind": "delete",
"schema": "mdmv2",
"table": "test_table",
"oldkeys": {
"keynames": ["id"],
"keytypes": ["character(10)"],
"keyvalues": ["id1 "]
}
}
]
}
也可以用PostgreSQL提供的REPLICA IDENTITY来决定对表UPDATE和DELETE操作时,logical日志输出信息的详细程度。
DEFAULT 逻辑日志中包含表中主键列被UPDATE或DELETE的前值的信息。
NOTHING 逻辑日志中不包含表中任何UPDATE或DELETE变更的信息。
FULL 逻辑日志中包含表中被UPDATE或DELETE列的前值的整行信息。
USING INDEX <index_name>仅包含指定索引中所有列的前值信息。
ALTER TABLE test_table REPLICA IDENTITY USING INDEX test_table_pkey;
{
"change": [
{
"kind": "update",
"schema": "mdmv2",
"table": "test_table",
"columnnames": ["id", "code"],
"columntypes": ["character(10)", "character(10)"],
"columnvalues": ["id1 ", "code2 "],
"oldkeys": {
"keynames": ["id"],
"keytypes": ["character(10)"],
"keyvalues": ["id1 "]
}
}
]
}
注意:
wal2json 插件无法很好的处理引号标识符
wal2json 插件无法输出没有主键的表的事件信息
wal2json 插件不支持特殊浮点类型的值(Nan 或 infinity)
参考
https://debezium.io/docs/install/postgres-plugins/
PostgreSQL中文社区欢迎广大技术人员投稿
投稿邮箱:press@postgres.cn
以上是关于PostgreSQL 源码解读(111)- WAL#7(Insert&WAL - XLogRecordAssemble-FPW)的主要内容,如果未能解决你的问题,请参考以下文章