【GaussDB】在逻辑复制中剔除指定用户的事务
【GaussDB】在逻辑复制中剔除指定用户的事务
1. 需求背景
在逻辑复制中,期望对源端指定用户的所有操作不复制到目标端。然而WAL日志中没有用户信息可用于过滤,因此考虑结合审计日志来实现这一需求。
2. 解决方案
2.1 配置审计日志
首先需要开启事务号记录和指定用户的全量审计:
--开启记录事务号
gs_guc reload -c "audit_xid_info=1"--配置指定用户开启全量审计
gs_guc reload -c "full_audit_users='ogadmin'"
2.2 查询指定用户的事务号
通过以下SQL查询指定用户的所有事务号:
--查询指定用户的所有事务号
with t as (
select case when substr(detail_info,1,3)='xid' and substr(detail_info,4,3)<>'=NA' then regexp_replace(detail_info,'xid=(\d+),.*','\1') end as transactionid, * from gs_query_audit('19000101','30000101') where username='ogadmin' )select * from t where transactionid is not null;
查询结果示例:
transactionid | time | type | result | userid | username | database | client_conninfo | object_name | detail_info | node_name | thread_id | local_port | remote_port |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
928506 | 2025-06-26 13:34:22.000 +0800 | ddl_table | ok | 16728 | ogadmin | postgres | gsql@[local] | t7 | xid=928506, create table t7(a int); | primary | 140316575266560@804231262475203 | 7456 | null |
928810 | 2025-06-26 13:40:41.000 +0800 | ddl_schema | ok | 16728 | ogadmin | postgres | gsql@[local] | s2 | xid=928810, create schema s2; | primary | 140316575266560@804231641079130 | 7456 | null |
929001 | 2025-06-26 13:44:46.000 +0800 | dml_action | ok | 16728 | ogadmin | postgres | gsql@[local] | t7 | xid=929001, insert into t7 values (1); | primary | 140316575266560@804231886663356 | 7456 | null |
2.3 审计日志事务号提取问题分析
GaussDB的ADM_AUDIT_TRAIL视图中已尝试截取出事务号,但该视图的SQL存在问题:
CASEWHEN "position"(a.detail_info, 'xid = NA'::text) = 1 AND "position"(a.detail_info, 'xid'::text) = 1 THEN NULL::textELSE substr(a.detail_info, "position"(a.detail_info, '='::text) + 1, "position"(a.detail_info, ','::text) - "position"(a.detail_info, '='::text) - 1)
END AS transactionid
主要问题:
- 实际日志中是
xid=NA
而非xid = NA
,导致第一段条件永远无法匹配 - 当detail_info中存在其他
=
号时(如配置guc参数),会错误截取非事务号内容(如'bind_procedure_searchpath
)
因此需要自行从原始detail_info中截取事务号。
3. 方案实施挑战
3.1 关键问题分析
实施此方案需要解决以下核心问题:
- 审计日志完整性:如何确保审计日志记录是完整的?
- 逻辑嵌入:如何将剔除事务的逻辑嵌入到原有的逻辑复制程序中去?
- 操作顺序:如何确保各项操作的先后顺序?
- 资源消耗:如何减少重复查询审计日志的资源消耗(可否直接基于操作系统的存储接口去监控审计日志文件的变化)?
4. 替代方案:基于事务标签的过滤机制
PostgreSQL在双向复制中,通过在事务上标记名称来区分源端事务,防止循环复制,GaussDB同样具有此能力:
gaussdb 已移除pg_recvlogical二进制程序,仅保留了相关接口,为测试方便,这里实际是使用MogDB测试的
# 服务端
--创建一个复制槽
pg_recvlogical -d postgres -S test_slot --create--创建复制源标签
select pg_replication_origin_create('maintain_node');# 采集端
--开启流式解码
pg_recvlogical -d postgres -S test_slot --start -v -f -## sql执行客户端
--执行一些SQL,采集端可以捕获到数据--绑定当前会话标签
select pg_replication_origin_session_setup ('maintain_node');---执行一些SQL,采集端没有捕获到数据--解绑当前会话标签
select pg_replication_origin_session_reset ();--执行一些SQL,采集端可以捕获到数据# 环境清理
pg_recvlogical -d postgres -S test_slot --drop
select pg_replication_origin_drop('maintain_node');
原理:逻辑复制输出插件(如pgoutput、pg_recvlogical或test_decoding)默认会忽略携带复制源标签的事务,因为这些事务被视为"已同步过的数据",避免重复复制。
可以通过创建登录事件触发器,针对指定用户的登录自动打上复制源标签,从而实现指定的用户事务不复制。
5. 推荐方案:历史表归档策略
我个人不认为数据复制软件是用来处理这种归档操作的。比较通用的方式是:
- 源库对需要归档的表,在当前库建立历史表
- 归档时将需要归档的数据插入到历史表,然后删除当前表已归档的数据
- 对于历史表的修改操作(一般只有运维delete或truncate),配置同步规则,不进行数据删除的同步
- 可新建一个schema存放历史表,便于在复制软件中进行规则配置
- 复制软件针对历史表进行增量复制,对于实时表进行全量复制(其实这是ETL该干的事了)
虽然插入历史表会产生额外IO,可能使数据归档操作时间翻倍,但相比剔除事务不同步的方式,历史表方式更加安全,避免剔除事务时遗漏某些关键事务。
6.总结
本文探讨了在GaussDB中实现指定用户操作不复制到目标端的三种方案:
- 基于审计日志的事务过滤
审计日志方案通过提取事务号实现过滤,但面临审计日志完整性、逻辑嵌入复杂性、操作顺序保证和资源消耗等挑战。虽然技术上可行,但实施复杂度较高,且存在事务遗漏风险。 - 基于逻辑复制标签的过滤
基于逻辑复制标签实现过滤,技术上可行,但打标签这个附加操作需要在执行sql前执行(除非使用触发器,但触发器属于高风险操作,不建议使用),如果漏执行,将会存在错误覆盖目标库的风险。 - 基于历史表的归档策略
历史表归档方案通过在源库建立历史表存储归档数据,配置复制规则排除历史表的删除操作,虽然会增加IO开销,但实现简单、安全性高,避免了事务过滤可能带来的风险。
推荐实践:采用历史表归档策略,新建独立schema存放历史表,便于复制规则配置。这种方法既满足了数据归档需求,又保证了复制数据的安全性和一致性,是更可靠的长期解决方案。
- 本文作者: DarkAthena
- 本文链接: https://www.darkathena.top/archives/GaussDB-Filtering-Out-Specified-User-s-Transactions-in-Logical-Replication
- 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处