02-PostgreSQL的WAL机制

1 Debezium是如何从PostgreSQL抓取数据的

Debebzium为PostgreSQL提供了PostgreSQL Connector,它通过同步WAL记录实现从PostgreSQL抓取数据的功能。

要真正明白PostgreSQL Connector的运行原理,就必须先了解PostgreSQL的WAL机制。

2 WAL(Write-Ahead Logging, 预写式日志)

WAL保存了对数据库的操作记录,保证了PostgreSQL的事务持久性和数据完整性,同时避免了频繁的io对数据库性能的影响。

WAL机制的核心理念是,对数据文件的操作,例如表和索引,都应该先将操作日志写入磁盘中的WAL日志文件,而Data Buffer中的脏页延迟至checkpoint发生时候才刷新到磁盘中的数据文件,这样做的原因:

(1) 刷新数据页涉及大量的随机io,即刷新脏页需对硬盘中的多个分散的数据块进行写操作,这里会涉及硬盘磁头的寻道操作,非常耗时;相比之下刷新WAL是把记录追加到WAL文件上,属于连续写的,效率要高得多。
(2) 日志先被持久化,即使数据库发生宕机,即使Data Buffer存在未刷新到数据文件的数据页,当数据库重新启动后,那些未刷新的数据页上的变动可以根据WAL日志重做,保证数据的完整性。

3 PostgreSQL的两种数据同步方式

流复制与逻辑复制是Postgres提供的两种数据同步机制,这两种方法都是基于WAL机制的。

3.1 流复制

流复制是指主库把WAL的记录传输到备库,备库再回放record,从而保证了与主库的数据一致性。流复制过程如下图所示:

02-PostgreSQL的WAL机制_第1张图片

  • (1) 发生DML时,WAL记录先被写入内存的WAL Buffer中。
  • (2) 提交事务时,WAL Buffer相关的WAL记录被刷新到硬盘的WAL日志文件中。
  • (3) 主库会启动一个WAL Sender进程负责把刚刚刷新到硬盘的WAL记录传输到备库。
  • (4) WAL Sender进程把WAL记录发送给备库的WAL Receiver。
  • (5) WAL Receiver接收到WAL记录后会通知WAL Writer进程。
  • (6) WAL Writer负责把WAL Receiver接收到的WAL记录写入硬盘的WAL日志文件。
  • (7) 备库接收WAL记录后,重播日志,并把数据写入硬盘的数据文件。

经过上面的步骤,从库跟备库实现了数据一致性。

3.2 逻辑复制(logical replication)

流复制的对象是数据库实例,适用于复制一个与主库实例一模一样的从库的场景。

当需要把同步的粒度细化到表级别,就需要使用逻辑流复制。Debezium的PostgreSQL Connector就是基于逻辑复制实现的

两者还有以下区别:

  • 流复制可以同步DDL操作,但逻辑复制不可以。
  • 逻辑复制需要编码器,例如pgoutput,把WAL解释成其他应用可以理解的格式,如在上一篇文章做的实验,Debezium收到的记录里面除了当前的数据外,还包含了修改前的数据。pgoutput是PostgreSQL自带的编码器。
  • 流复制要求数据库版本必须一致,而逻辑复制没有这个要求。
  • 流复制更适用于主备模式,主库可读可写,备库则可读但不可写。逻辑复制的发布节点和订阅节点皆为可读可写。

3.3 逻辑复制的几个概念

Publication: Publication(发布)可定义读写的PostgreSQL实例上,经创建Publication的数据库成为发布节点,一个数据库可以创建多个Publication。而Publication的对象只能是表,可以把需要被监控的多个表配置到一个发布中。

Subscription: Subscription(订阅)被配置到需要同步数据的PostgreSQL实例上,能实时同步发布节点指定的数据表。

Replication slots: 复制槽,定义在发布节点上,记录了订阅节点的数据复制情况。这样,若订阅节点宕机,发布节点会根据复制槽内记录的数据复制情况,不会清除订阅节点未复制的WAL日志。

3.4 逻辑复制的缺点

  • 发布节点相关的表的DDL不会被复制。

  • 由于复制槽会导致未复制的WAL日志不会被删除,因此,如果订阅节点消费WAL不及时会导致发布节点WAL日志文件增长。

4 测试逻辑复制

下面,将通过一个实验展示订阅节点如何同步发布节点的数据。

4.1 逻辑复制的测试环境

类型 Postgres版本 IP 用户名 流复制用户
发布节点 postgresql 12.4 192.168.56.106 postgres logical_user
订阅节点 postgresql 12.4 192.168.56.104 postgres

4.2 创建schema和表

使用下面的SQL,在发布节点和订阅节点创建需要进行同步的表:

