【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 許可協議。轉載請注明出處