直播時刻 | 騰訊分佈式數據庫TDSQL金融級能力的架構原理解讀

  • 2020 年 3 月 18 日
  • 筆記

為幫助開發者更好地了解和學習分佈式數據庫技術,2020年3月,騰訊雲數據庫、雲加社區聯合騰訊TEG數據庫工作組特推出為期3個月的國產數據庫專題線上技術沙龍《你想了解的國產數據庫秘密,都在這!》,邀請數十位鵝廠資深數據庫專家每周二和周四晚上在線深入解讀TDSQL、TBase、CynosDB三款鵝廠自研數據庫的核心架構、技術實現原理和最佳實踐等。三月為TDSQL專題月,本文將帶來直播回顧第一篇《騰訊自研分佈式數據庫TDSQL核心架構及特性拆解》。

視頻內容

大家好,我今天分享的主題是基於計費海量場景自研演進的分佈式數據庫TDSQL的核心架構解讀。

今天這個分享分為五個部分:

第一章,產品簡介以及適用場景

第二章,TDSQL架構分析及模塊介紹

第三章,數據一致性保障

第四章,分佈式TDSQL實踐

第五章,數據同步與備份

一、TDSQL是什麼:騰訊如何打造一款金融級分佈式數據庫

我們先初步了解TDSQL產品,以及它的適用場景。第一章包括四個方面:使用場景、發展歷程、核心特性,以及兼容性。

首先,TDSQL是騰訊推出的一款兼容MySQL的自主可控、高一致性分佈式數據庫產品。這裡我們強調一點,高度兼容MySQL——TDSQL完全兼容MySQL協議,並且做到完全自主可控、數據強一致性。第二是TDSQL具備分佈式的特性,具備一個彈性擴展、高可用的架構。在互聯網行業,海量的用戶流量場景很常見,如果數據庫不具備可伸縮性、可擴展性,是很難應對如:電商的大型促銷,春節搶紅包等突增流量的場景,這些其實都是對數據庫應對海量用戶流量的考驗。

目前TDSQL已經服務超過500+的金融政企,行業覆蓋銀行、保險、證券、政務、互聯網金融等各個領域。

我們再看一下TDSQL的前世今生。TDSQL最早可以追溯到2002年,那個時候其實還不叫TDSQL,它是騰訊計費平台部的一個數據庫服務,當時使用了開源的MySQL。2002年-2007年隨着公司業務的發展,騰訊所面臨的用戶量的壓力也越來越大。這個時候我們提出了7×24小時不宕機的高可用設計方案,來保證數據庫能提供7×24小時不間斷連續高可用服務。那個時候,騰訊的增值業務日漸成規模,業務對數據也越來越敏感,對數據可用性的要求越來越高,甚至平時還要防備一些像運營商的光纖被挖斷等各種各樣的異常場景。

在2007年-2012年,這可能是互聯網時代從互聯網到移動互聯網的發展的快速5年。當然,公司的業務也是突飛猛進。我們開始把這個高可用的數據庫產品化。到2012年,TDSQL的雛形就已經出來了,作為一款內部產品,開始在公司內部提供金融級的數據強一致性、可靠性服務。

從2012年起,TDSQL已經在騰訊內部做得已經比較成熟,已經是一個知名的產品了,但是它一直沒有對外做商業化。2014年恰逢一個很好的機會——微眾銀行的成立。微眾銀行做數據庫選型的時候關注到了TDSQL,經過反覆測試驗證,發現當時的TDSQL已經完全具備了微眾銀行對數據可用性和一致性的要求。藉此機會,TDSQL成功在微眾銀行投產,成為微眾銀行唯一的數據庫,覆蓋了銀行的核心業務。

所以說2014年,TDSQL完成了商業化,也實現了私有化部署。2014年以後,TDSQL推廣到了很多銀行、金融機構,這過程中是借鑒了2014年TDSQL在微眾銀行成功實施的寶貴的經驗。

因為在2014年微眾銀行的部署中,我們也踩了很多坑,也認識到在私有化部署環境的各種各樣的挑戰,並一一攻克了這些挑戰。當2014年在私有化部署完成之後,再到2015年TDSQL上公有雲,我們繼續通過公有雲服務打磨自己的產品。

所以從2012年作為一個內部產品到2014年的私有化部署,再到2015年公有雲上的部署,TDSQL已經逐步從一個內部產品逐漸走向行業,成為一個正式對外的商用數據庫。從2015年到2019年,TDSQL已經推廣到許多銀行和金融政企。但是很重要的一點是,雖然服務了很多銀行、金融客戶,但是在銀行領域有一塊比較難動的蛋糕叫銀行的傳統核心系統。傳統核心系統數據庫長期以來一直是被國外的商用數據庫所壟斷,比如說ORACLE、DB2啊,像TDSQL這類分佈式數據庫是很難介入的。

2018年,我們關注到張家港銀行有更換核心系統的需求,就此建立聯繫並成功達成合作,最終,2019年,我們將騰訊這套分佈式數據庫系統成功應用到了張家港銀行的傳統核心系統。張家港行也是作為全國第一家傳統核心系統上分佈式數據庫的銀行,分佈式數據庫不再是只局限於銀行的互聯網核心、互聯網銀行等外圍系統的嘗試,而是真真正正切入到銀行系統的心臟—傳統核心,這也是國產數據庫領域一個具有里程碑意義的事件。

所以在未來,我們也將繼續「走出去」深入到更複雜、更新核心的業務系統,打磨我們的產品。

這是TDSQL的發展歷程。

