操作记录:PostgreSQL的Debezium连接器

需求:

Kafka实时采集PostgreSQL数据库有关增删改事务(DML)的WAL信息。

使用组件:

debezium-connector-postgres(1.0.3版本以上)

postgresql数据库(10版本以上)

kafka可用集群(2.11-1.1.0版本,3台)

pgoutput(逻辑解码插件,版本数据库自带)

修改postgresql.conf配置文件:

vim $PGDATA/postgresql.conf

wal_level=logical
checkpoint_timeout = 15min[自定义,其它wal相关的,默认配置就好]

重启数据库服务。

安装并配置Debezium:

  1. 下载:wget https://repo1.maven.org/maven2/io/debezium/debezium-connector-postgres/1.0.3.Final/debezium-connector-postgres-1.0.3.Final-plugin.tar.gz
  2. 解压:tar -zxvf  ???/debezium-connector-postgres-0.8.3.Final-plugin.tar.gz
  3. 重命名:mv debezium-connector-postgres debezium
  4. 复制debezium的jar包至kafka的libs目录下(略)

建立Kafka连接器:

请选择任一台kafka集群服务器。

  • 修改kafka的配置文件

vim $KAFKA_HOME/config/connect-standalone.properties

bootstrap.servers=[ip]:9092
  • 编写连接器信息

vim $KAFKA_HOME/config/postgres.properties

name=[自定义]

slot.name=[自定义]

connector.class=io.debezium.connector.postgresql.PostgresConnector

database.hostname=[数据库ip]

database.port=[数据库端口]

database.user=[用户:需有replication权限]

database.password=[用户密码]

database.dbname=[监视数据库]

database.history.kafka.bootstrap.servers=[kafka服务器ip:9092]

database.server.name=[自定义]

table.whitelist=[监视表:如public.demo]

publication.name=[自定义]

decimal.handling.mode=string

plugin.name=pgoutput

#snapshot.mode=never

#heartbeat.interval.ms=10000

#slot.drop.on.stop=true

#publication.autocreate.mode=filtered

#time.precision.mode=adaptive_time_microseconds

#schema.refresh.mode=columns_diff_exclude_unchanged_toast

具体连接器配置可参阅官方文档

https://debezium.io/documentation/reference/1.0/connectors/postgresql.html

启动kafka连接器:

nohup $KAFKA_HOME/bin/connect-standalone.sh $KAFKA_HOME/config/connect-standalone.properties $KAFKA_HOME/config/postgres.properties >>$KAFKA_HOME/logs-debezium/connect_kafka_postgresql.log 2>&1 &

注意:

刚建立新的kafka连接器,服务器需要导入数据库快照,恢复数据(这里是更新lsn,在kafka服务器生成相应log文件);

快照模式(snapshot.mode)可根据官方文档修改;

若数据库的数据量很大,请确保kafka集群服务器有足够大的磁盘空间;

故需要一段较长的时间,复制槽的active才会更新为true,连接器才可正式使用。

修改Postgresql的发布表:

待数据恢复完成后,即复制槽的active更新为true,可修改发布表。

debezium的发布状态默认为alltables(发布所有表),生产环境尽量只发布相应表。

update pg_publication set puballtables=false where pubname is not null;
alter publication debezium_kafka_postgresql add table [发布表];
select * from pg_publication_tables;

加入心跳机制(可忽略):

当数据库执行大事务操作时(如reindexdb,vacuumdb等),复制槽中的订阅端无法消费这些数据,若发布端数据库在该时段内没有变更发布数据表操作,很大概率就会造成pg_replication_slots的<restart_lsn>一直停滞不前

导致wal空间不断增大且无法自动清理,影响数据库性能,故可加入心跳机制。

(本人认为)心跳机制可分为两种。

  • Debezium的心跳机制

连接器信息加入:

heartbeat.interval.ms=1000

具体连接器配置可参阅官方文档

https://debezium.io/documentation/reference/1.0/connectors/postgresql.html

