简介

postgresql中逻辑复制槽常备用于表级别的数据同步,多用于PG->PG之间跨版本的数据同步。Debezium插件便是利用逻辑复制的功能,可以将数据应用到异构数据库之间的数据同步。在不借助外部插件的情况下也可以同步读取复复制槽数据,实现数据异构之间的实时同步。 本文讲解一下使用逻辑解码实现异构数据库之间数据实时同步的方法思路。

创建复制槽

需要创建逻辑复制槽,物理复制槽并不能作为逻辑解码。 是用函数pg_create_logical_replication_slot手工创建,此函数有入参有四个 slot_name:定义复制槽名称 plugin name,:输出插件工具 temporary boolean DEFAULT false:是否定义临时复制槽,当会话断开之后就会被删掉。 twophase boolean DEFAULT false:是否定义两阶段提交 这里输出插件内置有pgoutput,test_conding两个,需要使用test_decoding。pgoutput插件暂时不支持SQL端调用解码,但是在发布订阅中,默认使用的是pgoutput输出插件,两者在输出格式上没什么太大差别。

输出插件在PG内置了两个pgoutput和test_decoding,

创建逻辑复制槽
select pg_create_logical_replication_slot('stream_slot_td','test_decoding',false,true);

select pg_create_logical_replication_slot('stream_slot_td_tmp','test_decoding',true,true);

select pg_create_logical_replication_slot('stream_slot_td_two','test_decoding',false,false);
删除复制槽
SELECT pg_drop_replication_slot('stream_slot_pt_two');

SQL接口进行解码

通过 pg_logical_slot_get_changes函数俘获 lot_name :需要俘获的复制槽名称 upto_lsn:读取终止点LSN upto_nchanges:读取终止行数,相当limit upto_lsn、upto_nchanges均为空的时候,读取到当前wal最后一个lsn 该函数用于消费复制槽中的数据,一旦被消费就会被清空。

除此之外还有两个函数可以进行逻辑解码

通过 pg_logical_slot_peek_changes 函数俘获 入参同pg_logical_slot_get_changes函数一样,此函数在消费过复制槽中的数据之后,复制槽中的数据并不会被清空。

通过 pg_logical_slot_get_binary_changes 函数俘获 入参同pg_logical_slot_get_changes函数一样,此函数在消费过复制槽中的数据之后,复制槽中的数据并不会被清空。返回值为bytea数据类型

使用函数进行解码复制槽其函数解码只会返回三个字段lsn,xid,data

select lsn,xid,data from pg_logical_slot_peek_changes ('stream_slot_td',null,null);

select lsn,xid,data from  pg_logical_slot_get_binary_changes ('stream_slot_td',null,null);

select lsn,xid,data from  pg_logical_slot_get_changes('stream_slot_td',null,null);

删除复制槽 SELECT pg_drop_replication_slot('your_slot_name');

在解码复制槽时,并不会读取到非创建库的WAL信息。

pg_recvlogical的俘获复制槽流

pg_recvlogical -h 10.0.0.107 -d postgres --slot=stream_slot_td  --start   --no-loop  --file=output --plugin=test_decoding  -U postgres 

将其内容读取到output文件中去 使用以下python代码 去每一秒读取output文件,此时你可以将其读取出来数据转换为SQL 语句 更新到其他的异构库数据库中去。

import time
import paramiko

# 远程服务器信息
ssh_host = "10.0.0.107"
ssh_user = "postgres"
ssh_password = "postgres"
remote_file_path = "/home/postgres/output"

# 记录文件上次读取的位置
last_position = 0


def run_ssh_command(ssh_client, command):
    """执行 SSH 命令并返回输出"""
    stdin, stdout, stderr = ssh_client.exec_command(command)
    return stdout.read().decode('utf-8')


try:
    # 设置SSH连接
    ssh_client = paramiko.SSHClient()
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh_client.connect(ssh_host, username=ssh_user, password=ssh_password)

    while True:
        # 使用 tail 命令从上次的位置读取新增内容
        tail_command = f"tail -c +{last_position + 1} {remote_file_path}"
        new_data = run_ssh_command(ssh_client, tail_command)
        last_position += len(new_data)

        if new_data:
            # 处理每行日志并打印到控制台
            lines = new_data.splitlines()
            for line in lines:
                print(line)

        # 每隔1秒检查一次
        time.sleep(1)

finally:
    ssh_client.close()