數據庫高可用是企業級用戶在關鍵業務系統中對數據庫的基本要求,在高可用方面,怎么提高能力都不為過。也正是因為高可用的問題,很多金融企業在選擇核心系統數據庫的時候,都被迫選擇分布式數據庫。因為目前國產集中式數據庫在高可用切換方面與分布式數據庫相比還存在較大的差距。前段時間在一個會議上,有一家券商就對某個國產集中式數據庫廠商提出了高可用的需求,問他們有沒有能力在證券交易系統上實現對Oracle的替代。
實現自動切換,RPO為零,RTO越低越好是關鍵業務系統給國產數據庫廠商出的一道考題。在這方面Oracle用TAC給出了一個近乎完美的答案。在Oracle RAC集群中,如果某個節點故障,應用系統的連接可以快速的以秒級的速度切換到存活的節點,SELECT查詢以及絕大多數DML/DDL也可以直接切過去繼續執行。這意味著一個建表操作或者一個數百萬數據寫入的事務可以在用戶無感知的情況下在一個實例故障時完成透明切換。頂多最終用戶會感覺某筆交易的延時長了一點點。在23C中,TAC不僅僅支持RAC,還支持ADG,當ADG SWITCHOVER的時候,或者是ADG是采用同步模式的時候,主庫故障或者切換的的時候,應用可以通過TAC切換到ADG備庫。
看起來好像這個功能也不算太難實現,不過為了這個功能,Oracle從98那年的TAF推出到實現TAC FOR JAVA整整花了20年時間,而推出比較全面的TAC功能,要到明年了。Oracle花了25年完成的革命性的功能提升,其背后到底有什么秘密呢?
一個外行來看數據庫產品的時候,往往覺得某些功能似乎很簡單,似乎只要開發人員代碼寫得好一點,實現起來很容易。其實有些看似很簡單的功能的背后有著十分復雜的邏輯。就像大家一直吐槽的XID64,十多年過去了,在PG 16里依然跳票了,雖然這個功能對于PG來說很必要,但是要想真正擁有這個功能沒那么簡單。TAC可能不一定有XID 64復雜,但是已經足夠復雜了。當RAC某個節點故障的時候,應用的客戶端感知RAC節點故障,自動重連到備用實例,如果是只讀事務,則根據PGA中的CURSOR數據以及SELECT發起時的SCN在新實例上繼續完成沒有完成的SQL PLAN余下的操作。如果是寫操作事務,事務守護模塊在新的連接中重放當前沒有完成的事務,實現無感知的自動切換。我們看上面的描述似乎實現起來并不難,實際上要想實現TAC功能,有很多基礎能力需要具備。
首先是數據庫產品必須擁有一個RPO為0的備機方案,這是確保TAC切換能順利完成繞不過去的技術點。業務系統高可用全自動快速切換中很重要的一點是數據0丟失。共享存儲多讀多寫,共享存儲強一致性讀寫分離、同步備機等都是可以實現RPO為0的備節點解決方案。如果是同步備機,一般而言可以選擇FAR-SYNC方案來避免備機對主庫產生的性能影響。
其次是高可用切換的備節點選擇問題,在多節點RAC上,如果某個節點故障時,無計劃,無策略的隨意切換會帶來很多不可知的問題,放大集群自研動態REMASTER的開銷,嚴重時甚至會引發集群性能問題。Oracle在這方面設計了SERVICE HA,需要做TAC的應用必須連接到某個設定好的SERVICE NAME,而每個需要TAC切換的SERVICE實現都已經配置好了HA切換策略。通過合理的SERVICE HA策略,可以確保切換的有序性。在這里,就需要數據庫產品有SERVICE和SERVICE HA的能力。
數據庫核心具備了上述能力后,還需要客戶端能夠快速切換的能力。在TAF里,是客戶端感知到服務器端的異常后,主動采取的切換動作,因為網絡超時、數據庫訪問超時等的判斷有一定的TIMEOUT限制,以及RETRY的需求,因此這樣的切換往往是分鐘級的,存在較大的 延時。為了更快的切換,必須采取其他的方案。Oracle為了實現快速切換,在服務端引入了一個服務—Oracle Notify Service(ONS),當某個數據庫實例故障時,ONS會很快發現,并通過Fast Application Notification(FAN)快速通知到每個連接到這個數據庫實例的會話。而每個BackEnd也都必須有接受FAN消息,并且根據FAN消息自動決策的能力。
當會話自動切換到新的數據庫實例上的時候,對于存在寫操作的會話,還需要能夠正在進行中的寫操作的能力。Oracle是通過Transaction Guard(TG)來實現的 ,TG通過當前會話正在進行的事務的完成情況自動完成未完成的事務。為了實現這個功能,Oracle引入了LTXID。LTXID的全稱是Logical Transaction ID,即邏輯事務ID。LTXID由以下四個部分組成:分支限定符(branch qualifier),用于標識事務的分支;全局事務ID(global transaction ID),用于標識事務的全局范圍;事務分支號(transaction branch number),用于標識事務的分支序號;事務序列號(transaction sequence number),用于標識事務的序列號。在TG中,把一個本地事務模擬成一個分布式事務,并將這個邏輯分布式事務的 ID與一個真實的本地事務做關聯。本地事務的變化也會同步到TG中的邏輯映射中,當發生故障切換時,TG可以根據當前操作的狀態(Prepared、committed、rolled back、complete、unknown等)采取不同的 自動化動作,完成寫操作的自動切換。如果事務的狀態是未知(unknown),事務協調器會重新查詢各個分支的狀態,并根據多數派原則(majority rule)決定事務的最終狀態,并選擇回滾還是提交。
似乎到這里TAC所需要的功能基本上都羅列了,不過還沒完,還有很多很重要的小地方的改造。比如判斷當前數據庫切換時發生的哪些錯誤是可以恢復的,哪些錯誤是無法恢復的。比如當前事務中存在一個當前數據庫無法恢復的錯誤,那么再去嘗試無損切換是不合理的,此時最好的辦法是向客戶端報個措,讓應用程序去處理這個故障。Oracle的處理方式是對每個故障增加了一個OracleException.IsRecoverable屬性,來標志故障是否可恢復,對于可恢復的故障才采取TAC無損切換,否則立馬向客戶端報錯。
前幾天我寫了一篇關于Oracle TAC的文章,當時就有好幾個國產數據庫廠商的朋友和我溝通,說他們想在自己的數據庫產品中實現TAC,從而更好的改善用戶體驗。我當時就和他們說,TAC對用戶來說是個十分棒的功能,但是要實現TAC其實并不容易。當時我承諾有空了再寫篇詳細一點的文章來介紹一些TAC的實現細節,這個周末正好想起了了,于是開始構思,今早約好了10點去拜訪一家國產數據庫廠家,因此早上的時間比較充裕,就寫了上面的內容。
實際上對于實現TAC來說,還有最后一個難關,那就是Oracle在TAC上的專利壁壘。作為參與全球化競爭的中國數據庫企業,遵守國際規則,尊重知識產權是必須要做的事情。在TAC實現路徑上,Oracle已經擁有了大量的專利,因此我們的國產廠商直接照著Oracle一頓猛抄肯定是不行的。首先我們要去研究明白在這方面哪些是Oracle的專利,必須在實現中對這些進行規避。