場景:現場庫到前置庫。

思考:使用觸發器?

1、侵入性解決方案

2、需要時各種配置,不需要時又是各種配置

Change Data Capture:捕捉變化的資料,通過日誌監測並捕獲資料庫的變動(包括資料或資料表的插入,更新,刪除等),將這些變更按發生的順序完整記錄下來,寫入到訊息中介軟體中或者通過其他途徑分發出去。

與觸發器相比,通過日誌監控的好處:

  • 1、對原庫基本無侵入性,不需要像觸發器一樣,對原庫進行操作。

  • 2、可以針對整庫或者庫中的表進行監控,比觸發器更加靈活高效,避免監控表比較多的情況的頻繁的建觸發器

說明

關於logminer:

所有對使用者資料和資料字典的改變都記錄在Oracle的Redo Log中,因此,Redo Log包含了所有進行恢復操作所需要的資訊。但是,原始的Redo Log檔案無法看懂,所以,Oracle從8i以後提供了一個非常有用的分析工具,稱為LogMiner。使用該工具可以輕鬆獲得Redo Log檔案(包含歸檔日誌檔案)中的具體內容。

關於程式碼:全部寫到一個java方法下,主要演示所有過程,關鍵問題處理,封裝留給你們自己進行。

關於參考:主要參考了debezium和一些國外開源的實現,但是這些都沒有支援clob大欄位和blob二進位制欄位,同時也沒考慮一些特殊情況,我在平時的測試積累中解決了此塊的內容,故一起開源分享出來。

Github地址

https://github.com/nengm/OracleCDCByLogminer

前置步驟

Oracle:通過開源logminer進行日誌分析,支援大字串,二進位制。

目前只針對線上日誌分析,離線的由於需要考慮log大小等,留給以後處理。

開啟歸檔日誌,這邊步驟主要是為了能夠使用logminer

以資料庫系統管理員sys as sysdba登入
SQL> shutdown immediate; 關閉資料庫
SQL> startup mount; 啟動資料庫到mount狀態
SQL> alter database archivelog; 啟動歸檔模式
SQL> alter database open;啟動資料庫
SQL> alter system switch logfile;切換日誌檔案
檢視資料字典表或檢視許可權
GRANT SELECT_CATALOG_ROLE TO [使用者名稱];
執行系統所有包許可權
GRANT EXECUTE_CATALOG_ROLE TO [使用者名稱];
建立會話許可權
GRANT CREATE SESSION TO [使用者名稱];
選擇任何事務的許可權
GRANT SELECT ANY TRANSACTION TO [使用者名稱];

對於12c及以上,還需要對pdb使用者進行一些設定,具體遇到再百度下。

分析過程

1、拿到當前最大位點

2、開啟logminer分析

如果ENDSCN和STARTSCN沒有超過步長,ENDSCN就拿當前的最大位點。

begin
DBMS_LOGMNR.START_LOGMNR(
STARTSCN => 586613478,
ENDSCN => 586613490,
OPTIONS =>
DBMS_LOGMNR.SKIP_CORRUPTION
+DBMS_LOGMNR.NO_SQL_DELIMITER
+DBMS_LOGMNR.NO_ROWID_IN_STMT
+DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG
+DBMS_LOGMNR.CONTINUOUS_MINE
+DBMS_LOGMNR.COMMITTED_DATA_ONLY
+DBMS_LOGMNR.STRING_LITERALS_IN_STMT
);
end;

3、通過資料字典得到表的資料型別

程式碼中的dictionary.sql

4、根據配置的白名單進行資訊刪選

SELECT
*
FROM
  V$LOGMNR_CONTENTS
WHERE(SEG_OWNER = 'EPOINT' AND TABLE_NAME = 'BASEINFO' AND COMMIT_SCN >=0)

5、分析

1、insert語句(帶大欄位和圖片)

大欄位和圖片都通過儲存過程得到。

我們根據csf看是否超過4000個位元組,如果超過csf為0,我們把所有的sql先組裝到一起,然後通過資訊組裝出一個guid,存入map,這樣後面就能找到對應的EMPTY_CLOB()對應的值了。

//用xid、ownerName、tableName、columnString唯一標識當前二進位制欄位的guid
//#!>-<!#為分隔符
//例如:99001800E16A0000#!>-<!#EPOINT#!>-<!#BASEINFO1#!>-<!#IMAGE

2、update語句(帶大欄位和圖片)

update "EPOINT"."BASEINFO" set "NAME" = '蘇愛毓', "BIRTHDAY" = TIMESTAMP ' 1977-03-17 17:22:59', "AGE" = 3, "ADDRESS" = UNISTR('\6FB3\95E8\516B\885718\53F7-8-6') where "ROWGUID" = 'b73af60a-cdda-4702-8f28-0d707c0245a1' and "NAME" = '法貞鳳' and "BIRTHDAY" = TIMESTAMP ' 1978-04-16 09:21:46' and "AGE" = 61 and "ADDRESS" = UNISTR('\8BF8\57CE\5927\53A674\53F7-6-8')

下面緊接著是他要處理的大欄位和二進位制。

程式中通過下面的guid判斷是否是一批資料,然後把檢測到的大欄位或者二進位制與之關聯。

//用xid、ownerName、tableName、columnString唯一標識當前二進位制欄位的guid
//#!>-<!#為分隔符
//例如:99001800E16A0000#!>-<!#EPOINT#!>-<!#BASEINFO1#!>-<!#IMAGE

注意:

我們再測試下,讓startscn加1

可以看到無法分析出資料了,所以我們程式需要處理掉這種情況,遇到這種情況最簡單的辦法就是startscn要往回退一點,也注意不能形成死迴圈

其實主要是通過這些進行流程的分析處理,裡面會遇到很多的坑。

測試實現

配置:由於測試整個過程,暫時單表,多表只要改造下。

1、配置

2、啟動

3、插入一條資料

主要在11g在測試,12c做了相容,而且要使用cdb賬戶

11g

插入mysql中

5、更新

oracle

更新完mysql

6、插入2000條測試

oracle

mysql目標資料庫