二、TDSQL核心特性:極具挑戰的「四高」服務與安全可運維

我們再看TDSQL的核心特性。

首先作為適應於金融場景的數據庫,數據強一致性是立命之本,因為數據不能丟、不能錯。在金融場景,你沒有辦法去估量——假如錯一條數據,到底這條數據是1分錢還是1個億,所以數據強一致是我們最根本的一個特性。不允許丟,不允許錯,這是對數據庫起碼的要求。

第二是金融級高可用。TDSQL確保99.999%以上的可用性,並支持跨IDC、多機房、同城多活的部署方式。我們最先切入金融場景是因為金融場景的挑戰是最大的。中國金融行業受監管要求最為苛刻,同時也對數據和業務的可用性、可靠性、一致性更是有極高的要求。我們要求99.999%的可用性,也就是說這個數據庫全年故障的時間不能超過5分鐘。

第三是高性能、低成本。互聯網時代的企業,都是海量業務、海量機器,性能稍微提高一個10%,可能就節約成百上千台機器的成本,這個經濟效益還是比較大的。所以高性能、低成本也是TDSQL的一個關鍵特性。

第四點是線性水平擴展。因為無論是互聯網還是其他企業,隨着數字化的發展,比如說出現突增流量,搞個活動等,現在單機的承載量越來越容易凸顯出瓶頸。所以我們提出這種水平線性擴展的能力,要求它可進行水平伸縮。可能一台機器的負載、硬盤、機器資源容納不了,但可以把它拆到多台機器,不需要考慮太多,它可以自動地提高自身吞吐量和負載量。

第五點是企業級安全。金融數據是敏感的,一些敏感的金融數據需要在當前數據基礎上再做一層更高級別的企業安全防護,比如數據庫防火牆,以及透明加密,等等。

第六點是便捷的運維,私有化部署中,很多情況下其實他們的網絡環境和部署環境跟我們是隔離的,如果銀行客戶有問題,那其實我們第一時間是切入不了去幫助解決的,所以就需要一套完善的配套設施,簡單容易上手,可以自動幫用戶去定位問題、解決問題,同時也盡量減少運維的複雜度。

三、TDSQL核心架構

接下來我們了解TDSQL的架構以及模塊劃分。通過這一章節的了解,我們更能切入TDSQL的技術細節,它為什麼要這樣設計,這樣設計有什麼好處,如何通過這樣的架構和設計實現高可用、線性擴展等能力。

1.TDSQL系統總覽

1.1 資源池

這張圖從下往上看,首先最底層是資源池,屬於IaaS層服務,可以是物理機,也可以是虛擬機,只要是給TDSQL添加機器就好,TDSQL是在一個機器的資源池上實現了數據庫實例的管理。當然,這裡推薦的還是物理機,如果增加一層虛擬機服務,無疑在穩定性和性能方面都會引入一些隱患。

1.2 存儲節點

從資源池再往上是存儲節點。存儲節點要強調的是TDSQL的兩種存儲形態,一種是Noshard數據庫,一種是分佈式數據庫(也叫Shard版TDSQL)。簡單來說,Noshard就是一個單機版的TDSQL,在MySQL的基礎上做了一系列的改造和改良,讓它支持TDSQL的一系列特性,包括高可用,數據強一致、7×24小時自動故障切換等。第二種是分佈式數據庫,具備水平伸縮能力。所以TDSQL對外其實呈現了兩種形態,呈現一種非分佈式形態,一種是分佈式的形態。至於這兩種形態的區別,或者說什麼場景更適合於哪種數據庫,後面我們有專門的章節去分析。

1.3 計算節點

再看計算節點。計算節點就是TDSQL的計算引擎,做到了計算層和存儲層相分離。計算層主要是做一些SQL方面的處理,比如詞法解、語法解析、SQL改寫等。如果是分佈式數據庫形態,還要做分佈式事務相關的協調,所以我們看到計算層不存儲數據,只運行SQL方面的實時計算,所以它更偏CPU密集型。此外,TDSQL計算節點還具備OLAP的能力,對一些複雜的計算可以進行算法上的優化——什麼時候該下推到存儲引擎層,什麼時候需要在計算層做匯總等,這是計算節點需要做的事情。

1.4 赤兔運營管理平台

再往上,是赤兔運營管理平台,如果說把下面這一套東西比作一個黑盒,我們希望有一個用戶界面操縱這個黑盒,這個界面就是赤兔運營管理平台。通過這個平台,DBA可以操縱TDSQL後台黑盒,所以相當於是一套WEB管理系統。讓所有DBA的操作都可以在用戶界面上完成,而不需要登陸到後台,不需要關心計算節點是哪個,存儲節點是哪個,或者怎麼樣管理它,要加一些節點或者減一些節點,或者把這個節點從哪裡要遷到哪裡……這些都可以通過界面化完成。DBA操作界面不容易出錯,但如果登陸到後台很容易一個誤操作,不小心把機器重啟了,就可能會造成一定的影響。

1.5 「扁鵲」智能DBA平台

有了赤兔之外,為什麼還有一個「扁鵲」智能DBA平台呢?可能正常情況下,我們機器是好的,但是,機器如果發生了故障,或者說哪天磁盤有壞塊了,或者是IO性能越來越差……SSD其實有一個衰老的過程,到了後期的話,吞吐量和IOPS可能會有一定下降,導致數據庫的響應速度變慢。這種情況如果DBA要排查,得先去看到是哪一個實例、涉及到哪一台機器、這個機器有什麼問題、檢測機器的健康狀態……這些都是機械性的工作,有了扁鵲智能管理平台,當出現故障的時候就可以自動分析故障的原因,舉個例子,可以找出是因為什麼導致SQL變慢了,或者又是因為什麼原因發生了主備切換,突然IO異常了或者其他什麼原因導致機器故障。

