當前位置:
首頁 > 科技 > 支持自動水平拆分的高性能分布式資料庫TDSQL

支持自動水平拆分的高性能分布式資料庫TDSQL

作者 | 張文


責編 | 仲培藝


隨著互聯網應用的廣泛普及,海量數據的存儲和訪問成為系統設計的瓶頸問題。對於大型的互聯網應用,每天幾十億的PV無疑對資料庫造成了相當高的負載。給系統的穩定性和擴展性造成了極大的問題。通過數據的切分來提高系統整體性能,擴充系統整體容量,橫向擴展數據層已經成為架構研發人員首選的方式。


2004年,騰訊開始逐步上線互聯網增值服務,業務量開始第一次爆炸。計費成為所有業務都需要的一個公共服務,不再是某個服務的專屬。業務量的爆炸給DB層帶來了巨大的壓力,原來的單機模式已經無法支撐。伴隨計費公共平台的整合建設,在DB層開始引入分庫分表機制:針對大的表,按照某個key預先拆成n個子表,分布在不同的機器節點上。邏輯層在訪問DB時,自己根據分表邏輯將請求分發到不同的節點。在擴容時,需要手工完成子表數據的搬遷和訪問路由的修改。DB層在業務狂潮之下,增加各種工具和補丁來解決容量水平擴展的問題。2012年TDSQL項目立項,目標為金融聯機交易資料庫。


TDSQL(Tencent Distributed MySQL,騰訊分布式MySQL)是針對金融聯機交易場景推出的高一致性、分布式資料庫解決方案。產品形態為一個資料庫集群,底層基於MySQL,對外的功能表現上與MySQL兼容。截至2017年,TDSQL已在公司內部關鍵數據領域獲得廣泛應用,其中之一作為Midas(米大師)核心資料庫,經受了互聯網交易場景的考驗。Midas作為騰訊官方唯一數字業務支付平台,為公司移動App(iOS、Android、Win phone等)、PC客戶端、Web等不同場景提供一站式計費解決方案。

水平拆分


TDSQL規定shardkey為表拆分的依據,即進行SQL查詢時,shardkey作為查詢欄位指明該SQL發往哪個Set(數據分片)。在分庫分表之前需要Schedule初始化集群,我們這裡稱作一個Group。在初始化Group時要確定最初的分片大小,因而需要確定準備幾套Set。例如,我們需要對邏輯表拆分成四張子表,需要我們在初始化集群時準備四個Set,同時指定每個Set的路由信息,並將這些路由信息寫入ZK,如圖1所示。

支持自動水平拆分的高性能分布式資料庫TDSQL



圖1 TDSQL分庫分表


完成集群初始化後,Proxy監控ZooKeeper中的路由節點,當發現新的路由信息後,更新新的路由到本地。當用戶通過Proxy創建表時,一個建表語句發給Proxy必須指定shardkey,例如create table test_shard(a int, b int) shardkey=a。然後,Proxy改寫SQL,根據路由信息,在最後增加對應的partition clause,然後發到所有的後端Set,如圖2所示。

支持自動水平拆分的高性能分布式資料庫TDSQL



圖2 Proxy建表語句


這樣,就完成了一次建表任務,用戶看到的是一張邏輯表test_shard,但是在後端創建了4個實體表test_shard,後續用戶通過網關進行帶shardkey的增刪改查時,Proxy便會根據shardkey的路由將SQL發往指定Set。

全局自增欄位


在單實例MySQL中,用戶可以通過auto_increment屬性生成一個唯一的值,在分布式資料庫下,利用MySQL的自增屬性,只能保證在一個後端實例內實現自增和全局唯一,無法保證整個集群的唯一。


為了保證整個集群的唯一性,很顯然不能依賴於後端的資料庫,而需要Proxy生成對應的值。同時在實際運行中,Proxy可能有多個,並且可能有重啟等操作,通過Proxy自身也很難做到全局唯一,因此選用了ZooKeeper作為唯一值的生成工具。


通過ZooKeeper的分布式特性,可以保證即使多個Proxy同時訪問,每次只會有一個Proxy能夠成功拿到,使得生成的值是全局唯一。從性能上考慮,不可能每次都與ZooKeeper進行交互獲取,因此每個Proxy每次都會申請一段值,都用完後才會向ZooKeeper進行申請。

支持自動水平拆分的高性能分布式資料庫TDSQL



圖3 全局唯一欄位表創建過程