我一阵捣鼓后发现效果不佳,故放弃了。如果有人应用过该配置,可分享下使用经验。

  • Postgresql模拟心跳机制

官方文档有这么一条说明:

WAL磁盘空间消耗

在某些情况下,WAL文件占用的PostgreSQL磁盘空间可能会出现峰值或超出正常比例。有三种可能的原因可以解释这种情况:

  • Debezium定期向数据库确认已处理事件的LSN。这confirmed_flush_lsnpg_replication_slots插槽表中可见。数据库负责回收磁盘空间,并且可以从restart_lsn同一表中计算WAL大小。因此,如果confirmed_flush_lsn定期增加并且restart_lsn滞后,则数据库确实需要回收空间。磁盘空间通常以批处理块的形式回收,因此这是预期的行为,无需在用户方面采取任何措施。

  • 受监视的数据库中有许多更新,但是只有很少量与受监视的表和/或架构有关。通过使用heartbeat.interval.ms配置选项启用定期心跳事件,可以轻松解决这种情况。

  • PostgreSQL实例包含多个数据库,其中一个是高流量数据库。与另一个数据库相比,Debezium监视另一个低流量的数据库。然后Debezium无法确认LSN,因为复制插槽针对每个数据库工作,并且不调用Debezium。由于WAL被所有数据库共享,因此它趋于增长,直到Debezium监视的数据库发出事件为止。

为了克服第三个原因,有必要

  • 使用heartbeat.interval.ms配置选项启用定期心跳记录生成

  • 定期从Debezium跟踪的数据库中发出变更事件

    • 对于wal2json解码器插件,生成空事件就足够了。例如,这可以通过截断一个空的临时表来实现。

    • 对于其他解码器插件,建议创建一个不受Debezium监视的补充表。

然后,一个单独的过程将定期更新表(插入新事件或全部更新同一行)。然后,PostgreSQL将调用Debezium,后者将确认最新的LSN,并允许数据库回收WAL空间。

①创建debezium_heartbeat表

CREATE TABLE public.debezium_heartbeat
 (
    id INT8 NOT NULL,
    now_heartbeat_ts TIMESTAMPTZ DEFAULT NOW(),
    last_heartbeat_ts TIMESTAMPTZ DEFAULT NOW(),
    CONSTRAINT debezium_heartbeat_pkey PRIMARY KEY (id)
);
INSERT INTO public.debezium_heartbeat (id,now_heartbeat_ts,last_heartbeat_ts) VALUES (202104,NOW(),NOW());

注意:

创建完后,务必将debezium_heart表添加至kafka连接器信息的table.writelist,并且加入postgresql的发布表pg_publication。

②数据库服务器上编写脚本

vim ???/shells/auto_update_debezium_heartbeat.sh

#!bin/bash

#数据库bin命令存放目录
pg_path="???"
#执行的SQL存放目录
sql_path="???"

db_host='ip'
db_port='端口'
db_username='用户'
db_name='数据库'
#密码可保存至~/.pgpass
#格式:ip:端口:数据库:用户:密码
#chmod 700 ~/.papass

step=10 #间隔的秒数,不能大于60

for (( i = 0; i < 60; i=(i+step) )); do
  echo '['$(date "+%Y-%m-%d %H:%M:%S")'] 每10s更新<debezium_heartbeat>>表'
  echo '['$(date "+%Y-%m-%d %H:%M:%S")'] 开始更新'
$pg_path/psql -h $db_host -p $db_port -U $db_username --dbname $db_name -e -f $sql_path'update_debezium_heartbeat.sql'
echo '['$(date "+%Y-%m-%d %H:%M:%S")'] 完成更新'
  sleep $step 
done
exit 0
chmod 755 auto_update_debezium_heartbeat.sh

vim ???/shells/update_debezium_heartbeat.sql

update public.debezium_heartbeat set now_heartbeat_ts=NOW(),last_heartbeat_ts=now_heartbeat_ts where id=202104;

③加入定时任务