此外,扁鵲智能DBA平台還有一個智能診斷系統,可以定期由DBA發起對實例進行的診斷。比如有些數據庫實例,CPU常年跑得很高,其實是一些比較差的SQL導致的。這個時候扁鵲智能DBA系統,可以很方便地到用戶實例上做巡檢,得到一個健康狀況圖,並對它進行打分,發現這個實例比如他的CPU超用了,需要擴容,但是沒有擴容,就會減分;然後其他表的索引沒有建好,要減分……以此生成一個診斷報告。所以,有了扁鵲,再加上赤兔運營管理平台,DBA的工作其實是非常輕鬆的,可能每天只需要點幾下按紐,然後就解決了一系列的麻煩,包括高可用,性能分析,鎖分析等,完全把DBA從繁雜的工作中解放出來。

此外,我們看到這裡其實還有幾個小的模塊。調度系統,調度系統主要是負責整體的資源調度,比如說數據庫實例的增加刪除、過期作廢,還有一些容量的調度,即擴容、縮容,還有一些多租戶的管理。也就是說這是整個管理台的調度器。

另外還有一個備份系統,這個是冷備中心,後面有一個專門的章節去講,這裡就不再贅述。此外,我們還提供了一些服務模塊作為輔助,比如審計,還有數據庫之間的遷移服務——我們TDSQL怎麼能夠幫助異地數據庫遷進來,或者從TDSQL再遷出。此外,還包括數據校驗、數據訂閱、SQL防火牆、注入檢測等安全方面的模塊,以及一個輔助模塊——幫助我們的DBA也好,用戶也好,完成一些個性化的豐富的需求。

以上是TDSQL系統總覽。

2.TDSQL架構模塊及其特性

我們再看一下核心架構。核心架構其實是上一個圖的縮覽,我們把核心的模塊挑選出來。

首先用戶的請求通過負載均衡發往SQL引擎。然後,SQL引擎作為計算接入層,根據這個SQL的要求從後端的存儲節點去取數據。當然,無論是SQL引擎還是後端的數據庫實例都存在一個元數據來管理調度。舉個例子,計算引擎需要拿到一個路由,路由告訴SQL引擎,這個SQL該發往哪一個後端的數據節點,到底是該發往主節點還是發往備節點。所以我們引入了ZK(Zookeeper)來儲存類似於路由這類元數據信息。當然ZK只是靜態的存儲元數據,維護和管理這些元數據信息,還需要有一套調度以及接口組件,這裡是OSS、Manager/Schedule。所以我們這張圖可以看到是TDSQL整體來說就分為三部分:管理節點、計算節點和存儲節點。當然這裡還有一個輔助模塊,幫助完成一些個性化需求的,比如備份、消息隊列,數據遷移工具等。另外,這裡的負載均衡其實不是必需的,用戶可以選用自身的硬件負載,也可以用LVS軟負載,這個負載均衡根據實際的用戶場景可自定義。

了解了整體架構以後,我們繼續再看一下每個節點的特性是什麼、對機器的依賴程度如何,要求機器有哪些特性,等等。

2.1 管理模塊:輕鬆通過web界面管理整個數據庫後台

首先,我們要看的是管理模塊。作為一個集群只搭建一套的管理模塊,一般可以復用一組機器。同時,管理模塊對機器的要求相對來說比較低,比如資源緊張的時候,我們用虛擬機就可以代替。在我們內部,一套管理模塊承載最大的管理單集群近上萬實例。

管理模塊包含前文說的幾個關鍵模塊:Zookeeper(ZK)、Scheduler、Manager、OSS和監控採集程序、赤兔管理控制台。那麼它們是怎麼聯合工作的呢?首先,DBA用戶在赤兔管理台——這一套WEB前台發起一個操作——點了一個按紐,這個按紐可能是對實例進行擴容,這個按紐會把這個https的請求轉移到OSS模塊,這個OSS模塊有點像web服務器,它能接收web請求,但是它可以把這個轉發到ZK。所以,OSS模塊就是一個前端到後台的橋樑,有了OSS模塊,整個後台的工作模塊都可以跟前台、跟web界面綁定在一起。

好,捕捉到這個請求之後,在ZK上創建一個任務節點,這個任務節點被調度模塊捕獲,捕獲之後就處理任務。處理完任務,再把它的處理結果返回到ZK上。ZK上的任務被OSS捕獲,最後也是https的請求,去查詢這個任務,最後得到一個結果,返回給前端。

這是一個純異步的過程,但是有了這套管理模塊,讓我們可以輕鬆的通過web界面去管理整個TDSQL的後台。當然,這整個過程都有一個監控採集模塊去採集,對整個流程的審計及狀態進行獲取。

2.2 DB模塊:數據庫無損升級

DB模塊,即數據節點,數據存取服務屬於IO密集型的服務,因此,數據節點也是我們的存儲節點,它對IO的要求比較高,一般建議配置SSD硬盤,最好是PCI-E的SSD。因為對數據庫服務來說,CPU再高,如果IO跟不上,仍然是小馬拉大車。比如只有1千的IOPS,CPU根本就跑不起來,用不起來。所以這裡一般建議至少IPS要達到1萬以上。