-- 创建schema
CREATE SCHEMA inventory
AUTHORIZATION postgres;

-- 创建products表
CREATE TABLE inventory.products
(
    id integer NOT NULL,
    name character varying(255),
    description character varying(512),
    weight double precision,
    CONSTRAINT products_pkey PRIMARY KEY (id)
);

-- 创建customers表
CREATE TABLE inventory.customers
(
    id integer NOT NULL,
    first_name character varying(255),
    last_name character varying(255),
    email character varying(255),
    CONSTRAINT customers_pkey PRIMARY KEY (id),
    CONSTRAINT customers_email_key UNIQUE (email)
);

4.3 发布节点的配置

postgresql.conf配置文件设置下面的参数:

wal_level = logical
max_wal_senders = 10
max_replication_slots = 10
  • wal_level: 设置成logical让发布节点支持逻辑复制。
  • max_repliation_slots: 最大的复制槽数量,一个订阅节点占用一个复制槽。例子中,max_replication_slots为10,则支持10个其他节点的订阅。
  • max_wal_senders:WAL发送进程的数量。

4.4 订阅节点的配置

postgresql.conf配置文件设置下面的参数:

max_logical_replication_workers = 8
  • max_logical_replication_workers:最大的逻辑复制进程数,参数默认为4,它会占用max_worker_processes参数设置的后台进程数的名额。

4.5 创建订阅用户

发布节点创建具备REPLICATION权限的复制用户,并把schema inventory的使用权限及要同步的表的SELECT权限赋给该用户:

# 创建用户logical_user
CREATE USER logical_user
	REPLICATION
	LOGIN
	CONNECTION LIMIT 8
	ENCRYPTED PASSWORD 'logical_user';

# 把schema inventory的使用权限赋给logical_user
GRANT USAGE ON SCHEMA inventory TO logical_user;

# 把customers和products表的SELECT权限赋给logical_user
GRANT SELECT ON inventory.customers, inventory.products TO logical_user;

4.6 创建Publication

发布节点创建发布,该发布将以刚刚创建的 inventory.customersinventory.products表为目标:

CREATE PUBLICATION inventory_pub FOR TABLE 
inventory.customers, inventory.products;

上面的脚本创建了一个名为inventory_pub的Publication,将发布inventory.customers和inventory.products的数据变更信息。

如果要发布当前数据库的所有表,则可以使用FOR ALL:

CREATE PUBLICATION inventory_pub FOR ALL;

可以通过pg_publication视图查询创建了的Publication信息:

SELECT * FROM pg_publication WHERE pubname = 'inventory_pub';

在这里插入图片描述

4.7 创建Subscription

订阅节点执行下面sql创建Subscription:

CREATE SUBSCRIPTION inventory_sub
CONNECTION 'host=192.168.56.106 port=5432 
	dbname=postgres user=logical_user password=logical_user'
PUBLICATION inventory_pub;

上面的sql在订阅节点上创建了一个名字为inventory_sub的Subscription,CONNECTION指定了刚刚创建的用户logical_user连接到发布节点,而PUBLICATION则指定了该Subscription对应发布节点的inventory_pub Publication

执行完上面的sql后,Postgres会返回以下的提示:
02-PostgreSQL的WAL机制_第2张图片
在订阅节点创建了Subscription的同时,发布节点同时也创建了与该Subscription同名的复制槽。

在发布节点查询pg_replication_slots视图可以查询复制槽的信息:

SELECT * FROM pg_replication_slots WHERE slot_name = 'inventory_sub';

02-PostgreSQL的WAL机制_第3张图片
其中,active为true,且active_pid为20930,表示这个复制槽已经被pid为20930的WAL Sender进程使用,可以在发布节点查看该进程:

在这里插入图片描述

4.8 测试同步效果

初始状态,发布节点与订阅节点的customers表都为空:

02-PostgreSQL的WAL机制_第4张图片

insert数据:

02-PostgreSQL的WAL机制_第5张图片
如上图所示,在发布端插入的数据被同步到了订阅端节点。

update数据:

02-PostgreSQL的WAL机制_第6张图片
上面的实验中,在发布端节点,把id为1的email修改为"[email protected]",订阅端节点也把修改数据同步过来了。

删除数据

02-PostgreSQL的WAL机制_第7张图片
在上面的实验中,在发布端把id为2的记录删除,订阅端也同时把id为2的记录删除。

truncate table

02-PostgreSQL的WAL机制_第8张图片
如上图所示,在发布端清空customers表,订阅端的customers表也同时被清空了。

通过上面的实验,我们了解到逻辑复制是如何通过WAL实验数据同步的,在下一篇文章,将对Kafka Connect作详尽的介绍,谢谢大家。

你可能感兴趣的:(Debezium,postgresql,kafka)