這種設計方式實現了分布式環境下的自增屬性全局唯一。每個Proxy緩存一定數量的值,並且增加單獨線程負責向ZooKeeper申請值,使得性能影響降到最低,同時具有容災特性,即使Proxy掛了或者重啟,都能保證全局唯一。但是缺點是:多個Proxy一起使用的時候,只能保證全局唯一,不能保證單調遞增。


全局唯一欄位的創建方式和普通的自增欄位一樣:


create table auto_inc(a int auto_increment,b int) shardkey=b;

使用方式也相同:


insert into shard.auto_inc ( a,b,d,c) values(1,2,3,0),(1,2,3,0);


對應的欄位如果賦值為0或者NULL時,由Proxy生成唯一的值,然後修改對應的SQL發送到後端。同時也支持select last_insert_id(),返回上次插入的值,每個線程互相獨立。


分布式JOIN


在分布式資料庫中,數據根據shardkey拆分到後端多個Set中,每個後端Set保存的都只是一部分數據。我們可以方便地在一個Set內做各種複雜的操作,如JOIN、子查詢等。分布式JOIN依賴於網關的語法分析,何為語法分析?簡單來說,語法分析主要做兩方面的事:判斷輸入是否滿足指定的語法規則,同時生成抽象語法樹。對於詞法分析以及語法分析,開源有多種現成的工具,不需要從頭開始做,Linux下用的比較多的是Flex和Bison。

支持自動水平拆分的高性能分布式資料庫TDSQL



圖4 語法分析過程


有了語法分析的支持,對於涉及分布式JOIN的查詢,例如表t1和t2要做JOIN操作,可能使用不同的欄位作為shardkey,這樣根據shardkey路由後,相關記錄可能分布在兩個Set,網關分析後先將數據表t1數據取出,然後再根據t1的shardkey去獲取t2的數據,網關在這個過程中先做語法解析再進行數據聚合,最後返回給用戶結果集。此外,在實際業務中,有一些特殊的配置表,這些表都比較小,並且變動不多,但是會和很多其他表有關聯,對於這類表沒必要進行分片,因此支持一種叫做全局表的特殊表。如果用戶創建時指定是全局表的話(g1),該表全量存放在後端的所有Set中,查詢時隨機選擇一個Set,修改時修改所有Set。如果對全局表進行JOIN的話,就不需要限制條件,即支持select * from t1 join g1。


分布式事務

針對分布式事務,TDSQL採用兩階段提交演算法來實現分布式事務,其中Proxy作為協調者,狀態數據持久化到全局事務管理系統中,目前選用的是TDSQL本身的一個InnoDB表來保存(gtid_log);所有的Group作為參與者來負責具體子事務的執行。

支持自動水平拆分的高性能分布式資料庫TDSQL



圖5 分布式事務


Client向Proxy發送事務


Begin;


Statment1;


Statment2;



Proxy為該事務分配一個ID,並將SQL轉為:

Xa begin 「id」


Statment1;


Statment2;



Client提交事務


Client最終向Proxy發送commit。


Proxy對事務prepare


Proxy向所有參與該事務的Set發送:


Xa end 「id」 標識該事務的結束;


Xa prepare 「id」 mysql將事務計入Binlog,並通過Binlog傳遞給Slave,不同於普通事務,寫入Binlog之後該事務仍然沒有提交;

如果任意Set在 prepare過程中失敗或者超時,由於此時還沒有寫存儲引擎日誌,MySQL自動rollback這個事務,並向Client返回相應錯誤信息。


Proxy對事務commit


當Proxy收到所有Set的prepare響應之後,Proxy更新gtid_log表將對應XID的事務置為commit狀態;Proxy隨後向所有Set發送Xa commit 「id」,Set收到該請求之後提交該事務。


Proxy返回Client OK


Proxy等待所有Set的commit響應,當所有Set返回成功,Proxy返回前台成功。若其中一台返回失敗(當Set發生重啟等故障時,需要等待Agent補提交該事務,因而當前屬於未提交狀態),Proxy返回前台狀態未知,稍後請繼續查詢事務狀態。


當Proxy在第四步寫完commit後,開始逐個Set提交事務,當還沒有完成所有Set提交時Proxy發生宕機,剩餘Set中未提交的事務由Agent來提交,以此來保證事務的一致性。Agent會定期通過命令Xa recover查詢MySQL中處於prepare狀態的事務,再對照gtid-log表查詢該事務是否處於commit狀態,如果是則comimt。否則可能由於prepare成功後寫gtid_log失敗,因而Agent需要將該事務abort。


多種模式的讀寫分離