我們再看一下SET的概念。SET就是數據庫實例,一個SET包含數據庫的——比如我們默認要求的是一主兩備,一個Master節點和兩個Slave節點。當然在DB節點上有一個Agent的模塊。MySQL在執行中,我們要監控它的行為,以及進行操作。如果把這些東西做到MySQL裏面為什麼不可以呢?這其實存在一個問題,如果對數據節點進行升級,可能就要涉及到重啟,一旦重啟就影響用戶的業務,影響服務。這個時候我們就考慮,在它上面加一個模塊Agent,它來完成對所有集群對MySQL的操作,並且上報MySQL的狀態。有了它之後,對TDSQL數據節點的大部分升級,都會轉變為對Agent的升級,而升級Agent,對業務沒有任何影響,這就實現了無損升級。相比於Agent我們對數據節點MySQL不會頻繁升級,一般情況下一年、半年都不會動它。這是我們DB模塊,也是存儲節點。

2.3 SQL引擎模塊:分佈式複雜SQL處理

接下來再看另外一個比較重要的模塊:SQL引擎模塊。SQL引擎處於計算層的位置,本身屬於CPU密集型,所以我們在選機型上盡量要求CPU高一些。其次是內存,作為計算接入層,它要管理鏈接,如果是大量的短鏈接或者長鏈接,非常占內存,所以它對CPU和內存的要求比較高。此外,它本身不存儲數據,也沒有主備之分,所以對硬盤沒有太大要求。

我們看一下SQL引擎的特性。SQL引擎首先還是從ZK上拉取到元數據,作為SQL引擎,包括權限校驗、讀寫分離,以及統計信息、協議模擬等相關的操作。

可能有些人會問,其實這個SQL引擎豈不是一種中間件?其實並不是這樣,SQL引擎如果是一個中間件,它都可以脫離MySQL。但是我們這個SQL引擎,需要做詞法、語法分析,以及作為查詢引擎等工作。而且在分佈式的場景下,SQL引擎複雜的功能性就會凸顯,比如要處理分佈式事物,還要維護全局自增字段,保證多個數據、多個存儲節點共享一個保證全局自增的序列;如果是分佈式的話,要限制一些語法,包括詞法和語法的解析;還有在一些複雜計算上,它還要做一些SQL下推,以及最後數據的聚合。所以SQL引擎還是一個相對來說比較複雜的模塊,作為計算層,並不是一個簡單的中間件那麼簡單。這就是一個SQL引擎。

三、TDSQL金融級特性之:數據強一致性保障

前面我們了解了TDSQL的整體架構和核心特性。接下來我們要重點聊一聊它最重要的特性——作為金融場景下不可或缺的數據強一致性的保障。我們將從四個方面來聊一聊數據一致性的保障:

1.主備數據複製方式

2.數據複製比較:TDSQL主備數據複製方案 VS MySQL原生方案

3.核心功能:容災切換,數據強一致、0丟失0出錯

4.數據強一致性

  1. TDSQL主備數據複製:高性能強同步

首先在講數據一致性之前,我們先了解一下MySQL原生的數據複製的方式。

首先第一種是異步複製:主機在不等從機應答直接返回客戶端成功。這個在金融場景是不能接受的,這樣的話相當於數據是沒有多副本保障。

第二種是半同步:主機在一定條件下等備機應答,如果等不到備機應答,它還是會返回業務成功,也就是說它最終還會退化成一個異步的方式,這同樣也是金融場景所不能接受的。

除此之外,原生半同步其實是有一個性能方面的缺陷,即在跨IDC網絡抖動的場景下,請求毛刺現象很嚴重。所以原生的異步複製和半同步複製都存在一些問題,並不能完全適應金融場景。

TDSQL引入了基於raft協議的強同步複製,主機接收到業務請求後,等待其中一個備機應答成功後才返回客戶端成功。比如這張圖,我們一主兩備下一條業務請求到達了主機之後必須等其中一個備機應答成功,才能返回客戶端成功,否則這個請求是不會應答的。所以說,強同步是TDSQL最基礎的一個特性,是TDSQL保證數據不會丟、不會錯的關鍵。

講到這裡的話,可能有些同學會問,你們這個強同步其實也不複雜,不就是在半同步的基礎上把這個超時時間改成無限大同時應答的備機設置為1。並不是這樣的,TDSQL強同步這裡的關鍵不是在解決備機應答的問題,而是要解決這種增加了等待備機的機制之後,如何能保證高性能、高可靠性。換句話說,如果在原生半同步的基礎上不改造性能,僅把超時時間改成無限大的時候,其實跑出來的性能和異步比甚至連異步的一半都達不到。這個在我們看來也是無法接受的。相當於為了數據的一致性犧牲了很大一部分性能。

TDSQL強同步複製的性能是在原生半同步的基礎上做了大量的優化和改進,使得性能基本接近於異步。

所以這裡強同步強調的是,實現強同步的同時還具備高性能特性,所以確切地說是一個高性能的強同步。

那麼我們如何實現高性能的強同步?我們繼續往下看。這裡TDSQL對MySQL主備複製的機製做了改造。首先,我們先引入了一個線程池的模型。

原生的MySQL是——一個互聯請求是一個線程,這樣對操作系統的資源消耗還是很大的:5千個連接,5千個線程。那1萬個連接,1萬個線程,操作系統能扛得住嗎?肯定扛不住。而且原生的數據複製的方式,其實串行化比較高,比如說一個用戶請求發過來後,等備機應答;這個過程中用戶線程在這裡是完全不能做事的,只有等備機應答之後,它才能夠返回前端。也就是說大量的線程都處於一個等待的狀態,這是半同步效率低的根本原因。

