DBus資料庫表結構變更處理方案

宜信技術學院發表於2019-07-25

導讀:DBus是我們要介紹的在敏捷大資料(Agile BigData)背景下的第一個平臺。企業中大量業務資料儲存在各個業務系統資料庫中,為同時解決資料同步的一致性和實時性問題,DBus(資料匯流排)平臺應運而生。

DBus專注於資料的實時採集和實時分發,是一種基於日誌的解決方案,同時能夠提供訊息訂閱的方式給下游系統使用。本篇文章主要介紹在DBus的設計中,它是如何處理表結構變更及其帶來的各種問題的。

資料庫表結構變更在軟體產品快速迭代過程中是普遍存在的現象,抽取資料庫中的資料是DBus最重要的功能之一,那麼對於資料庫中表結構變更及其帶來的各種問題,DBus是如何處理的呢? (本文僅討論DBus for Oracle的實現方案)

貼源輸出是DBus的基本設計原則之一,通過解析後的資料庫日誌獲取資料轉換成UMS輸出到Kafka,當表結構發生變更時DBus必須能夠及時的調整輸出UMS的結構,以確保和資料庫中表結構保持一致,這裡有兩個問題需要解決:

1)如何感知表結構變更?

2)表結構變更後,新的表結構要如何與OGG輸出的二進位制資料關聯?  

一、感知表結構變更

對於感知表結構變更,Oracle已經通過DDL trigger為我們提供了很好的支援,接下來我們要考慮的是如何讓DBus感知到表結構變更? 我們討論出以下兩種方案:

1.1 RPC方案

在DDL trigger中呼叫DBus提供的REST服務,將表結構變更事件傳送給DBus。

該方案思路簡單容易實現,但也有一些明顯的弊端,比如DBus需要提供高可用、低延時的REST服務,否則可能會使資料庫中的DDL操作變得緩慢甚至執行出現錯誤; DBus 的REST伺服器對有資料實時同步需求的所有資料庫都必須開通防火牆策略,這將給DBus的部署帶來很大的麻煩。

1.2 OGG實時同步方案

在DDL trigger中將表結構變更事件儲存到一張Event表裡,然後通過OGG實時的從日誌中將資料同步到Kafka,從而感知表結構變更事件。

該方案實現相對複雜但具有很多優點,比如對資料庫的侵入性相對較小,DDL執行時只是將資料寫入到Event表中,相對網路通訊來說,其延時更低、可靠性更高;更明顯的優勢是這種方案基於資料庫日誌實現,能夠使用Event表的資料,嚴格的將表結構變更前後的資料區分開。

舉例來說,對於表:test來說,依次執行insert → alter → insert 三個操作,因為OGG讀取資料庫日誌存在延時,如果利用RPC方案,可能出現這樣的一種情況:DBus REST服務接收到alter事件之後,第一個insert的記錄才被OGG捕獲併傳送給DBus,此時DBus會認為這條資料中包含alter變化後的資料。這是一個很嚴重的問題,而OGG實時同步方案無論資料還是時間均通過OGG讀取日誌的方案實現,可以完美的避免這種問題的發生。

對比兩種方案OGG實時同步方案優勢明顯,最終我們採用此方案。

然而,採用這種方案也並非一帆風順,按照該方案的總體思路實現以後,我們遇到了一個很奇怪的問題:通過DDL trigger寫到Event表中的資料無法被OGG讀取,在經歷多番嘗試無解之後,我們試圖到OGG的文件中尋找答案,而最終的結果卻是:DML or DDL operations performed from within a DDL trigger are not captured.

這個答案讓問題變得更棘手,但這是最佳方案,我們沒有理由放棄。於是我們開始嘗試在DDL trigger中呼叫儲存過程,在儲存過程中執行Event表的insert操作,但由於儲存過程和DDL trigger仍然屬於同一個事務,因此Event表的資料依然不能被OGG捕獲,但通過這個嘗試我們覺得只要在另外一個事務中寫Event表就能解決我們面臨的問題,於是我們又想到了RPC,但RPC缺點太過明顯。那麼有沒有其他可以替代的方案呢?

實際上oracle資料庫裡可以使用多種語言來編寫儲存過程,Oracle 8i開始支援java編寫儲存過程,於是我們立即開始實現java儲存過程,通過JDBC連線資料庫實現Event表的寫入並提交事務,最終通過實踐驗證了這種辦法的可行性,OGG成功的獲取到了DDL trigger呼叫java儲存過程寫入到Event表的資料。

然而,這種實現並不算完美。當我們在生產環境部署DDL trigger的時候,發現資料庫伺服器中並沒有安裝執行java所需要的元件,每次部署都需要DBA同學安裝執行java儲存過程所需要的元件,我們試圖找到一個不使用java儲存過程的方案。這裡要感謝韓鋒老師對我們的幫助,韓老師在聽了我 們的實現原理之後,啟發我們自治事務應該可以解決這個問題,我們即刻動手開始改造DDL trigger,使之支援自治事務,經過改造之後該方案才算完美,最終實現邏輯如圖1所示:

二、處理表結構變更事件

DBus已經具備通過事件方式感知表結構變更的能力,接下來詳細說明一下表結構變更事件該如何處理。

下圖描述了Event的完整處理流程:

Event中描述了發生結構變更的表名、該表所屬的schema以及後設資料版本號,DBus接受並解析Event之後,根據表名、schema以及版本號呼叫後設資料抓取模組獲取該表的後設資料(包括表的欄位型別、長度以及註釋等)資訊,實際上DDL trigger和alter語句在一個事務中執行,這樣在trigger執行過程中無法從oracle的資料字典裡獲取到修改之後表結構後設資料,我們寫入到meta_history表中的後設資料只是執行alter語句之前的後設資料資訊(因此我們給這個表取名為table_meta_his),要得到完整的後設資料資訊需要聯合table_meta_his和資料字典進行查詢,示意SQL如下:

這個SQL的結果有兩種可能:

1)只包含all_tab_cols檢視中的資料

2)既包含all_tab_cols檢視中的資料又包含table_meta_his表的資料(is_current欄位的作用是區別該欄位的來源)

結果A表明在table_meta_his表中沒有找到資料,這說明在生成表結構變更Event至後設資料抓取程式成功獲取後設資料期間沒有再次發生表結構變更,結果B則說明在此期間又發生過一次或多次表結構變更。  

為什麼要使用union all?

單獨使用上圖中的兩個SQL可能導致後設資料獲取程式獲取到錯誤的結果,例如:接到表結構變更Event 1後,我們呼叫SQL 1 查詢table_meta_his結果集為空,在呼叫SQL 2之前表結構再次發生變更(命名為Event 2),這種情況下我們通過SQL 2 查詢到的結果實際上是再次變更後的結果,使用這個結果產生的後設資料去解析Event 1和Event 2之間的資料,如果兩次表結構變更是不相容的,那麼必然會導致解析失敗。

感知表結構變更以及處理表結構變更事件的最終目的是能夠生成正確的輸出結果,其中的更多細節以及實現可以參考:

https://github.com/BriData/DBus

作者:張玉峰

來源:宜信技術學院


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69918724/viewspace-2651704/,如需轉載,請註明出處,否則將追究法律責任。

相關文章