vim /etc/crontab

#更新debezium_heartbeat
* 15 * * * root ???/shells/auto_update_debezium_heartbeat.sh >> ???/shells/auto_update_debezium_heartbeat.log 2>&1

测试:

数据库服务执行vacuumdb full(该指令订阅端无法消费):

pg_wal目录最大可增长至10~12G(wal_sender进程跟不上checkpoint进程或受网络波动),

所以data目录得预留20G左右磁盘空间(根据自身数据量)。

期间pg_wal目录根据checkpoint点记录的lsn位置(且是订阅端返回的lsn位置)自动清理wal文件,

最后保留max_wal_size配置(默认1G)定义的wal文件大小。

自动清理成功!

vacuumdb full期间也执行一些DDL操作,

订阅端也能正常接收(可通过$KAFKA_HOME/logs查看或更下游消费端测试);

结论:

加入心跳机制,实质上是推动复制槽的<restart_lsn>。

请根据实际情况选择是否加入心跳机制!

后续:

使用PostgreSQL的Debezium连接器,并相应解决了一些可能会出现的问题,

Kafka成功实时采集到Postgresql数据库相应的DML(增删改)操作。

如何消费Kafka订阅信息并作如何处理,目前我还没有一个完整完善的流程操作。

计划流程:postgresql -> debezium -> kafka -> spark streaming(spark sql) -> mysql(postgresql/hive) ->superset

待整个(伪)实时流程建立好后,再抽空编写后面操作的博文。

如有任何不足、错误或可补充的地方,欢迎各位一起讨论,一起学习!

附1:停止连接器并删除复制槽

kill -9 [ConnectStandalone进程pid]
select * from pg_replication_slots;
select pg_drop_replication_slot('debezium_kafka_postgresql');
delete from pg_publication where pubname='debezium_kafka_postgresql';

附2:相关Kafka指令

#查看topic
kafka-topics.sh --list --zookeeper ???:2181,???:2181,???:2181
#删除topic
kafka-topics.sh --zookeeper ???:2181,???:2181,???:2181 --delete --topic [topic]

附3:手动清除wal文件

#!/bin/bash
##pg_wal_clean.sh

CUR_DATE=`date +"%Y%m%d"`
CUR_TIME=`date +"%Y-%m-%d %H:%M:%S"`

pg_bin_path="???/bin"
pg_data_path="???/data"
pg_wal_path="???/data/pg_wal"
#pg_archive_path="???/archive/archdir"
sql_path="???/shells"

db_host='???'
db_port='???'
db_username='postgres'
db_name='???'
#密码可保存至~/.pgpass
#格式:ip:端口:数据库:用户:密码
#chmod 700 ~/.papass

echo '['$CUR_TIME'] 开始:针对性清除wal文件'

lastwal=`$pg_bin_path/pg_controldata  -D $pg_data_path |grep "Latest checkpoint's REDO WAL file" |awk '{print $6}'`
echo '['$CUR_TIME'] [1]Latest checkpoint's REDO WAL file:$lastwal

echo '['$CUR_TIME'] 执行checkpoint'
$pg_bin_path/psql -h $db_host -p $db_port -U $db_username --dbname $db_name -e -f $sql_path'/pg_checkpoint.sql'
lastwal=`$pg_bin_path/pg_controldata  -D $pg_data_path |grep "Latest checkpoint's REDO WAL file" |awk '{print $6}'`
echo '['$CUR_TIME'] [2]Latest checkpoint's REDO WAL file:$lastwal

#echo '['$CUR_TIME'] 清除归档目录的wal文件'
#$pg_bin_path/pg_archivecleanup -d $pg_archive_path/$CUR_DATE/ $lastwal

echo '['$CUR_TIME'] 清除pg_wal目录的wal文件'
$pg_bin_path/pg_archivecleanup -d $pg_wal_path/ $lastwal

echo '['$CUR_TIME'] 完成:针对性清除wal文件'
##pg_checkpoint.sql
checkpoint;

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值