引入了線程池模型之後,我們還需要考慮怎麼調度線程池。我們希望MySQL維護一小部分工作的線程,比如說有1萬個用戶連接,真正幹活的可能就那麼100個、50個MySQL的工作線程。如果是非工作的線程,他在等IO應答時可以先去睡眠,並不讓它去影響我們的吞吐量。所以TDSQL對原生的複製做了改造,除了引入線程池模型,還增加了幾組自定義的線程。這個線程是做什麼呢?當一筆用戶請求過來之後完成了寫操作以及刷新了binlog,第二步他應該要等備機應答。這個時候被我們新引入的線程組所接管,把這個用戶對話保留下來,釋放了工作線程,工作線程該幹什麼繼續幹什麼,處理其他的用戶連接請求。真正備機給了應答之後,再由另外一組線程將它喚醒,回吐到客戶端,告訴客戶端這個應答成功了。

所以經過這樣一個異步化的過程,相當於把之前串行的流程異步化了,這樣就達到了接近於異步複製的性能,這就是TDSQL在強同步的改造的核心。所以我們這裡強調不單單是一個實現強同步的過程,更是一個接近異步性能的強同步。

再看一下改造之後的性能對比:異步TPS大概是6萬左右,平均時耗小於等於10毫秒。再看半同步,明顯有三分之二的性能損耗,並且這個時耗波動還是比較大的,比如說IDC網絡抖動一下。強同步一主兩備模式下,首先性能已經接近於異步的性能,此外時耗並沒有額外增加。

因為一主兩備,除非兩個機房網絡同時抖動,否則的話強同步的時耗不會有明顯波動。所以我們看到基於TDSQL的強同步實現了數據的多副本又保證了性能。

有了這個多副本保障,又怎麼如何實現故障自動切換,數據不丟不錯呢?這其實還是需要一套切換選舉的流程。這就引出了TDSQL的容災切換功能。

2.自動容災切換:數據強一致、0丟失0出錯

自動容災切換在有了強同步特性的基礎,就變得非常容易實現了。我們先看一下這個結構圖:

SQL引擎將請求發給主節點,主節點被兩個備機所同步,每個節點上都有對應的Agent上報當前節點的狀態。這時,主節點發生了故障被Agent覺察,上報到zk被Scheduler捕獲,然後Scheduler首先會把這個主節點進行降級,把它變成Slave。也就是說此時其實整個集群裏面全是Slave,沒有主節點。這個時候另外兩個存活的備機上報自己最新的binlog點,因為有了強同步的保障,另外兩個備機其中之一一定有最新的binlog,那麼兩個備機分別上報自己最新的點後,Schedule就可以清楚的知道哪個節點的數據是最新的,並將這個最新的節點提升成主節點。比如說發現Slave1的數據是最新的,這個時候Schedule修改路由,把主節點設置為Slave1,完成了主備的切換。有了前述這個切換機制,保證了整個切換無需人為干預,並且切換前與切換後的數據是完全一致的。這裡做一個總結,正是由於強同步的保證,所以當主機發生故障的時候,我們一定是可以從一個備節點上找到最新數據,把它提成主節點。這就是TDSQL容災切換的整個過程,容災切換需要建立在強同步的基礎上。

3.極端場景下的數據一致性保障

聊完了容災切換,我們再聊一聊故障節點的後續處理事宜。故障處理可能有幾種情況:一種是故障以後,它還能活過來。比如說像機房可能突然掉電了,掉完電之後它馬上又恢復。或者機器因為硬件原因不小心發生了重啟,重啟完之後節點是可以被拉起,被拉起之後,我們希望它能夠迅速的再加入到集群中,這時數據其實是沒有丟沒有錯的,我們不希望把節點故障之後又重新建一份數據,把之前的數據全抹掉。而是從它最後一次同步的數據為斷點,繼續續傳後面的數據。帶着上述問題,我們看一下節點的故障後恢復過程。

首先我們考慮一種場景,比如說A節點作為主節點,B、C是從,正常去同步A節點的數據,A+1、A+2,接下來該同步A+3。當A+3還沒有同步到從節點的時候發生了故障,這個時候根據B、C節點的數據情況,C的數據是最新的,因此C被選成了主節點,進而C繼續同步數據到B。過了一陣,A節點拉起了,可以重新加入集群。重新加入集群之後,發現它有一筆請求A+3還沒有來得及被B、C節點應答,但已經寫入到日誌。這個時候其實A節點的數據是有問題的,我們需要把這個沒有被備機確認的A+3的回滾掉,防止它將來再同步給其他的節點。所以這裡強調的是一個數據的回退,相當於每一個Slave新加入節點的時候,我們都會對它的數據進行檢驗,將多寫的數據回滾。當然剛剛的假設是運氣比較好,A節點還能重啟。有些時候A節點可能就掛了,這個機器就再也起不來了,我們需要把這個節點換掉,即換一個新機器,比如:加一個D節點。我們希望它快速重建數據並且對當前線上業務儘可能無影響。如何讓它快速去重建呢?當然是基於物理拷貝,而且它是從備機上去拉數據,這樣它既不會對主節點產生影響,又能夠快速把數據重建好。

四、分佈式TDSQL的實踐

