银行核心海量数据无损迁移:TDSQL数据库多源异构迁移方案
為幫助開發(fā)者更好地了解和學(xué)習(xí)分布式數(shù)據(jù)庫技術(shù),2020年3月,騰訊云數(shù)據(jù)庫、云加社區(qū)聯(lián)合騰訊TEG數(shù)據(jù)庫工作組特推出為期3個月的國產(chǎn)數(shù)據(jù)庫專題線上技術(shù)沙龍《你想了解的國產(chǎn)數(shù)據(jù)庫秘密,都在這!》,邀請數(shù)十位鵝廠資深數(shù)據(jù)庫專家每周二和周四晚上在線深入解讀TDSQL、CynosDB/CDB、TBase三款鵝廠自研數(shù)據(jù)庫的核心架構(gòu)、技術(shù)實現(xiàn)原理和最佳實踐等。
本文將帶來直播回顧第五篇《高性能、安全穩(wěn)定、數(shù)據(jù)一致:TDSQL如何實現(xiàn)數(shù)據(jù)庫異構(gòu)遷移》。
點擊圖片收看直播回放
我今天的主題是關(guān)于TDSQL異構(gòu)數(shù)據(jù)同步與遷移能力的建設(shè)以及應(yīng)用方面的內(nèi)容。整個內(nèi)容分四個部分:
一是異構(gòu)數(shù)據(jù)庫方面包括數(shù)據(jù)分發(fā)遷移同步的背景——我們?yōu)槭裁匆l(fā)展這一塊的能力以及現(xiàn)在這部分服務(wù)的基本架構(gòu);
二是TDSQL異構(gòu)遷移能力有哪些比較好的特性,以及在實現(xiàn)這些特性的過程中的難點問題和我們提出的特色的解決方案;
三是結(jié)合TDSQL現(xiàn)在在國產(chǎn)數(shù)據(jù)庫的一些推廣以及應(yīng)用的經(jīng)驗,我們針對在異構(gòu)數(shù)據(jù)遷移或者同步的領(lǐng)域場景最佳實踐,也介紹一些好的用法和場景;
四是針對今天講的內(nèi)容做一個總結(jié)。
事實上,作為國產(chǎn)自研的成熟的分布式數(shù)據(jù)庫產(chǎn)品,TDSQL對內(nèi)穩(wěn)定支撐騰訊海量計費(fèi)業(yè)務(wù),對外開放5年來也通過云服務(wù)為微眾銀行等超過500家金融政企機(jī)構(gòu)提供高性能、高可用、高可靠、強(qiáng)一致的分布式數(shù)據(jù)庫服務(wù)。TDSQL崇尚良性的競爭,也給予客戶強(qiáng)信任的保障:TDSQL具備開放的架構(gòu),不僅支持安全快速的數(shù)據(jù)庫數(shù)據(jù)遷入,同樣支持異構(gòu)數(shù)據(jù)庫遷出。
從客戶的需求角度出發(fā),持續(xù)打磨產(chǎn)品,是我們一貫的原則。當(dāng)然,除了支持?jǐn)?shù)據(jù)庫遷移,多源異構(gòu)遷移方案也支撐數(shù)據(jù)匯總、分發(fā)等業(yè)務(wù)場景,這也是TDSQL具備完善的產(chǎn)品服務(wù)體系的體現(xiàn)。
1.?TDSQL異構(gòu)數(shù)據(jù)遷移分發(fā)的背景及架構(gòu)方案
1.1 TDSQL異構(gòu)數(shù)據(jù)遷移方案的場景
TDSQL作為一個金融級數(shù)據(jù)庫,面對的更多是金融級場景以及金融機(jī)構(gòu)客戶,金融機(jī)構(gòu)往往有一些比較特殊的需求,比如說保險行業(yè),他們基于TDSQL構(gòu)建業(yè)務(wù)時會進(jìn)行一些業(yè)務(wù)劃分,或者基于水平擴(kuò)展的要求,數(shù)據(jù)會落在多個分庫上等,而有時又需要把多個區(qū)域或者多個分庫上的數(shù)據(jù)匯總到一個總庫上進(jìn)行統(tǒng)計分析,TDSQL作為數(shù)據(jù)層的服務(wù)必須具備高性能、準(zhǔn)確可靠的將數(shù)據(jù)實時匯總的能力。也就是說,TDSQL遇到的第一個數(shù)據(jù)庫遷移場景需求就是要支持高速準(zhǔn)確進(jìn)行數(shù)據(jù)匯總的能力。
二是來自跨城容災(zāi)場景的需求。舉個例子,騰訊內(nèi)部金融級業(yè)務(wù)有跨城容災(zāi)的需求,這也是大型互聯(lián)網(wǎng)公司中常見的容災(zāi)級別要求,我們要求業(yè)務(wù)具備城市級別自動快速容災(zāi)切換的能力。比如說在深圳和上海分別有一套數(shù)據(jù)庫并且支撐了相關(guān)業(yè)務(wù)SVR,我們就需要在這兩個城市間的數(shù)據(jù)庫DB之間實現(xiàn)數(shù)據(jù)實時同步,這樣有兩個好處:1、這一套實時同步的東西可以在城市級切換的時候快速地將業(yè)務(wù)切換備城;2、這一套實時同步做到足夠好,比如說實時性更好或者說數(shù)據(jù)準(zhǔn)確度是完全沒有問題的情況下,我們可以做到業(yè)務(wù)的分流,比如有部分的業(yè)務(wù)是在主城的SVR上,當(dāng)我們認(rèn)為主城SVR業(yè)務(wù)量過大或者說壓力太大時候,也可以切換一部分業(yè)務(wù)流量到備城的SVR上,實現(xiàn)兩個城市之間數(shù)據(jù)層的數(shù)據(jù)和數(shù)據(jù)同步。我們遇到第二個需求就是在跨城容災(zāi)或者跨城業(yè)務(wù)分流、跨城數(shù)據(jù)同步上我們需要DB側(cè)有這樣的能力提供給業(yè)務(wù)使用。
三是異構(gòu)的數(shù)據(jù)分發(fā)和遷移。TDSQL作為一個金融級數(shù)據(jù)庫,對外是非常開放的架構(gòu),我們支持將數(shù)據(jù)以各種各樣滿足業(yè)務(wù)的方式同步到外面的平臺,比如當(dāng)有一些業(yè)務(wù)需要在Oracle上跑一些比較老的業(yè)務(wù)或請求等等;也有一些業(yè)務(wù)需要把數(shù)據(jù)同步到消息隊列給下游業(yè)務(wù)使用,比如大數(shù)據(jù)平臺、其他的檢索之類,我們可以通過消息隊列把數(shù)據(jù)抽出來。金融機(jī)構(gòu)往往需要數(shù)據(jù)不僅能夠存進(jìn)來而且能夠很好地按照要求分發(fā)出去,供下游業(yè)務(wù)使用,這也是我們遇到比較重要的需求。
針對上面提到的三種場景——數(shù)據(jù)匯總、跨城容災(zāi)、異構(gòu)數(shù)據(jù)庫間數(shù)據(jù)的分發(fā)和遷移TDSQL針對這些需求構(gòu)建出一套叫做多源同步的系統(tǒng)?,F(xiàn)在介紹一下多源同步的系統(tǒng)是通過什么樣的架構(gòu)來滿足我們提到的三個需求的。
1.2 開放的架構(gòu):TDSQL多源同步方案架構(gòu)解讀
從圖上可以看到有三個組成部分:一是原數(shù)據(jù)的抽取;二是中間的存儲——這是一個消息隊列,三是目標(biāo)實例。這是一個非常典型的CDC架構(gòu),通過獲取源端數(shù)據(jù)源的增量數(shù)據(jù),通過消息隊列,下游消費(fèi)的邏輯將數(shù)據(jù)進(jìn)行分發(fā)。從左邊看到,這一套多源同步,源端支持MySQL,就是對MySQL系列的DB可以獲取它的增量數(shù)據(jù);還有Oracle。。抽取完數(shù)據(jù)增量,比如說binlog日志或者增量數(shù)據(jù)獲取服務(wù)抽取到的數(shù)據(jù),我們會以一個中間格式存放到消息隊列里面;存到消息隊列以后,我們自己實現(xiàn)了一個消費(fèi)邏輯——叫做consumer消費(fèi)者,它可以實現(xiàn)將這一套存到消息隊列的數(shù)據(jù)按照不同的需求以及不同的目標(biāo)端類型將數(shù)據(jù)推送到下游。
目前TDSQL多源同步方案支持的目標(biāo)端類型有下面這幾種:PG, TDSQL, Oracle,還有一部分就是MySQL,另外還可以將增量數(shù)據(jù)再推往另外一個消息隊列,比如說有一些業(yè)務(wù)可能需要將這套增量的數(shù)據(jù)推往業(yè)務(wù)使用的隊列組件里面,我們也是支持這樣做的。
最后,在這一套同步的數(shù)據(jù)鏈路過程中,我們有一個數(shù)據(jù)校驗的服務(wù),包括兩個方面:一是增量校驗,含義就是會實時校驗這一筆數(shù)據(jù)從源端抽取,到它的增量變化,再到寫到目標(biāo)端之后,這筆數(shù)據(jù)落庫落得準(zhǔn)不準(zhǔn)確,是不是在正確的目標(biāo)上寫下這筆數(shù)據(jù);二是存量校驗,可能是一些定時定期去跑批,比如說定期對源和目標(biāo)的數(shù)據(jù)進(jìn)行整體的校驗,我們能夠主動及時地發(fā)現(xiàn)整個數(shù)據(jù)通路上的問題和錯誤。
結(jié)合我們剛剛說的需求,基于數(shù)據(jù)同步的跨城雙活架構(gòu),也是騰訊內(nèi)部現(xiàn)在在使用的架構(gòu)?;跀?shù)據(jù)同步的跨城雙活架構(gòu)是這樣的形式:
首先左邊和右邊代表不同的城市,這里舉例左邊是深圳,右邊是上海。從圖上可以看到,TDSQL在SZ這套實例會將業(yè)務(wù)不斷寫入的增量數(shù)據(jù)源源不斷地寫入本城的消息隊列里面。對城的SH也會將自己業(yè)務(wù)訪問的增量數(shù)據(jù)源源不斷寫到消息隊列里面,同時在各個城市有一套自己的消費(fèi)服務(wù),這套消費(fèi)服務(wù)會拉取對端的增量數(shù)據(jù),也就是說會拉對城的消息隊列里面的增量數(shù)據(jù)進(jìn)行重放,這樣就實現(xiàn)了兩套基于數(shù)據(jù)同步的一套跨城雙活。這個雙活是有前提條件的——就是兩套業(yè)務(wù)在SZ和SH同時寫的時候,它的訪問主鍵一定是分離,在這一套邏輯下面沒有辦法做到同時對同一條主鍵進(jìn)行修改。我們基于跨城的這套雙活架構(gòu)也是要基于主鍵分離的做法。
2 TDSQL 多源同步方案的挑戰(zhàn)和特性
2.1 要求與挑戰(zhàn)
介紹完整體架構(gòu),我們繼續(xù)深入拆解下,這套架構(gòu)所面對的業(yè)務(wù)場景,都有哪些要求?在這些要求實現(xiàn)的過程中是有哪些難點,并且針對這些難點我們是怎么處理的?以下將介紹這其中的特性、難點、解決方案。
一是高性能:對實時性要求比較高的業(yè)務(wù)對數(shù)據(jù)同步的速率有比較高的要求,比如說秒級別等等。但無論如何,在這個互聯(lián)網(wǎng)時代,這套數(shù)據(jù)同步要快,不能說加了這套數(shù)據(jù)同步、異構(gòu)分發(fā)的邏輯以后,它同步的速度非常慢,這肯定是不可以。
二是數(shù)據(jù)強(qiáng)一致性:在快的基礎(chǔ)上,同步的數(shù)據(jù)一定要準(zhǔn)。這套數(shù)據(jù)同系統(tǒng),分發(fā)的系統(tǒng)把數(shù)據(jù)從源端抽出來,往里面寫的過程中,需要做到原來寫出來是什么樣的,目標(biāo)重放就是什么樣的,兩邊的數(shù)據(jù)一致性一定要有保證,這里面就包含了我們?nèi)绾我?guī)避在抽取鏈路、重放鏈路這兩個數(shù)據(jù)鏈路上的錯誤;二是如何保證在異常情況下寫入的數(shù)據(jù)一定是對的。
三是服務(wù)高可用:這一套同步服務(wù),一定是高可用的,體現(xiàn)在兩個方面:1、災(zāi)難的情況下,本身消費(fèi)者的服務(wù)能夠在假如機(jī)器出現(xiàn)一些不可恢復(fù)的故障時能夠及時地感知并且自動遷移和切換;2、要應(yīng)對本身常規(guī)的擴(kuò)容——垂直擴(kuò)容或者水平擴(kuò)容的伸縮性需求,這也是我們比較強(qiáng)調(diào),這一套同步服務(wù)要能夠兼容各種災(zāi)難情況和常規(guī)的運(yùn)維場景下各種各樣的要求來做到服務(wù)的高可用。
接下來就針對上面這些點一個一個來看。
2.2 一致性保障
2.2.1 自動化消息連續(xù)性檢測
從上面的架構(gòu)圖我們可以看出來,整個數(shù)據(jù)鏈路比較長,它要先把增量數(shù)據(jù)拿到,寫到消息隊列里面去;再從消息隊列里面消費(fèi)出來。生產(chǎn)者這一套服務(wù)做的事情就是首先要拿到增量數(shù)據(jù),二是要正確地把拿到的增量數(shù)據(jù)準(zhǔn)確地投遞到消息隊列里面,這里面有兩個問題:1、如何判斷我拿到的消息——本身的增量數(shù)據(jù),是對的;2、我如何確定寫到消息隊列里面,消息隊列存的也是對的。
這就衍生出來兩個方式:一是拿到增量消息的時候,我們會根據(jù)GTID的特性,檢測拿到消息的GTID的連續(xù)性,保證拿到這套增量數(shù)據(jù)的東西一定是準(zhǔn)的,比如說GTID上一個拿到的是345,下一個如果拿到的是348,這個時候系統(tǒng)會認(rèn)為現(xiàn)在拿到這一條GTID跟上一個并不連續(xù),并不連續(xù)的情況下我們就要進(jìn)行容錯處理,比如會向主機(jī)補(bǔ)償或者向其他的節(jié)點切換補(bǔ)償?shù)???偨Y(jié)來說,TDSQL在拿增量消息這部分,是具備連續(xù)性檢測的能力,保證拿到的數(shù)據(jù)一定是準(zhǔn)確、連續(xù)的。
二是系統(tǒng)如何保證寫到隊列里面的數(shù)據(jù)一定是準(zhǔn)?在寫到隊列過程中有可能出現(xiàn)重復(fù)、亂序等情況,TDSQL多源同步方案采用的策略是——利用Kafka本身在寫消息的回調(diào)通知的特性,我們在將消息推到Kafka的時候,會給每個消息賦予一個連續(xù)遞增的序列號,通過Kafka回調(diào)的寫入消息來確定系統(tǒng)寫入的消息是不是有序的。舉個例子,我們按[5,6,7,8,9]這樣的順序向Kafka生產(chǎn)一部分消息(寫),屆時收到的消息回調(diào)序號也應(yīng)該是[5,6,7,8,9];當(dāng)接收完9號這條消息回調(diào)的時候,下一條如果收到的回調(diào)序號是12、或者11,那么就會認(rèn)為從9號往后的消息隊列消息不是一個有序的消息,這時系統(tǒng)會重新從9這條序號往后的消息重新上報kafka,最終保證寫到消息隊列里面的數(shù)據(jù)是沒有空洞并且是連續(xù)遞增的。這是生產(chǎn)者服務(wù)在消息連續(xù)性異常檢測方面我們提供的兩種機(jī)制。
2.2.2 異常自動切換機(jī)制
以上介紹的機(jī)制可以保障多源同步、異構(gòu)遷移中如何檢測到錯誤。那么,檢測到錯誤之后如何處理呢?以下就介紹生產(chǎn)者異常自動切換的機(jī)制、切換的條件。
這里面都以TDSQL的實踐為例:獲取增量日志必須要在一個合適的TDSQL角色上處理,TDSQL本身是一個一主多備的分布式數(shù)據(jù)庫集群,在選擇獲取數(shù)據(jù)庫增量日志的角色上我們選擇從備機(jī)上獲取。
選擇一個合適的備機(jī)對增量數(shù)據(jù)獲取來說是非常重要的。當(dāng)獲取增量日志的備機(jī)的延遲比較大,或者這個備機(jī)本身不存活,或者這個冷備發(fā)生了遷移(什么是冷備?離主機(jī)的距離最近,或者跟主機(jī)的差距最小的備機(jī),我們叫它冷備),這個時候系統(tǒng)就會將解析日志生產(chǎn)者的服務(wù)切換到另外一個節(jié)點,整個切換流程通過MetaCluster服務(wù)協(xié)調(diào)。也就是說當(dāng)工作的數(shù)據(jù)庫節(jié)點本身的狀態(tài)發(fā)生躍遷之后,其他節(jié)點生產(chǎn)者服務(wù)就會通過MetaCluster來感知到狀態(tài)的躍遷,并且適當(dāng)?shù)貑幼约旱姆?wù)——從一臺備機(jī)狀態(tài)躍遷到另外一臺備機(jī),而躍遷前的備機(jī)的生產(chǎn)者服務(wù)會停掉,新的備機(jī)生產(chǎn)者服務(wù)會自動拉起來。這就是它的切換流程——通過MetaCluster進(jìn)行下發(fā)協(xié)調(diào)。
切換是基于什么樣的觸發(fā)條件?現(xiàn)在解析到的這臺備機(jī)本身狀態(tài)是正常的,比如延遲沒問題,存活性也正常,冷備角色一直沒有發(fā)生變化,但是發(fā)現(xiàn)它的binlog不連續(xù)。當(dāng)我們檢測到拿到的這套binlog是不連續(xù)的時候,就可以認(rèn)為這里面可能會出現(xiàn)binlog的丟失,這個時候就要發(fā)起補(bǔ)償?shù)牟僮?。怎么補(bǔ)償呢,通過這個流程給大家介紹一下。
當(dāng)系統(tǒng)發(fā)現(xiàn)解析到這套GTID不連續(xù)了,就會向ZK注冊一個節(jié)點。舉個例子,系統(tǒng)現(xiàn)在已經(jīng)發(fā)現(xiàn)拿到的binlog不連續(xù)了,于是注冊一個補(bǔ)償節(jié)點,包含著“向主機(jī)補(bǔ)償”這樣的信號。當(dāng)主機(jī)檢測到有這樣一個補(bǔ)償節(jié)點時,會將日志解析的角色接管過來并開始工作。
接下來,我們?nèi)绾未_定主機(jī)從哪里開始解析日志?我們會從Kafka上讀取最后一條消息——最后一條消息包含GTID的信息。這時主機(jī)就會把這條消息對應(yīng)的GTID轉(zhuǎn)化成本地的binlog文件名和偏移量開始解析。
主機(jī)的補(bǔ)償需要持續(xù)多長時間?持續(xù)一個文件處理的流程。當(dāng)主機(jī)補(bǔ)償?shù)浇馕龅乃谖募Y(jié)束以后就會退出主機(jī)補(bǔ)償?shù)牧鞒?#xff0c;并將這個角色通過MetaCluster重新下放給備機(jī)的生產(chǎn)流服務(wù),而備機(jī)的生產(chǎn)流服務(wù)接到這個請求以后會重新從Kafka上拉取上一次主機(jī)補(bǔ)償?shù)娜罩局凶詈笠惶紫?。如果說找到了對應(yīng)的GTID,并且往下解析的時候沒有發(fā)現(xiàn)不連續(xù)的情況,這一套補(bǔ)償流程就算結(jié)束,備機(jī)會繼續(xù)在自己的角色上持續(xù)地進(jìn)行增量數(shù)據(jù)生產(chǎn)。
如果發(fā)現(xiàn)從Kafka拉下來的主機(jī)補(bǔ)償日志最后一條本機(jī)找不到,就說明這個主機(jī)的補(bǔ)償不完整,有可能備機(jī)缺了兩三個文件,這個時候會持續(xù)向主機(jī)進(jìn)行補(bǔ)償,通過注冊MetaCluster節(jié)點的方式一直重復(fù)這個流程,直到主機(jī)補(bǔ)償完成,備機(jī)在接管角色的時候能夠連續(xù)順利地接著解析,本身的日志才認(rèn)為這套補(bǔ)償流程已經(jīng)完全結(jié)束——這就是一個通過不斷向主補(bǔ)償日志的方式來進(jìn)行異常的切換的流程。
2.2.3 冪等重放機(jī)制
介紹完生產(chǎn)這套鏈路之后介紹一下下游的鏈路——消費(fèi),消費(fèi)的鏈路中怎么保證數(shù)據(jù)一致性?首先回顧一下剛剛提到的生產(chǎn)端的數(shù)據(jù)一致性保障——生產(chǎn)端在實現(xiàn)消息生產(chǎn)的時候?qū)崿F(xiàn)的是一種at-lease-once的模式進(jìn)行消息生產(chǎn),這里面就要求消費(fèi)服務(wù)必須能夠確地處理消息重復(fù)這個問題,也就是說我們要支持所謂的冪等邏輯。
支持冪等之后有什么好處?在binlog是連續(xù)無空洞的前提下,支持冪等機(jī)制的消費(fèi)服務(wù)可以從任意一個時間點重放binlog消息,當(dāng)重放結(jié)束以后目標(biāo)的數(shù)據(jù)會達(dá)到最終一致,這就是消費(fèi)鏈路實現(xiàn)冪等的動機(jī)和優(yōu)勢。這個機(jī)制實現(xiàn)的難點在于要絕對的可靠——重放一定是要百分百沒有問題,準(zhǔn)確無誤。
基于這樣的要求以及上游數(shù)據(jù)寫到消息隊列里面的現(xiàn)狀,TDSQL以此為設(shè)計的原則,實現(xiàn)保證按照binlog事件本身的意圖對目標(biāo)實例進(jìn)行修改。什么叫做按照binlog事件的意圖去對目標(biāo)進(jìn)行修改呢?
增量數(shù)據(jù)無非就是三個方面:一是insert的寫入,二是更新,三是刪除。
1. insert寫入
在寫入的時候我們是如何做到insert事件冪等呢?一個start進(jìn)來我們要重放insert:
當(dāng)它的影響行數(shù)大于0,我們就認(rèn)為這套insert執(zhí)行成功;如果執(zhí)行失敗,我們認(rèn)為它可能有一些報錯,比如說語法錯誤或者目標(biāo)的字段過小,并進(jìn)行重試的邏輯。
當(dāng)影響行數(shù)等于0,則判定可能會出現(xiàn)主鍵沖突——insert失敗影響行數(shù)為0,這里面唯一的可能就是出現(xiàn)了沖突。出現(xiàn)主鍵沖突的時候這個時候怎么處理?insert這一條數(shù)據(jù)發(fā)生的時候意圖是什么?在insert之前DB里面是沒有insert這條數(shù)據(jù),而當(dāng)這條insert發(fā)生之后,DB里面是有的——按照本身的意圖來做,意味著如果發(fā)生了主鍵沖突或者影響行數(shù)等于0的情況,里面存在一個相同的記錄,這個時候系統(tǒng)會按照insert本身的值拼一個delete操作。這條delete操作下去后,就能保證在這條insert寫入之前,目標(biāo)里面是沒有這條數(shù)據(jù)的;當(dāng)我這條delete做完之后,再把這條insert進(jìn)行插入。這個時候如果影響行數(shù)大一點,可認(rèn)為這條insert被按照本身的意圖做完了。其實也就是說,要保證這條insert做的時候,只有當(dāng)前這一條數(shù)據(jù)——這就是insert本身的冪等。
2. 更新
更新:首先一條update,如果影響行數(shù)大于0,可判定這條執(zhí)行是正常的。如果小于0,則意味著可能出現(xiàn)一些執(zhí)行錯誤,比如語法有問題或者字段長度有問題。
如果它的影響行數(shù)等于0,有兩種可能:一是沒有匹配到——進(jìn)行update時是按照全字段進(jìn)行匹配的,這一行改之前和改之后所有的字段都在這條消息里面,原始更新也會按照所有字段來去拼裝,沒有匹配到則意味著某些字段沒有匹配到,這個時候會按主鍵更新——也就是匹配到這些值可能是全字段,執(zhí)行更新的操作。
如果按照主鍵更新操作,影響行數(shù)還是0的話,則可以判定為出現(xiàn)了主鍵操作的沖突。這個時候系統(tǒng)就會思考一下,這條update它的語義是什么——update的語義是指這條update執(zhí)行完以后,目標(biāo)庫里面第一個是沒有改之前的值,第二個是有且只有改之后的值,所以我們按照這個語義做接下來的操作,按照所有的唯一鍵去構(gòu)造一個刪除的操作,操作完了以后再按照update里面改后的,構(gòu)造一條插入操作,將這條插入操作寫入目標(biāo)DB——如果影響行數(shù)大于0,實際可認(rèn)為這條update就是按照它本身的意圖對目標(biāo)實例進(jìn)行了修改。
3. 刪除
我們來看一下刪除的過程。相對于update來說簡單多了。這個過程中,delete結(jié)束后大于0就成功;小于0就是失敗;等于0的時候我們認(rèn)為它可能沒有匹配到行,這個時候我就按照主鍵操作——因為刪除的操作最終的結(jié)果就是目標(biāo)一定沒有了當(dāng)前刪除的消息主鍵所標(biāo)識的這一行——這條操作完成后,DB里一定沒有這行數(shù)據(jù),因此僅僅是按照主鍵進(jìn)行刪除就可以了。這個時候如果影響行數(shù)大于0,則刪除成功。如果等于0,就認(rèn)為按照主鍵去匹配,本身刪除不到,匹配不到——意思是本身目標(biāo)就沒有這條要刪的主鍵所標(biāo)識的數(shù)據(jù)——所以實際上它的結(jié)果跟要做完刪除的結(jié)果,影響是一樣,也就結(jié)束這一條刪除的冪等。
回顧三種類型的時候,我們比較關(guān)注這條數(shù)據(jù)在執(zhí)行前后的狀態(tài),它執(zhí)行前是什么樣的,執(zhí)行后是什么樣的,我們在重放這條消息的時候,嚴(yán)格按照這個來做,insert就是執(zhí)行前沒有這條數(shù)據(jù),執(zhí)行后有這條數(shù)據(jù),如果遇到?jīng)_突就先刪除后insert,update執(zhí)行后它的結(jié)果,一定沒有改之前的值,有且只有改之后的值,刪除也是一樣,目標(biāo)里面一定沒有主鍵所標(biāo)識的這一行在目標(biāo)實例里面,我們按照這個邏輯設(shè)置冪等的流程,就是這樣的過程。
2.2.4 跨城數(shù)據(jù)同步如何規(guī)避數(shù)據(jù)回環(huán)
接下來看一下在跨城數(shù)據(jù)同步如何規(guī)避數(shù)據(jù)回環(huán)??绯堑募軜?gòu)中,本城的一套數(shù)據(jù)實例會把增量數(shù)據(jù)寫到消息隊列,對城會有一個服務(wù)從消息隊列里面把這個數(shù)據(jù)拉出來——對城的消息也會落到對城的消息隊列,本城有一些消費(fèi)服務(wù)會把這些數(shù)據(jù)拉過來,也就是說數(shù)據(jù)具有一個環(huán)路。如果不做回環(huán)檢測和規(guī)避,比如插入這一條數(shù)據(jù),這條數(shù)據(jù)的目標(biāo)又插入了,并且也落了一個日志,做了這個日志又寫回來,這相當(dāng)于同一個主鍵的數(shù)據(jù)來來回回在寫,這樣會把數(shù)據(jù)寫臟。
我們是如何來規(guī)避跨城數(shù)據(jù)同步的回環(huán),以及對它進(jìn)行檢測的?TDSQL結(jié)合DB內(nèi)核的改造,通過SERVERID來規(guī)避數(shù)據(jù)在跨城雙活數(shù)據(jù)同步架構(gòu)里面的回環(huán)問題。
假如說左右兩端是兩個DB,這兩個DB對應(yīng)的SERVER ID不一樣:一個是23243,一個是43423?,F(xiàn)在有一條叫做insert的數(shù)據(jù)寫入目標(biāo),寫入目標(biāo)之后會設(shè)置當(dāng)前這個SERVER ID跟原來的SERVER ID一樣——23243的ID。這條insert落到DB里面,會記錄成它的對應(yīng)日志的SERVER ID 23243,而不是記錄它本身備城的43423的ID。
當(dāng)消費(fèi)服務(wù)拿到增量日志——拿到的這條日志所對應(yīng)的SERVER ID跟目標(biāo)DB的SERVER ID是一樣時,則認(rèn)為拿到這條日志一定是目標(biāo)寫過來的日志,然后執(zhí)行跳過的操作。只有當(dāng)拿到的這條日志對應(yīng)的SERVER ID跟目標(biāo)的SERVER ID不匹配的時候,才會把這條數(shù)據(jù)寫到目標(biāo)里面去。這樣一來,才能保證只有是真正業(yè)務(wù)訪問到源端的DB,并落下來的那條日志,才會被成功寫入到目標(biāo)上——這就形成一個通過SERVER ID將環(huán)路里面的數(shù)據(jù)過濾的機(jī)制。
2.3高性能保障
2.3.1 有序消息并發(fā)重放
現(xiàn)在介紹一下關(guān)于高性能的優(yōu)化實現(xiàn)。
MySQL本身在落日志的時候是有序的消息,就是說binlog是有序的。如果按照binlog的數(shù)據(jù)來重放,是沒有問題的——按照一個事務(wù)一個事務(wù)進(jìn)行串行解析。但這會帶來一個問題——就是慢。
對于這個問題,TDSQL想辦法對有序消息進(jìn)行并發(fā)重放來提升數(shù)據(jù)同步的效率。采取通過基于row格式binlog日志的hash并發(fā)策略來實現(xiàn)。
這個hash策略就是根據(jù)表名和主鍵來做:首先從消息隊列拿到數(shù)據(jù)之后,系統(tǒng)會進(jìn)行派發(fā),派發(fā)過程中根據(jù)消息里面的主鍵和表名進(jìn)行hash,將消息hash到不同的工作隊列。
這樣的hash策略有一個什么樣的結(jié)果?相同表的同一行操作的序列一定會被劃分在同一個工作現(xiàn)場,只要保證對某一張表其中固定一行的操作是串行有序的,就認(rèn)為這套數(shù)據(jù)在并發(fā)重放結(jié)束之后數(shù)據(jù)是最終一致的。
總結(jié)而言,TDSQL多源同步并發(fā)策略就是按照主鍵和表名進(jìn)行hash,保證每一個表里面的固定一行的操作序列在同一個工作隊列里面串行化。并發(fā)的數(shù)據(jù)同步和串行數(shù)據(jù)同步區(qū)別就是一致性的問題,可以看到這種并發(fā)策略相當(dāng)于把事務(wù)打散。這里面并發(fā)重放的時候就會產(chǎn)生事務(wù)一致性的問題,有可能會非常小的幾率讀到中間狀態(tài),在數(shù)據(jù)同步速率有保障的情況下說不會出現(xiàn)這種問題的。如果說這個業(yè)務(wù)本身對事務(wù)一致性要求非常嚴(yán)格——當(dāng)然我們現(xiàn)在還沒有碰到這樣的場景。這就是一個有序消息的并發(fā)重放。
2.3.2 有序消息并發(fā)解析
以上是消費(fèi)端性能優(yōu)化的過程,首先就是要寫得更快,通過各種優(yōu)化把hash并發(fā)到多個現(xiàn)場去寫。那么寫完之后,消費(fèi)端的性能瓶頸在哪里?在解析上。
大家如果有印象的話,我們寫到Kafka里面的數(shù)據(jù)是中間格式——json格式。json格式需要一個解析過程。當(dāng)我們解決了重放性能瓶頸之后,原始的消息包拿到后解析的過程又變成性能優(yōu)化的瓶頸。針對這個問題TDSQL同樣做了有序消息的并發(fā)解析優(yōu)化。
并發(fā)解析的策略就是,維持一個線程池。從Kafka上拉下來的這條消息,本身是一個原始沒有解析的包,當(dāng)拉下來這條消息包時會從這個池子里面撈一個空閑隊列,并把這個包給空閑的線程,這個線程拿到這個包以后就開始解析,當(dāng)它拿到包這一刻就會進(jìn)入到另外一個busy隊列里面。它在忙隊列里面會不停地解析拿到的這些原始消息,解析完之后會有一個協(xié)調(diào)線程,從忙隊列面不斷把解析線程摘出來喚醒,把解析后的消息再并發(fā)地分往后面的工作線程。源源不斷從Kafka拉消息,拉完之后就把這些沒有解析的消息分給一組線程去解析,這一組線程在解析的時候——雖然解析是并發(fā)的,但在被喚醒派發(fā)的時候有一個出隊的操作——也就是派發(fā)是按照順序派發(fā)——這就做到有序消息的并發(fā)解析:通過一個忙隊列、一個閑暇隊列,兩個協(xié)調(diào)線程把整個流程串起來,這樣基本解決了在json解析上的瓶頸。
2.4 高可用保障:多機(jī)容災(zāi)保護(hù)
2.4.1 多機(jī)容災(zāi)保護(hù)
現(xiàn)在介紹一下消費(fèi)者高可用保障。消費(fèi)者服務(wù)本身無狀態(tài),所有的任務(wù)下發(fā)通過MetaCluster實現(xiàn),可以通過多臺機(jī)器去部署同步服務(wù),這套容災(zāi)機(jī)制通過manager進(jìn)程來實現(xiàn),也就是說當(dāng)整個機(jī)器掉電,運(yùn)營這個機(jī)器的consumer已經(jīng)不存活,這個時候這些consumer在MetaCluster上的存活節(jié)點的失效就會被其他機(jī)器的manager節(jié)點感知——認(rèn)為另外一些機(jī)器的consumer已經(jīng)不存活,這個時候就會把任務(wù)接管過來,并且在自己機(jī)器上重新拉起這些服務(wù)。
二是我們要做到同一個數(shù)據(jù)同步的鏈路不能在兩臺機(jī)器上同時拉起,這是一個互斥的要求。高可用機(jī)制會通過一些像唯一標(biāo)識或者當(dāng)前的分派節(jié)點做到,同一個數(shù)據(jù)同步的任務(wù)在被拉起的時候一定是發(fā)生在不同的機(jī)器上來實現(xiàn)漂移與互斥的操作,這個多機(jī)容災(zāi)保護(hù)總體上就是通過MetaCluster和監(jiān)控的進(jìn)程,比如說manager這樣的服務(wù)進(jìn)行協(xié)調(diào)完成,保證在機(jī)器級別災(zāi)難或者其他災(zāi)難情況下這些任務(wù)能夠在十秒以內(nèi)成功遷移到其他的存活節(jié)點上。
2.4.2 擴(kuò)容場景的高可用設(shè)計
可用性一方面在災(zāi)難情況下需要保證服務(wù)可用,另一方面則是在擴(kuò)容等數(shù)據(jù)庫常規(guī)運(yùn)營場景下保證這個數(shù)據(jù)同步有效且不會中斷。
首先來說一下為什么在擴(kuò)容的場景下,有可能造成數(shù)據(jù)同步異常?以垂直擴(kuò)容來說,是相當(dāng)于重新買了一套實例,然后經(jīng)過數(shù)據(jù)的搬遷來實現(xiàn)數(shù)據(jù)同步的。這個時候我們是通過TOPIC唯一性來保證服務(wù)可用。擴(kuò)容中從一個實例遷移到另外一個實例的時候,兩個實例之間關(guān)系是什么?它們會往同一個Kafka上TOPIC去打增量數(shù)據(jù)。新實例打增量數(shù)據(jù)的起始點是什么?生產(chǎn)者在工作的時候會從Kafka上拿起始點,上一個服務(wù)結(jié)束的位置就是這個服務(wù)開始的位置。
關(guān)于水平擴(kuò)容,則是新擴(kuò)出一個set來,然后建立數(shù)據(jù)同步,對重復(fù)的分區(qū)進(jìn)行切割和刪除。如果現(xiàn)在有兩套分布式實例進(jìn)行數(shù)據(jù)同步,比如源端有一個分布式實例,這里面對應(yīng)會有兩個同步任務(wù)寫到目標(biāo)上,如果對其中一個分片進(jìn)行水平拆分之后,就會拆出另外一個實例來,這個實例在拆分中有一個數(shù)據(jù)同步的過程,這個過程會產(chǎn)生問題——在set 3 binlog里面,會有一些set2上寫的數(shù)據(jù),并且SERVER ID跟set 2一樣,如果單純對set 3新擴(kuò)出來的分片創(chuàng)建一個數(shù)據(jù)同步任務(wù),將數(shù)據(jù)寫到目標(biāo)上的話,我們認(rèn)為這里面可能會把SET2已經(jīng)寫進(jìn)去的部分?jǐn)?shù)據(jù)重復(fù)。這個TDSQL的數(shù)據(jù)同步服務(wù)針對水平擴(kuò)容的這個場景也是實現(xiàn)了高可用保障,比如我們會針對擴(kuò)容前的SERVER ID進(jìn)行過濾,過濾水平擴(kuò)容前set的原實例的SERVERID,這個跟跨城的回環(huán)操作是比較類似的。通過這樣的方式,來保證新創(chuàng)建出來的這部分增量數(shù)據(jù)開始往目標(biāo)上寫的時候,一定是這套擴(kuò)容流程已經(jīng)結(jié)束了,并且是有真的業(yè)務(wù)數(shù)據(jù)寫到新擴(kuò)的分片上來,不會出現(xiàn)同樣的數(shù)據(jù)反復(fù)寫兩遍的情況。
3 TDSQL 多源同步金融級應(yīng)用場景和最佳實踐
上面我們解釋了這個模塊的特性、難點、解決的方式,現(xiàn)在介紹這些應(yīng)用場景以及案例,包括TDSQL在多個客戶場景中的最佳實踐。
3.1 實現(xiàn)業(yè)務(wù)驗證
關(guān)于實現(xiàn)業(yè)務(wù)的驗證,比如可以對兩個DB進(jìn)行實時的數(shù)據(jù)同步。新的業(yè)務(wù)系統(tǒng)升級時,不可能直接把新的業(yè)務(wù)系統(tǒng)放到老的DB上直接跑,這時可以把新的業(yè)務(wù)系統(tǒng)先落到新的DB上做相關(guān)業(yè)務(wù)驗證,或者在異構(gòu)數(shù)據(jù)庫的DB層變更上,把數(shù)據(jù)先同步到新的DB上來做業(yè)務(wù)上新老系統(tǒng)并行跑的驗證——一方面是保證了原先業(yè)務(wù)系統(tǒng)的安全性,另一方面也可以讓業(yè)務(wù)切割更加方便,因為數(shù)據(jù)已經(jīng)實時同步了。
3.2 實現(xiàn)業(yè)務(wù)灰度
二是業(yè)務(wù)的灰度,以張家港農(nóng)商銀行的實踐為例,在核心系統(tǒng)上線的過程中,我們把數(shù)據(jù)通過主鍵同步到TDSQL或者Oracle上,主庫如果發(fā)生了一些比較小概率的災(zāi)難性實踐,這時可以將這個業(yè)務(wù)系統(tǒng)迅速地切入到備庫上,可以是TDSQL也可以是PG、Oracle,相當(dāng)于是實時的數(shù)據(jù)備份來形成備份的DB,走備庫上把這些業(yè)務(wù)拉起來在備份的DB上跑起來。
3.3 實現(xiàn)業(yè)務(wù)割接
三是在實現(xiàn)分布式改造、進(jìn)行業(yè)務(wù)割接過程中,可以將單實例的操作同步到分布式的實例上——這個過程先將數(shù)據(jù)通過多源同步組件同步到分布式的實例上,之后將業(yè)務(wù)的流量逐漸地從單實例往分布式實例上切,同時在分布式實例上也可以去相關(guān)業(yè)務(wù)驗證,這也是我們的應(yīng)用場景。
3.4 金融級最佳案例實踐
我們可以通過多源同步對業(yè)務(wù)進(jìn)行分布式的改造,將數(shù)據(jù)直接通過實時的同步將單實例往分布式的架構(gòu)上遷移。比如說保險客戶通過多個分庫、多個分片區(qū)或者多個單的業(yè)務(wù)、邏輯上的劃分,把這些數(shù)據(jù)通過TDSQL這套服務(wù)同步到存量庫里面。
我們在云上也有一些客戶。公有云上,TDSQL的實例是通過公網(wǎng)實時寫入自建的IDC里面,不管是Oracle還是TDSQL——寫到Oracle我們也支持,我們可以直接把MySQL的DDL轉(zhuǎn)換成Oracle可以兼容的DDL,實現(xiàn)云上的生產(chǎn)業(yè)務(wù)在跑的同時,本身之前IDC離線業(yè)務(wù)的老舊業(yè)務(wù)系統(tǒng)也可以在自建IDC的老實例上運(yùn)行。
在張家港行實踐中,核心交易集群是TDSQL,我們數(shù)據(jù)同步通過內(nèi)部的局域網(wǎng),將存量和增量數(shù)據(jù),寫入到備份機(jī)房,同時也通過全量的數(shù)據(jù)校驗服務(wù)保證數(shù)據(jù)源、目標(biāo)是完全一致的來做風(fēng)險控制。當(dāng)核心交易系統(tǒng)如果出現(xiàn)一些小概率不可恢復(fù)的災(zāi)難時候,系統(tǒng)可以在短時間內(nèi)將交易的服務(wù)全部切換到備份機(jī)房的Oracle上。
四 總結(jié)
以上介紹TDSQL對外分發(fā)解耦,數(shù)據(jù)分發(fā)、遷移、同步的能力,承載這部分能力的模塊叫做多源同步模塊,在應(yīng)對金融級別或者金融場景客戶的對外解耦、遷移的需求時候,所衍生出來的,高一致、高性能、高可用這“三高”的特性,并且介紹了針對這些特性我們是如何通過技術(shù)手段來實現(xiàn)的。
Q&A
Q:全量檢測的效率怎么樣?
A:單表的話全量校驗一分鐘可以校驗5個G的數(shù)據(jù),但是表和表之間本身是可以并發(fā),也就是說單表一分鐘5個G,但是可以多個表并發(fā)去跑,這個上限就是機(jī)器本身的上限,比如說網(wǎng)卡。這套全量校驗也是通過主鍵去把數(shù)據(jù)值拉出來走內(nèi)存去做MD5。這套效率目前來看還可以,但是校驗速度也不適宜過快,本身它去拉取數(shù)據(jù)時候?qū)?shù)據(jù)庫也是有一些影響的,我們本身做校驗的時候也會在業(yè)務(wù)低峰或者晚上去做。
Q:如何實現(xiàn)抽取binlog到Kafka不丟事務(wù)?
A:這就是前面介紹的,系統(tǒng)去做GTID連續(xù)性檢測,我們知道在記錄binlog的時候,GTID一定是連續(xù)的,如果不連續(xù)則可判定認(rèn)為它丟了,繼而會到主機(jī)上補(bǔ)償。就是通過這樣的方式保證我們拿到的數(shù)據(jù)一定是沒問題的。
Q:DDL同步嗎?
A:DDL同步。因為DDL同步是會進(jìn)行一次語法解析,解析出來相關(guān)的操作的,比如要改的哪些表、哪些字段,哪些類型需要改,針對目標(biāo)的不同類型去做類型的轉(zhuǎn)換,將這個DDL重放到目標(biāo)上。
Q:原抽取和目標(biāo)回放支持按條件抽取、按條件回放嗎?
A:抽取我們支持按白名單去抽。為什么要支持白名單抽?我們原先的策略是全量上報到Kafka,有的時候會帶來一些問題,比如說業(yè)務(wù)去清理一些歷史的表,比如說一些流水的大表可能去做日志的刪除等等,會批量去做一些操作,這個時候會產(chǎn)生一些瘋狂往Kafka上打一些業(yè)務(wù)并不關(guān)心、同步并不關(guān)心的數(shù)據(jù),這個時候我們也支持源端配白名單的方式,我抽取哪些庫表的數(shù)據(jù)。目標(biāo)重放更靈活一些,目標(biāo)重放的時候是通過一個同步規(guī)則去配,同步規(guī)則本身支持精確匹配,同時也支持政策匹配。精確匹配就是一個表同步到另外一個表,精確匹配支持表名的變換,比如說我在原實例上表名是A表,同步到B實例上表名是B表,這里面強(qiáng)調(diào)的是表名可以不一樣,但是它的表結(jié)構(gòu)是要一樣的。也支持我可以匹配源端多個表同步到目標(biāo)的一張表里面,也可以支持匯總的方式,就是表名在映射這一塊也是比較靈活的。
以上是今天的提問解答。謝謝大家。
TDSQL是騰訊TEG數(shù)據(jù)庫工作組三大產(chǎn)品系之一,在國產(chǎn)數(shù)據(jù)庫領(lǐng)域?qū)掖温氏韧黄?#xff0c;包括助力微眾銀行搭建首個全行級互聯(lián)網(wǎng)銀行核心系統(tǒng),以及助力張家港農(nóng)商行實現(xiàn)傳統(tǒng)核心系統(tǒng)數(shù)據(jù)庫首次國產(chǎn)化等。目前,TDSQL已廣泛應(yīng)用于金融、政務(wù)、物聯(lián)網(wǎng)、智慧零售等行業(yè),擁有大量分布式數(shù)據(jù)庫最佳實踐。
推薦閱讀:
騰訊技術(shù)沙龍 | 快手春節(jié)紅包:高并發(fā)存儲架構(gòu)設(shè)計
億級流量場景下的平滑擴(kuò)容:TDSQL的水平擴(kuò)容方案實踐
流量洪峰成為常態(tài),騰訊數(shù)據(jù)庫如何高性能支撐海量SQL查詢?
總結(jié)
以上是生活随笔為你收集整理的银行核心海量数据无损迁移:TDSQL数据库多源异构迁移方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信扫一扫识物的技术揭秘:抠图与检索
- 下一篇: 腾讯开源 TurboTransforme