【云圖說】第235期 DDS讀寫兩步走 帶您領略只讀節點的風采
846
2022-05-30
導讀: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
來源:宜信技術學院
本文轉載自異步社區。
原文鏈接:https://www.epubit.com/articleDetails?id=NN02521e23-c7fb-4d6f-af7f-480603b11164
SQL 數據庫
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。