第四部分,我們開始講分佈式TDSQL的實踐。前三章我們在聊TDSQL的高可用、強一致。這些作為金融場景是一個必備的特性,還沒有涉及到分佈式,當涉及到分佈式時就開啟了TDSQL的另外一種形態。接下來我們就聊一聊分佈式TDSQL跟單節點的TDSQL有什麼不同,以及這種分佈式架構下又是如何實現一系列的保障,同時如何做到對業務透明、對業務無感知。

1.分表

分表,當在單機模式下,用戶看到的一張邏輯表,其實也是一張物理表,存儲在一個物理節點(物理機)上。在分佈式形態下,用戶看到的邏輯表的實際物理存儲可能是被打散分佈到不同的物理節點上。所以TDSQL分表的目標,希望做到對業務完全透明,比如業務只看到一個完整的邏輯表,他並不知道這些表其實已經被TDSQL均勻拆分到各個物理節點上。比如:之前可能數據都在一台機器上,現在這些數據平均分佈在了5台機器上,但用戶卻絲毫沒有覺察,這是TDSQL要實現的一個目標——在用戶看來是完全的一張邏輯表,實際上它是在後台打散了的。

這個表在後台如何去打散,如何去分佈呢?我們希望對用戶做到透明,做到屏蔽,讓他不關心數據分佈的細節。怎麼將這個數據分佈和打散呢?這就引出了一個概念:shardkey——是TDSQL的分片關鍵字,也就是說TDSQL會根據shardkey字段將這個數據去分散。

我們認為,shardkey是一個很自然的字段,自然地通過一個字段去將數據打散。舉個例子,騰訊內部我們喜歡用QQ號作為一個shardkey,通過QQ號自動把數據打散,或者微信號;而一些銀行類的客戶,更喜歡用一些客戶號、身份證號以及銀行卡號,作為shardkey。也就是說通過一個字段自然而然把這個數據分散開來。我們認為引入shardkey後並不會增加額外的工作,因為首先用戶是最了解自己得數據的,知道自己的數據按照什麼字段均勻分佈最佳,同時給用戶自主選擇分片關鍵字的權利,有助於從全局角度實現分佈式數據庫的全局性能最佳。

所以這裡可能有些人會想,是不是主鍵是最好的或者儘可能地分散?沒錯,確實是這樣的,作為TDSQL的分片關鍵字越分散越好,要求是主鍵或者是唯一索引的一部分。確定了這個分片關鍵字後,TDSQL就可以根據這個分片關鍵字將數據均勻分散開來。比如這張圖,我們按照一個字段做了分片之後,將1萬條數據均勻分佈在了四個節點上。

既然我們了解了shardkey是一個分片關鍵字,那怎麼去使用呢?這裡我們就聊聊如何去使用。

舉個例子,我們創建了TB1這個表,這裡有若干個字段,比如說ID,從這個名字上來看就應該知道它是一個不唯一的,或者可以說是一個比較分散的值。我們看到這裡,以「ID」作為分配關鍵字,這樣六條數據就均勻分散到了兩個分片上。當然,數據均勻分散之後,我們要求SQL在發往這邊的都需要帶上shardkey,也就是說發到這裡之後可以根據對應的shardkey發往對應的分片。如果不帶這個shardkey的話,它不知道發給哪個分片,就發給了所有分片。所以強調通過這樣的改善,我們要求儘可能SQL要帶上shardkey。帶上shardkey的話,就實現了SQL的路由分發到對應的分片。

講完數據分片,我們再看一下數據的拆分。

2.水平拆分

對於分佈式來說,可能最初我們所有的數據都在一個節點上。當一個節點出現了性能瓶頸,需要將數據拆分,這時對我們TDSQL來說非常簡單,在界面上的一個按紐:即一鍵擴容,它就可以將這個數據自動拆分。拆分的過程也比較容易理解,其實就是一個數據的拷貝和搬遷過程,因為數據本身是可以按照一半一半這樣的劃分的。比如最先是這麼一份數據,我們需要拆成兩份,需要把它的下半部分數據拷到另外一個節點上。這個原理也比較簡單,就是一個數據的拷貝,這裡強調的是在拷貝的過程中,其實業務是不受任何影響的。最終業務只會最終有一個秒級凍結。

為什麼叫秒級凍結?因為,最後一步,數據分佈到兩個節點上涉及到一個路由信息變更,比如原來的路由信息要發到這個分片,現在改了之後需要按照劃分,上半部分要發給一個分片,下半部分發給另一個分片。我們在改路由的這個過程中,希望這個數據是沒有寫入相對靜止的。

當然改路由也是毫秒級別完成,所以數據拆分時,真正最後對業務的影響只有不到1s,並且只有在最後改路由的凍結階段才會觸發。

講完數據拆分,我們開始切入分佈式裏面最難解決的這個問題,分佈式事務。

3.健壯、可靠的分佈式事務

單節點的事務是很好解決的,但是在分佈式場景下想解決分佈式事務還是存在一定的困難性,它需要考慮各種各樣複雜的場景。

其實分佈式事務實現不難,但首要是保證它的健壯性和可靠性,能應對各種各樣的複雜場景。比如說涉及到分佈式事務的時候,有主備切換、節點宕機……在各種容災的測試環境下,如何保證數據總帳是平的,不會多一分錢也不會少一分錢,這是分佈式事務需要考慮的。

TDSQL分佈式事務基於拆的標準兩階段提交實現,這也是業內比較通用的方法。我們看到SQL 引擎作為分佈式事務的發起者,聯合各個資源節點共同完成分佈式事務的處理。