TDSQL支持三種模式的讀寫分離。第一種模式下網關開啟語法解析的配置,通過語法解析過濾出用戶的select讀請求,默認把讀請求直接發給備機。這種方案的缺點有兩個:1. 網關需要對SQL進行分析,降低整體性能;2. 當主備延遲較大時,直接從備機獲取數據可能會得到錯誤的數據。


除了上述模式,TDSQL支持通過增加Slave注釋標記,將指定的SQL發往備機。即在SQL中添加/slave/這樣的標記,該SQL會發送給備機,即用戶能夠根據業務場景可控地選擇讀寫分離,即使主備延遲比較大,用戶也能夠根據需要靈活選擇從主機還是備機讀取數據,這種模式下網關不需要進行詞法解析,因而相比第一種方案提高了整體性能。但是,這種方案的缺陷是改寫了正常SQL,需要調整已有用戶代碼,成本較高,用戶可能不太願意接受。


表1 三種讀寫分離模式比較

針對前兩種讀寫分離的不足,最新版本的TDSQL增加了基於只讀帳號的讀寫分離模式。這種模式下,由只讀帳號發送的請求會根據配置的屬性發給備機。有兩種可配屬性,IDC屬性和備機延遲屬性。IDC屬性可配置三種屬性:1. 為同IDC屬性,即只讀帳號的請求必須發往同IDC的備機;2. 優先發給同IDC的備機,但當同IDC的備機不存在或宕機時,發往不同IDC的備機;3. 如果找不到滿足條件的備機,則發往主機。延遲屬性:如果延遲超過閥值,認為該備機不可用。只讀帳號能夠在既不改變原有用戶代碼,又不影響系統整體性能的前提下,同時提供多種可配參數解決讀寫分離的問題,更具有靈活性和實用性。


總結


2014年微眾銀行設立之初,在其分布式的去IOE架構中,TDSQL承擔了去O的角色,以TDSQL作為交易的核心DB,承載全行所有OLTP業務。2015年,TDSQL和騰訊雲攜手,正式啟動「TDSQL上雲」的工作,接入了一系列傳統以及新型金融企業,覆蓋保險、證券、理財、諮詢等金融行業。目前,分布式TDSQL正作為騰訊日益重要的金融級資料庫,搭建著上百個實例,部署於上千台機器,日均產生TB級數據,承載著公司內外各種關鍵業務。


在未來,TDSQL重點會在資料庫性能、分布式事務、語法兼容三個方面做改進。目前TDSQL基於MariaDB 10.1.x、Percona-MySQL 5.7.x兩個分支版本,後續我們會緊密跟進社區並及時應用官方補丁,同時不斷針對金融場景的特性對資料庫內核進行優化,以此來提升資料庫性能和穩定性。當前分布式事務處於初級階段,對ZooKeeper的依賴性較強,後續可能針對分布式事務的可靠性做持續改進。由於TDSQL在分表環節對語法層做了一些限制,將來我們希望通過對網關解析器的改進,使其能夠支持更豐富的語法、詞法。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

2022年國外十大技術預測

TAG:CSDN |

您可能感興趣

ONO DAC分散式自治組織 將運營任務拆分並進行公開分發
MySQL 千萬級的大表要怎麼優化(讀寫分離、水平拆分、垂直拆分)
以 Vans 為首的品牌高速增長,母公司拆分牛仔業務
谷歌架構調整:拆分搜索與AI部門,Jeff Dean全面接管AI業務
醫拍智能轉型All in區塊鏈,原AI醫療業務獨立拆分融資
醫拍智能宣布轉型All in區塊鏈,原AI醫療業務將獨立拆分融資
MYSQL包含逗號的欄位拆分查詢
中國移動需要的是「拆分」還是「拆分IPO」?
唯品會自研微服務框架OSP,解決一系列拆分、擴容難題
醫拍智能All in區塊鏈,原AI醫療業務獨立拆分融資
拆分+區塊鏈數字貨幣=拆分模式的新出路 新高度
EXCEL VBA 高階 字典用法集錦及代碼詳解之拆分數據不重複
谷歌拆分搜索和AI業務,Jeff Dean將領導所有AI業務
股權拆分——投資高手是如何煉成的
淺談BAJ金融業務拆分的邏輯
為什麼要把系統拆分成分散式的,為啥要用Dubbo?
在Python中使用PDF:閱讀和拆分
小米拆分重組松果電子 加速AIoT戰略落地
降低AR/VR性能需求,微軟提出動態的多層圖形渲染技術,拆分背景層、場景層、戶界面層等
oracle拆分逗號分隔的字元串