分佈式事務也是根據shardkey來判斷,具體來說,對於SQL引擎讀發起一個事務,比如第一條SQL是改用戶ID為A的用戶信息表。第二條SQL是插入一個用戶ID為A的流水表,這兩張表都以用戶ID作為shardkey。我們發現這兩條SQL都是發往一個分片,雖然是一個開啟的事務,但是發現它並沒有走分佈式事務,它實際還是限制在單個分片裏面走了一個單節點的事務。當然如果涉及到轉帳:比如從A帳戶轉到B帳戶,正好A帳戶在第一個分片,B帳戶是第二個分片,這樣就涉及到一個分佈式事務,需要SQL引擎完成整個分佈式事務處理。

分佈式事務是一個去中心化的設計,無論是SQL引擎還是後端的數據節點,其實都是具備高可用的同時支持線性擴展的設計。分佈式事務比較複雜,單獨講的話可能能講一門課,這裏面涉及的內容非常多,比如兩級段提交過程中有哪些異常場景,失敗怎麼處理,超時怎麼處理,怎麼樣保證事務最終的一致性等等。這裡不再深入,希望有機會能單獨給大家分享這塊內容。

所以,這裡只對分佈式事務做一個總結,我們不再去探討它的細節:

首先是基於兩階段提交,我們在MySQL原生XA事務的基礎上做了大量的優化和BUG修復。比如說原生的XA在主備切換時會發生數據不一致和丟失,TDSQL在這個基礎上做了大量的修復,讓XA事務能夠保證數據一致性。

第二個是強勁的性能。起初我們引進原生分佈式事務的時候,分佈式事務的性能還達不到單節點的一半。當然經過一系列的優化調優,最後我們的性能損耗是25%,也就是說它能達到單節點75%的性能。

第三個是對業務透明,因為對業務來說其實根本無需關心到底是分佈式還是非分佈式,僅需要按照正常業務開啟一個事務使用即可。

第四個是完備的異常容錯。分佈式事務是否健壯也需要考慮容錯性的能力。

第五個是全局的鎖檢測。對於分佈式環境下鎖檢測也是不可或缺的。TDSQL提供全局視角的分佈式死鎖檢測,可清晰查看多個分佈式事務之間的鎖等待關係。

第六點是完全去中心化。無論是SQL引擎還是數據節點,都是支持高可用並且能夠線性擴展。

以上是TDSQL分佈式事務的總結。如果說用戶要求保持跟MySQL的高度兼容性,那可能Noshard版TDSQL更適合。但是如果對於用戶來說,單節點已經達到資源的瓶頸,沒有辦法在單節點下做數據重分佈或者擴容,那必須選擇Shard模式。但是在Shard模式下,我們對SQL有一定的約束和限制,後面會有專門的一門課去講分佈式TDSQL對SQL是如何約束的。

我們看到無論是Noshard還是Shard,都具備高可用、數據強一致、自動容災的能力。

同時TDSQL也支持Noshard到Shard的遷移,可能早期我們規劃的Noshard還可以承載業務壓力,但是隨着業務的突增已經撐不住了,這個時候需要全部遷到Shard,那麼TDSQL也有完善的工具幫助用戶快速進行數據遷移。

五、TDSQL數據同步和備份

接下來是TDSQL另外一個輔助特性:數據同步和備份。

1.TDSQL數據同步組件

數據同步的重點分為三個場景:

第一個場景是一個數據匯總。比如,多個數據庫實例的數據同步到一個數據庫實例,如保險行業用戶多喜歡將全國多個區域數據庫實例的數據同步到全國總庫進行統計分析。

第二個就是跨城的容災。跨城容災,一般一個城市的分佈式數據庫的數據需要同步到另外一個城市異構的分佈式數據庫中做災備。有的時候我們要做異構數據庫的跨城容災,比如說主城是一個十六節點的數據庫,它非常龐大。但是備城,可能我們基於成本考慮,選用的設備數量、機房都要差一些。比如災備實例只有兩個物理分片。一個是兩分片數據庫實例,而另外一個時十六分片的數據庫實例。從十六分片同步到;兩分片,這是一個異構的數據庫的同步,這時候我們就需要利用數據同步這個組件。

第三個是遷移。異構數據庫的遷移,將數據從TDSQL同步到MySQL、Oracle、PostgreSQL等數據庫。當然,從TDSQL到TDSQL是一種同步方式,更有一種是TDSQL到其他異構數據庫。舉個例子,張家港農商行,它的核心系統需要從傳統的國外商用數據庫替換為TDSQL,可能還是需要做一定的風險的防範。最終我們給了一套用Oracle作為TDSQL災備示例的方案,通過數據同步組件,將TDSQL的數據准實時同步到Oracle。假如在極端情況下需要將業務切到Oracle,我們也是有這個能力的。

當然數據遷移也體現了TDSQL開放的思想,既然允許用戶將數據遷移到TDSQL,如果有一天用戶可能覺得TDSQL不是很好,覺得有更好的產品可以替代它,TDSQL支持用戶把數據遷走。

2.TDSQL數據備份

最後我們聊一聊TDSQL的備份。

TDSQL支持在線的實時熱備,同時這個備份是基於備機上做的備份,備份支持鏡像和binlog的備份。鏡像又支持物理鏡像和邏輯鏡像(也叫物理備份和邏輯備份)。

物理備份的好處是速度快,直接操縱物理文件,缺點是只能備份整個數據庫實例,無法選擇指定庫表。邏輯備份的好處是通過SQL的方式備份,它可以選擇單個庫表備份,但是如果對整個實例備份效率不及物理備份。比如說有1T的數據,只有100兆是我的關鍵的數據,如果為了節省存儲空間就沒有必要用物理備份,就用邏輯備份,只備份我們關心的庫表。

有了物理備份和邏輯備份之後,基於數據的鏡像,再結合binlog輕鬆實現數據的定點恢復。對於binlog的備份TDSQL的Agent模塊完成准實時的異步備份。比如說我們每天的0點備份鏡像,同時各個時間段的binlog准實時備份。當需要恢復到一個早上6點的數據,利用0點的數據鏡像再結合0點到6點的binlog,即可完成6點的數據恢復。

備份是在備機上做不會影響主機。整個備份過程也是有監控有告警,實現整個備份過程的追蹤。

因為架構這一塊內容確實是比較多,本次也作為所有TDSQL系列分享的一個前瞻,後面更多的系列分享會根據這門課的部分章節詳細展開。這次分享主要是想幫助大家了解TDSQL的整體架構和模塊劃分,以及它的關鍵特性是如何設計,是基於什麼樣的考慮。聽完本次分享再聽後面的專題,會更容易去理解。

好的,以上就是今天分享的全部內容,謝謝大家!

六、Q&A

Q:TDSQL 1.0版本沒有SQL引擎模塊吧?

A:最早在2002年的時候我們使用單機版MySQL作為數據存取服務,而後衍生出了TDSQL,這種計算存儲相分離的架構,進而引入SQL引擎。

Q:請問存儲節點的MySQL是使用官方原生的么 ?

A:TDSQL在原生MySQL基礎上做了大量調優,如線程池、強同步的優化,以及安全限制,分佈式事務XA優化等等。

Q:銀行核心要做到分庫分表,開發的聚合查詢如何實現?

A:SQL引擎屏蔽了分表的細節,讓業務在邏輯上看到的和單節點模式一下一樣,仍然是一張獨立的庫表。此外,SQL引擎會自動做數據聚合,業務開發不需要關心。

Q:a+3,如果掉失了,B和C節點都沒有同步過來,怎麼辦?A機器已經已經無法恢復。

A:a+3如果沒有被B,C確認,即不滿足被多數派確認,是不會應答給業務成功的,最終會以超時的錯誤返回給業務。如果A機器無法恢復,這時新加入節點會通過物理拷貝方式最快速度「克隆」出一個備節點繼續代替A節點提供服務。

Q:Shard版本的可以通過pt工具,或者gh-ost加字段不?會有什麼限制不?

A:TDSQL管理台提供online ddl的功能,會自動對多分片做原子性變更,不需要業務再用第三方工具;分佈式TDSQL在做DDL的時候不允許調整shardkey字段,比如原來以id作為shardkey,現在要調整成name作為shardkey,這樣是不允許的。

Q:TDSQL Shard算法有幾種?建表語句必須需要修改語法嗎?

A:TDSQL shard算法對業務屏蔽,即基於MySQL分區表做的hash拆分(該算法不允許用戶修改),這麼做也是為了對業務屏蔽TDSQL分表細節。這裡並不是限制用戶只能做基於hash的分區,用戶可以在TDSQL-shard的基礎上做二級分區(比如:按照日期、時間)。建表以及使用方面的語法,後面有一門關於分佈式開發的課程,敬請期待。

Q:誰來解決zk的可靠性?

A:一方面,zk自身做了高可用跨機房部署,同時奇數個zk部署當發生故障時只要剩餘存活zk數量大於集群zk的一半,zk就可以繼續提供服務;另外一方面,就算zk節點全部宕機,各個模塊自身對zk也不是強依賴的,即zk在不工作的情況下,數據庫實例的正常讀寫請求不會受到任何影響,只是不能處理切換、擴容等調度相關的觸髮式操作。

Q:老師剛才講兩種模式,如果是用Shard模式,應用層對Sql語法有要求?是我聽錯了嗎?

A:兼容MySQL 99%的SQL語法,shard模式與noshard模式最主要的區別是shardkey的引入。引入shardkey之後,為了發揮出shard模式下的性能優勢,建議所有sql都帶shardkey訪問,同時在shard模式下,一些數據庫的高級特性如:存儲過程、觸發器、視圖等特性會受到一定的使用限制。更詳細的內容後面會有專門的分佈式開發的課程會專門介紹。

Q:分支到總行數據同步匯總,Oralce 同步到TDSQL是雙向都支持么?

A:支持雙向同步。這裡的支持是有條件的,從TDSQL到Oracle是可以做到准實時同步,但是從Oracle到TDSQL目前還無法做到准實時同步,後續會支持。

Q:分佈式表和非分佈式表如何做join?

A:分佈式TDSQL下存在兩種表分別是大表和小表,大表就是shard表(分佈式表),小表又叫廣播表,會冗餘到所有數據節點上。分佈式表和非分佈式表做join時,由於分佈式表所在的物理節點上存在非分佈式表的一份冗餘,因而可以在單個數據節點上完成join。

Q:是否支持自建私有雲?如果公有雲,成本會不會上升?

A:支持私有雲的,TDSQL大部分的銀行客戶都是採用私有雲部署模式,並和外網隔離。公有雲的成本相比私有雲明顯是前者更低,像私有雲需要自建機房,搭建光纖設備,但是好處是獨佔物理硬件資源。公有雲的話都是和公有雲上的其他用戶公用一套IDC、網絡環境。

Q:是否支持K8S部署?

A:TDSQL自帶了一鍵部署解決方案,不依賴K8S。

Q:分局分表支持大表關聯查詢嗎?

A:支持的

//下期預告//