自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

億級(jí)大表分庫(kù)分表實(shí)戰(zhàn)總結(jié)(萬(wàn)字干貨,實(shí)戰(zhàn)復(fù)盤(pán))

運(yùn)維 數(shù)據(jù)庫(kù)運(yùn)維
分庫(kù)分表的文章網(wǎng)上非常多,但是大多內(nèi)容比較零散,以講解知識(shí)點(diǎn)為主,沒(méi)有完整地說(shuō)明一個(gè)大表的切分、新架構(gòu)設(shè)計(jì)、上線的完整過(guò)程。我結(jié)合去年做的一個(gè)大型分庫(kù)分表項(xiàng)目,來(lái)復(fù)盤(pán)一下完整的分庫(kù)分表從架構(gòu)設(shè)計(jì) 到 發(fā)布上線的實(shí)戰(zhàn)總結(jié)。

 分庫(kù)分表的文章網(wǎng)上非常多,但是大多內(nèi)容比較零散,以講解知識(shí)點(diǎn)為主,沒(méi)有完整地說(shuō)明一個(gè)大表的切分、新架構(gòu)設(shè)計(jì)、上線的完整過(guò)程。

因此,我結(jié)合去年做的一個(gè)大型分庫(kù)分表項(xiàng)目,來(lái)復(fù)盤(pán)一下完整的分庫(kù)分表從架構(gòu)設(shè)計(jì) 到 發(fā)布上線的實(shí)戰(zhàn)總結(jié)。


1.前言

為什么需要做分庫(kù)分表。這個(gè)相信大家多少都有所了解。

海量數(shù)據(jù)的存儲(chǔ)和訪問(wèn)成為了MySQL數(shù)據(jù)庫(kù)的瓶頸問(wèn)題,日益增長(zhǎng)的業(yè)務(wù)數(shù)據(jù),無(wú)疑對(duì)MySQL數(shù)據(jù)庫(kù)造成了相當(dāng)大的負(fù)載,同時(shí)對(duì)于系統(tǒng)的穩(wěn)定性和擴(kuò)展性提出很高的要求。

而且單臺(tái)服務(wù)器的資源(CPU、磁盤(pán)、內(nèi)存等)總是有限的,最終數(shù)據(jù)庫(kù)所能承載的數(shù)據(jù)量、數(shù)據(jù)處理能力都將遭遇瓶頸。

目前來(lái)說(shuō)一般有兩種方案。

1)一種是更換存儲(chǔ),不使用MySQL,比如可以使用HBase、polarDB、TiDB等分布式存儲(chǔ)。

2)如果出于各種原因考慮,還是想繼續(xù)使用MySQL,一般會(huì)采用第二種方式,那就是分庫(kù)分表。

文章開(kāi)頭就說(shuō)了,網(wǎng)上分庫(kù)分表文章很多,對(duì)知識(shí)點(diǎn)講解比較多,因此,本文將不再過(guò)多贅述分庫(kù)分表方案的范式處理。

而是專(zhuān)注于梳理分庫(kù)分表從架構(gòu)設(shè)計(jì) 到 發(fā)布上線的完整過(guò)程,同時(shí)總結(jié)其中的注意事項(xiàng)和最佳實(shí)踐。包括五個(gè)部分:

  • 業(yè)務(wù)重構(gòu)
  • 存儲(chǔ)架構(gòu)設(shè)計(jì)
  • 改造和上線
  • 穩(wěn)定性保障
  • 項(xiàng)目管理

尤其是各個(gè)階段的最佳實(shí)踐,都是血與淚凝聚的經(jīng)驗(yàn)教訓(xùn)。

2.第一階段:業(yè)務(wù)重構(gòu)(可選)

對(duì)于微服務(wù)劃分比較合理的分庫(kù)分表行為,一般只需要關(guān)注存儲(chǔ)架構(gòu)的變化,或者只需要在個(gè)別應(yīng)用上進(jìn)行業(yè)務(wù)改造即可,一般不需要著重考慮“業(yè)務(wù)重構(gòu)” 這一階段,因此,這一階段屬于“可選”。

本次項(xiàng)目的第一大難點(diǎn),在于業(yè)務(wù)重構(gòu)。

而本次拆分項(xiàng)目涉及到的兩張大表A和B,單表將近八千萬(wàn)的數(shù)據(jù),是從單體應(yīng)用時(shí)代遺留下來(lái)的,從一開(kāi)始就沒(méi)有很好的領(lǐng)域驅(qū)動(dòng)/MSA架構(gòu)設(shè)計(jì),邏輯發(fā)散非常嚴(yán)重,到現(xiàn)在已經(jīng)涉及50+個(gè)在線服務(wù)和20+個(gè)離線業(yè)務(wù)的的直接讀寫(xiě)。

因此,如何保證業(yè)務(wù)改造的徹底性、全面性是重中之重,不能出現(xiàn)有遺漏的情況。

另外,表A 和 表B 各自有二、三十個(gè)字段,兩表的主鍵存在一一對(duì)應(yīng)關(guān)系,因此,本次分庫(kù)分表項(xiàng)目中,還需要將兩個(gè)表進(jìn)行重構(gòu)融合,將多余/無(wú)用的字段剔除。

2.1 查詢(xún)統(tǒng)計(jì)

在線業(yè)務(wù)通過(guò)分布式鏈路追蹤系統(tǒng)進(jìn)行查詢(xún),按照表名作為查詢(xún)條件,然后按照服務(wù)維度進(jìn)行聚合,找到所有相關(guān)服務(wù),寫(xiě)一個(gè)文檔記錄相關(guān)團(tuán)隊(duì)和服務(wù)。

這里特別注意下,很多表不是只有在線應(yīng)用在使用,很多離線算法和數(shù)據(jù)分析的業(yè)務(wù)也在使用,這里需要一并的梳理好,做好線下跨團(tuán)隊(duì)的溝通和調(diào)研工作,以免切換后影響正常的數(shù)據(jù)分析。

2.2 查詢(xún)拆分與遷移

創(chuàng)建一個(gè)jar包,根據(jù)2.1的統(tǒng)計(jì)結(jié)果,與服務(wù)owner合作將服務(wù)中的相關(guān)查詢(xún)都遷移到這個(gè)jar包中(本項(xiàng)目的jar包叫projected)。

此處為1.0.0-SNAPSHOT版本。

然后將原本服務(wù)內(nèi)的xxxMapper.xxxMethod( ) 全部改成projectdb.xxxMethod( )進(jìn)行調(diào)用。

這樣做有兩個(gè)好處:

  • 方便做后續(xù)的查詢(xún)拆分分析。
  • 方便后續(xù)直接將jar包中的查詢(xún)替換為改造后 中臺(tái)服務(wù) 的rpc調(diào)用,業(yè)務(wù)方只需升級(jí)jar包版本,即可快速?gòu)膕ql調(diào)用改為rpc查詢(xún)。

這一步花了幾個(gè)月的實(shí)際,務(wù)必梳理各個(gè)服務(wù)做全面的遷移,不能遺漏,否則可能會(huì)導(dǎo)致拆分分析不全面,遺漏了相關(guān)字段。

查詢(xún)的遷移主要由于本次拆分項(xiàng)目涉及到的服務(wù)太多,需要收攏到一個(gè)jar包,更方便后期的改造。如果實(shí)際分庫(kù)分表項(xiàng)目中僅僅涉及一兩個(gè)服務(wù)的,這一步是可以不做的。

2.3 聯(lián)合查詢(xún)的拆分分析

根據(jù)2.2收攏的jar包中的查詢(xún),結(jié)合實(shí)際情況將查詢(xún)進(jìn)行分類(lèi)和判斷,把一些歷史遺留的問(wèn)題,和已經(jīng)廢棄的字段做一些整理。

以下舉一些思考點(diǎn)。

1)哪些查詢(xún)是無(wú)法拆分的?例如分頁(yè)(盡可能地改造,實(shí)在改不了只能以冗余列的形式)

2)哪些查詢(xún)是可以業(yè)務(wù)上join拆分的?

3)哪些表/字段是可以融合的?

4)哪些字段需要冗余?

5)哪些字段可以直接廢棄了?

6)根據(jù)業(yè)務(wù)具體場(chǎng)景和sql整體統(tǒng)計(jì),識(shí)別關(guān)鍵的分表鍵。其余查詢(xún)走搜索平臺(tái)。

思考后得到一個(gè)查詢(xún)改造總體思路和方案。

同時(shí)在本項(xiàng)目中需要將兩張表融合為一張表,廢棄冗余字段和無(wú)效字段。

2.4 新表設(shè)計(jì)

這一步基于2.3對(duì)于查詢(xún)的拆分分析,得出舊表融合、冗余、廢棄字段的結(jié)果,設(shè)計(jì)新表的字段。

產(chǎn)出新表設(shè)計(jì)結(jié)構(gòu)后,必須發(fā)給各個(gè)相關(guān)業(yè)務(wù)方進(jìn)行review,并保證所有業(yè)務(wù)方都通過(guò)該表的設(shè)計(jì)。有必要的話可以進(jìn)行一次線下review。

如果新表的過(guò)程中,對(duì)部分字段進(jìn)行了廢棄,必須通知所有業(yè)務(wù)方進(jìn)行確認(rèn)。

對(duì)于新表的設(shè)計(jì),除了字段的梳理,也需要根據(jù)具體查詢(xún),重新設(shè)計(jì)、優(yōu)化索引。

2.5 第一次升級(jí)

新表設(shè)計(jì)完成后,先做一次jar包內(nèi)sql查詢(xún)的改造,將舊的字段全部更新為新表的字段。

此處為2.0.0-SNAPSHOT版本。

然后讓所有服務(wù)升級(jí)jar包版本,以此來(lái)保證這些廢棄字段確實(shí)是不使用了,新的表結(jié)構(gòu)字段能夠完全覆蓋過(guò)去的業(yè)務(wù)場(chǎng)景。

特別注意的是,由于涉及服務(wù)眾多,可以將服務(wù)按照 非核心 與 核心 區(qū)分,然后分批次上線,避免出現(xiàn)問(wèn)題導(dǎo)致嚴(yán)重故障或者大范圍回滾。

2.6 最佳實(shí)踐

2.6.1 盡量不改變?cè)淼淖侄蚊Q(chēng)

在做新表融合的時(shí)候,一開(kāi)始只是簡(jiǎn)單歸并表A 和 表B的表,因此很多字段名相同的字段做了重命名。

后來(lái)字段精簡(jiǎn)過(guò)程中,刪除了很多重復(fù)字段,但是沒(méi)有將重命名的字段改回來(lái)。

導(dǎo)致后期上線的過(guò)程中,不可避免地需要業(yè)務(wù)方進(jìn)行重構(gòu)字段名。

因此,新表設(shè)計(jì)的時(shí)候,除非必不得已,不要修改原表的字段名稱(chēng)!

2.6.2 新表的索引需要仔細(xì)斟酌

新表的索引不能簡(jiǎn)單照搬舊表,而是需要根據(jù)查詢(xún)拆分分析后,重新設(shè)計(jì)。

尤其是一些字段的融合后,可能可以歸并一些索引,或者設(shè)計(jì)一些更高性能的索引。

2.6 本章小結(jié)

至此,分庫(kù)分表的第一階段告一段落。這一階段所需時(shí)間,完全取決于具體業(yè)務(wù),如果是一個(gè)歷史包袱沉重的業(yè)務(wù),那可能需要花費(fèi)幾個(gè)月甚至半年的時(shí)間才能完成。

這一階段的完成質(zhì)量非常重要,否則可能導(dǎo)致項(xiàng)目后期需要重建表結(jié)構(gòu)、重新全量數(shù)據(jù)。

這里再次說(shuō)明,對(duì)于微服務(wù)劃分比較合理的服務(wù),分庫(kù)分表行為一般只需要關(guān)注存儲(chǔ)架構(gòu)的變化,或者只需要在個(gè)別應(yīng)用上進(jìn)行業(yè)務(wù)改造即可,一般不需要著重考慮“業(yè)務(wù)重構(gòu)” 這一階段。

3.第二階段:存儲(chǔ)架構(gòu)設(shè)計(jì)(核心)

對(duì)于任何分庫(kù)分表的項(xiàng)目,存儲(chǔ)架構(gòu)的設(shè)計(jì)都是最核心的部分!

3.1 整體架構(gòu)

根據(jù)第一階段整理的查詢(xún)梳理結(jié)果,我們總結(jié)了這樣的查詢(xún)規(guī)律。

80%以上的查詢(xún)都是通過(guò)或者帶有字段pk1、字段pk2、字段pk3這三個(gè)維度進(jìn)行查詢(xún)的,其中pk1和pk2由于歷史原因存在一一對(duì)應(yīng)的關(guān)系

20%的查詢(xún)千奇百怪,包括模糊查詢(xún)、其他字段查詢(xún)等等

因此,我們?cè)O(shè)計(jì)了如下的整體架構(gòu),引入了數(shù)據(jù)庫(kù)中間件、數(shù)據(jù)同步工具、搜索引擎(阿里云opensearch/ES)等。

下文的論述都是圍繞這個(gè)架構(gòu)來(lái)展開(kāi)的。


3.1.1 mysql分表存儲(chǔ)

Mysql分表的維度是根據(jù)查詢(xún)拆分分析的結(jié)果確定的。

我們發(fā)現(xiàn)pk1\pk2\pk3可以覆蓋80%以上的主要查詢(xún)。讓這些查詢(xún)根據(jù)分表鍵直接走mysql數(shù)據(jù)庫(kù)即可。

原則上一般最多維護(hù)一個(gè)分表的全量數(shù)據(jù),因?yàn)檫^(guò)多的全量數(shù)據(jù)會(huì)造成存儲(chǔ)的浪費(fèi)、數(shù)據(jù)同步的額外開(kāi)銷(xiāo)、更多的不穩(wěn)定性、不易擴(kuò)展等問(wèn)題。

但是由于本項(xiàng)目pk1和pk3的查詢(xún)語(yǔ)句都對(duì)實(shí)時(shí)性有比較高的要求,因此,維護(hù)了pk1和pk3作為分表鍵的兩份全量數(shù)據(jù)。

而pk2和pk1由于歷史原因,存在一一對(duì)應(yīng)關(guān)系,可以?xún)H保留一份映射表即可,只存儲(chǔ)pk1和pk2兩個(gè)字段。

3.1.2 搜索平臺(tái)索引存儲(chǔ)

搜索平臺(tái)索引,可以覆蓋剩余20%的零散查詢(xún)。

這些查詢(xún)往往不是根據(jù)分表鍵進(jìn)行的,或者是帶有模糊查詢(xún)的要求。

對(duì)于搜索平臺(tái)來(lái)說(shuō),一般不存儲(chǔ)全量數(shù)據(jù)(尤其是一些大varchar字段),只存儲(chǔ)主鍵和查詢(xún)需要的索引字段,搜索得到結(jié)果后,根據(jù)主鍵去mysql存儲(chǔ)中拿到需要的記錄。

當(dāng)然,從后期實(shí)踐結(jié)果來(lái)看,這里還是需要做一些權(quán)衡的:

1)有些非索引字段,如果不是很大,也可以冗余進(jìn)來(lái),類(lèi)似覆蓋索引,避免多一次sql查詢(xún);

2)如果表結(jié)構(gòu)比較簡(jiǎn)單,字段不大,甚至可以考慮全量存儲(chǔ),提高查詢(xún)性能,降低mysql數(shù)據(jù)庫(kù)的壓力。

這里特別提示,搜索引擎和數(shù)據(jù)庫(kù)之間同步是必然存在延遲的。所以對(duì)于根據(jù)分表id查詢(xún)的語(yǔ)句,盡量保證直接查詢(xún)數(shù)據(jù)庫(kù),這樣不會(huì)帶來(lái)一致性問(wèn)題的隱患。

3.1.3 數(shù)據(jù)同步

一般新表和舊表直接可以采用 數(shù)據(jù)同步 或者 雙寫(xiě)的方式進(jìn)行處理,兩種方式有各自的優(yōu)缺點(diǎn)。


一般根據(jù)具體情況選擇一種方式就行。

本次項(xiàng)目的具體同步關(guān)系見(jiàn)整體存儲(chǔ)架構(gòu),包括了四個(gè)部分:

1)舊表到新表全量主表的同步

一開(kāi)始為了減少代碼入侵、方便擴(kuò)展,采用了數(shù)據(jù)同步的方式。而且由于業(yè)務(wù)過(guò)多,擔(dān)心有未統(tǒng)計(jì)到的服務(wù)沒(méi)有及時(shí)改造,所以數(shù)據(jù)同步能避免這些情況導(dǎo)致數(shù)據(jù)丟失。

但是在上線過(guò)程中發(fā)現(xiàn),當(dāng)延遲存在時(shí),很多新寫(xiě)入的記錄無(wú)法讀到,對(duì)具體業(yè)務(wù)場(chǎng)景造成了比較嚴(yán)重的影響。(具體原因參考4.5.1的說(shuō)明)

因此,為了滿(mǎn)足應(yīng)用對(duì)于實(shí)時(shí)性的要求,我們?cè)跀?shù)據(jù)同步的基礎(chǔ)上,重新在3.0.0-SNAPSHOT版本中改造成了雙寫(xiě)的形式。

2)新表全量主表到全量副表的同步

3)新表全量主表到映射表到同步

4)新表全量主表到搜索引擎數(shù)據(jù)源的同步

2)、3)、4)都是從新表全量主表到其他數(shù)據(jù)源的數(shù)據(jù)同步,因?yàn)闆](méi)有強(qiáng)實(shí)時(shí)性的要求,因此,為了方便擴(kuò)展,全部采用了數(shù)據(jù)同步的方式,沒(méi)有進(jìn)行更多的多寫(xiě)操作。

3.2 容量評(píng)估

在申請(qǐng)mysql存儲(chǔ)和搜索平臺(tái)索引資源前,需要進(jìn)行容量評(píng)估,包括存儲(chǔ)容量和性能指標(biāo)。

具體線上流量評(píng)估可以通過(guò)監(jiān)控系統(tǒng)查看qps,存儲(chǔ)容量可以簡(jiǎn)單認(rèn)為是線上各個(gè)表存儲(chǔ)容量的和。

但是在全量同步過(guò)程中,我們發(fā)現(xiàn)需要的實(shí)際容量的需求會(huì)大于預(yù)估,具體可以看3.4.6的說(shuō)明。

具體性能壓測(cè)過(guò)程就不再贅述。

3.3 數(shù)據(jù)校驗(yàn)

從上文可以看到,在本次項(xiàng)目中,存在大量的業(yè)務(wù)改造,屬于異構(gòu)遷移。

從過(guò)去的一些分庫(kù)分表項(xiàng)目來(lái)說(shuō),大多是同構(gòu)/對(duì)等拆分,因此不會(huì)存在很多復(fù)雜邏輯,所以對(duì)于數(shù)據(jù)遷移的校驗(yàn)往往比較忽視。

在完全對(duì)等遷移的情況下,一般確實(shí)比較少出現(xiàn)問(wèn)題。

但是,類(lèi)似這樣有比較多改造的異構(gòu)遷移,校驗(yàn)絕對(duì)是重中之重!!

因此,必須對(duì)數(shù)據(jù)同步的結(jié)果做校驗(yàn),保證業(yè)務(wù)邏輯改造正確、數(shù)據(jù)同步一致性正確。這一點(diǎn)非常非常重要。

在本次項(xiàng)目中,存在大量業(yè)務(wù)邏輯優(yōu)化以及字段變動(dòng),所以我們單獨(dú)做了一個(gè)校驗(yàn)服務(wù),對(duì)數(shù)據(jù)的全量、增量進(jìn)行校驗(yàn)。

過(guò)程中提前發(fā)現(xiàn)了許多數(shù)據(jù)同步、業(yè)務(wù)邏輯的不一致問(wèn)題,給我們本次項(xiàng)目平穩(wěn)上線提供了最重要的前提保障!!

3.4 最佳實(shí)踐

3.4.1 分庫(kù)分表引起的流量放大問(wèn)題

在做容量評(píng)估的時(shí)候,需要關(guān)注一個(gè)重要問(wèn)題。就是分表帶來(lái)的查詢(xún)流量放大。

這個(gè)流量放大有兩方面的原因:

  • 索引表的二次查詢(xún)。比如根據(jù)pk2查詢(xún)的,需要先通過(guò)pk2查詢(xún)pk1,然后根據(jù)pk1查詢(xún)返回結(jié)果。
  • in的分批查詢(xún)。如果一個(gè)select...in...的查詢(xún),數(shù)據(jù)庫(kù)中間件會(huì)根據(jù)分表鍵,將查詢(xún)拆分落到對(duì)應(yīng)的物理分表上,相當(dāng)于原本的一次查詢(xún),放大為多次查詢(xún)。(當(dāng)然,數(shù)據(jù)庫(kù)會(huì)將落在同一個(gè)分表的id作為一次批量查詢(xún),而這是不穩(wěn)定的合并)

因此,我們需要注意:

  • 業(yè)務(wù)層面盡量限制in查詢(xún)數(shù)量,避免流量過(guò)于放大;
  • 容量評(píng)估時(shí),需要考慮這部分放大因素,做適當(dāng)冗余,另外,后續(xù)會(huì)提到業(yè)務(wù)改造上線分批進(jìn)行,保證可以及時(shí)擴(kuò)容;
  • 分64、128還是256張表有個(gè)合理預(yù)估,拆得越多,理論上會(huì)放大越多,因此不要無(wú)謂地分過(guò)多的表,根據(jù)業(yè)務(wù)規(guī)模做適當(dāng)估計(jì);
  • 對(duì)于映射表的查詢(xún),由于存在明顯的冷熱數(shù)據(jù),所以我們又在中間加了一層緩存,減少數(shù)據(jù)庫(kù)的壓力

3.4.2 分表鍵的變更方案

本項(xiàng)目中,存在一種業(yè)務(wù)情況會(huì)變更字段pk3,但是pk3作為分表鍵,在數(shù)據(jù)庫(kù)中間件中是不能修改的,因此,只能在中臺(tái)中修改對(duì)pk3的更新邏輯,采用先刪除、后添加的方式。

這里需要注意,刪除和添加操作的事務(wù)原子性。當(dāng)然,簡(jiǎn)單處理也可以通過(guò)日志的方式,進(jìn)行告警和校準(zhǔn)。

3.4.3 數(shù)據(jù)同步一致性問(wèn)題

我們都知道,數(shù)據(jù)同步中一個(gè)關(guān)鍵點(diǎn)就是(消息)數(shù)據(jù)的順序性,如果不能保證接受的數(shù)據(jù)和產(chǎn)生的數(shù)據(jù)的順序嚴(yán)格一致,就有可能因?yàn)?消息)數(shù)據(jù)亂序帶來(lái)數(shù)據(jù)覆蓋,最終帶來(lái)不一致問(wèn)題。

我們自研的數(shù)據(jù)同步工具底層使用的消息隊(duì)列是kakfa,,kafka對(duì)于消息的存儲(chǔ),只能做到局部有序性(具體來(lái)說(shuō)是每一個(gè)partition的有序)。我們可以把同一主鍵的消息路由至同一分區(qū),這樣一致性一般可以保證。但是,如果存在一對(duì)多的關(guān)系,就無(wú)法保證每一行變更有序,見(jiàn)如下例子。


那么需要通過(guò)反查數(shù)據(jù)源獲取最新數(shù)據(jù)保證一致性。

但是,反查也不是“銀彈“,需要考慮兩個(gè)問(wèn)題。

1)如果消息變更來(lái)源于讀寫(xiě)實(shí)例,而反查 數(shù)據(jù)庫(kù)是查只讀實(shí)例,那就會(huì)存在讀寫(xiě)實(shí)例延遲導(dǎo)致的數(shù)據(jù)不一致問(wèn)題。因此,需要保證 消息變更來(lái)源 和 反查數(shù)據(jù)庫(kù) 的實(shí)例是同一個(gè)。

2)反查對(duì)數(shù)據(jù)庫(kù)會(huì)帶來(lái)額外性能開(kāi)銷(xiāo),需要仔細(xì)評(píng)估全量時(shí)候的影響。

3.4.4 數(shù)據(jù)實(shí)時(shí)性問(wèn)題

延遲主要需要注意幾方面的問(wèn)題,并根據(jù)業(yè)務(wù)實(shí)際情況做評(píng)估和衡量。

1)數(shù)據(jù)同步平臺(tái)的秒級(jí)延遲

2)如果消息訂閱和反查數(shù)據(jù)庫(kù)都是落在只讀實(shí)例上,那么除了上述數(shù)據(jù)同步平臺(tái)的秒級(jí)延遲,還會(huì)有數(shù)據(jù)庫(kù)主從同步的延遲

3)寬表到搜索平臺(tái)的秒級(jí)延遲

只有能夠滿(mǎn)足業(yè)務(wù)場(chǎng)景的方案,才是合適的方案。

3.4.5 分表后存儲(chǔ)容量?jī)?yōu)化

由于數(shù)據(jù)同步過(guò)程中,對(duì)于單表而言,不是嚴(yán)格按照遞增插入的,因此會(huì)產(chǎn)生很多”存儲(chǔ)空洞“,使得同步完后的存儲(chǔ)總量遠(yuǎn)大于預(yù)估的容量。

因此,在新庫(kù)申請(qǐng)的時(shí)候,存儲(chǔ)容量多申請(qǐng)50%。

具體原因可以參考我的這篇文章 為什么MySQL分庫(kù)分表后總存儲(chǔ)大小變大了?

3.5 本章小結(jié)

至此,分庫(kù)分表的第二階段告一段落。

這一階段踩了非常多的坑。

一方面是設(shè)計(jì)高可用、易擴(kuò)展的存儲(chǔ)架構(gòu)。在項(xiàng)目進(jìn)展過(guò)程中,也做了多次的修改與討論,包括mysql數(shù)據(jù)冗余數(shù)量、搜索平臺(tái)的索引設(shè)計(jì)、流量放大、分表鍵修改等問(wèn)題。

另一方面是“數(shù)據(jù)同步”本身是一個(gè)非常復(fù)雜的操作,正如本章最佳實(shí)踐中提及的實(shí)時(shí)性、一致性、一對(duì)多等問(wèn)題,需要引起高度重視。

因此,更加依賴(lài)于數(shù)據(jù)校驗(yàn)對(duì)最終業(yè)務(wù)邏輯正確、數(shù)據(jù)同步正確的檢驗(yàn)!

在完成這一階段后,可以正式進(jìn)入業(yè)務(wù)切換的階段。需要注意的是,數(shù)據(jù)校驗(yàn)仍然會(huì)在下一階段發(fā)揮關(guān)鍵性作用。

4.第三階段:改造和上線(慎重)

前兩個(gè)階段完成后,開(kāi)始業(yè)務(wù)切換流程,主要步驟如下:

1)中臺(tái)服務(wù)采用單讀 雙寫(xiě) 的模式

2)舊表往新表開(kāi)著數(shù)據(jù)同步

3) 所有服務(wù)升級(jí)依賴(lài)的projectDB版本,上線RPC,如果出現(xiàn)問(wèn)題,降版本即可回滾(上線成功后,單讀新庫(kù),雙寫(xiě)新舊庫(kù))

4)檢查監(jiān)控確保沒(méi)有 中臺(tái)服務(wù) 以外的其他服務(wù)訪問(wèn)舊庫(kù)舊表

5)停止數(shù)據(jù)同步

6)刪除舊表

4.1 查詢(xún)改造

如何驗(yàn)證我們前兩個(gè)階段設(shè)計(jì)是否合理?能否完全覆蓋查詢(xún)的修改 是一個(gè)前提條件。

當(dāng)新表設(shè)計(jì)完畢后,就可以以新表為標(biāo)準(zhǔn),修改老的查詢(xún)。

以本項(xiàng)目為例,需要將舊的sql在 新的中臺(tái)服務(wù)中 進(jìn)行改造。

1)讀查詢(xún)的改造

可能查詢(xún)會(huì)涉及以下幾個(gè)方面:

a)根據(jù)查詢(xún)條件,需要將pk1和pk2的inner join改為對(duì)應(yīng)分表鍵的新表表名

b)部分sql的廢棄字段處理

c)非分表鍵查詢(xún)改為走搜索平臺(tái)的查詢(xún),注意保證語(yǔ)義一致

d)注意寫(xiě)單測(cè)避免低級(jí)錯(cuò)誤,主要是DAO層面。

只有新表結(jié)構(gòu)和存儲(chǔ)架構(gòu)能完全適應(yīng)查詢(xún)改造,才能認(rèn)為前面的設(shè)計(jì)暫時(shí)沒(méi)有問(wèn)題。

當(dāng)然,這里還有個(gè)前提條件,就是相關(guān)查詢(xún)已經(jīng)全部收攏,沒(méi)有遺漏。

2) 寫(xiě)查詢(xún)的改造

除了相關(guān)字段的更改以外,更重要的是,需要改造為舊表、新表的雙寫(xiě)模式。

這里可能涉及到具體業(yè)務(wù)寫(xiě)入邏輯,本項(xiàng)目尤為復(fù)雜,需要改造過(guò)程中與業(yè)務(wù)方充分溝通,保證寫(xiě)入邏輯正確。

可以在雙寫(xiě)上各加一個(gè)配置開(kāi)關(guān),方便切換。如果雙寫(xiě)中發(fā)現(xiàn)新庫(kù)寫(xiě)入有問(wèn)題,可以快速關(guān)閉。

同時(shí),雙寫(xiě)過(guò)程中不關(guān)閉 舊庫(kù)到新庫(kù) 的數(shù)據(jù)同步。

為什么呢?主要還是由于我們項(xiàng)目的特殊性。由于我們涉及到幾十個(gè)服務(wù),為了降低風(fēng)險(xiǎn),必須分批上線。因此,存在比較麻煩的中間態(tài),一部分服務(wù)是老邏輯,一部分服務(wù)是新邏輯,必須保證中間態(tài)的數(shù)據(jù)正確性,具體見(jiàn)4.5.1的分析。

4.2 服務(wù)化改造

為什么需要新建一個(gè) 服務(wù)來(lái) 承載改造后的查詢(xún)呢?

一方面是為了改造能夠方便的升級(jí)與回滾切換,另一方面是為了將查詢(xún)收攏,作為一個(gè)中臺(tái)化的服務(wù)來(lái)提供相應(yīng)的查詢(xún)能力。

將改造后的新的查詢(xún)放在服務(wù)中,然后jar包中的原本查詢(xún),全部替換成這個(gè)服務(wù)的client調(diào)用。

同時(shí),升級(jí)jar包版本到3.0.0-SNAPSHOT。

4.3 服務(wù)分批上線

為了降低風(fēng)險(xiǎn),需要安排從非核心服務(wù)到核心服務(wù)的分批上線。

注意,分批上線過(guò)程中,由于寫(xiě)服務(wù)往往是核心服務(wù),所以安排在后面??赡艹霈F(xiàn)非核心的讀服務(wù)上線了,這時(shí)候會(huì)有讀新表、寫(xiě)舊表的中間狀態(tài)。

1) 所有相關(guān)服務(wù)使用 重構(gòu)分支 升級(jí)projectdb版本到3.0.0-SNAPSHOT并部署內(nèi)網(wǎng)環(huán)境;

2) 業(yè)務(wù)服務(wù)依賴(lài)于 中臺(tái)服務(wù),需要訂閱服務(wù)

3) 開(kāi)重構(gòu)分支(不要與正常迭代分支合并),部署內(nèi)網(wǎng),內(nèi)網(wǎng)預(yù)計(jì)測(cè)試兩周以上

使用一個(gè)新的 重構(gòu)分支 是為了在內(nèi)網(wǎng)測(cè)試兩周的時(shí)候,不影響業(yè)務(wù)正常迭代。每周更新的業(yè)務(wù)分支可以merge到重構(gòu)分支上部署內(nèi)網(wǎng),然后外網(wǎng)使用業(yè)務(wù)分支merge到master上部署。

當(dāng)然,如果從線上線下代碼分支一致的角度,也可以重構(gòu)分支和業(yè)務(wù)分支一起測(cè)試上線,對(duì)開(kāi)發(fā)和測(cè)試的壓力會(huì)較大。

4)分批上線過(guò)程中,如果碰到依賴(lài)沖突的問(wèn)題,需要及時(shí)解決并及時(shí)更新到該文檔中

5)服務(wù)上線前,必須要求業(yè)務(wù)開(kāi)發(fā)或者測(cè)試,明確評(píng)估具體api和風(fēng)險(xiǎn)點(diǎn),做好回歸。

這里再次提醒,上線完成后,請(qǐng)不要漏掉離線的數(shù)據(jù)分析業(yè)務(wù)!請(qǐng)不要漏掉離線的數(shù)據(jù)分析業(yè)務(wù)!請(qǐng)不要漏掉離線的數(shù)據(jù)分析業(yè)務(wù)!

4.4 舊表下線流程

1)檢查監(jiān)控確保沒(méi)有中臺(tái)服務(wù)以外的其他服務(wù)訪問(wèn)舊庫(kù)舊表

2)檢查數(shù)據(jù)庫(kù)上的sql審計(jì),確保沒(méi)有其他服務(wù)仍然讀取舊表數(shù)據(jù)

3)停止數(shù)據(jù)同步

4)刪除舊表

4.5 最佳實(shí)踐

4.5.1 寫(xiě)完立即讀可能讀不到

在分批上線過(guò)程中,遇到了寫(xiě)完立即讀可能讀不到的情況。由于業(yè)務(wù)眾多,我們采用了分批上線的方式降低風(fēng)險(xiǎn),存在一部分應(yīng)用已經(jīng)升級(jí),一部分應(yīng)用尚未升級(jí)的情況。未升級(jí)的服務(wù)仍然往舊表寫(xiě)數(shù)據(jù),而升級(jí)后的應(yīng)用會(huì)從新表讀數(shù)據(jù),當(dāng)延遲存在時(shí),很多新寫(xiě)入的記錄無(wú)法讀到,對(duì)具體業(yè)務(wù)場(chǎng)景造成了比較嚴(yán)重的影響。

延遲的原因主要有兩個(gè):

1)寫(xiě)服務(wù)還沒(méi)有升級(jí),還沒(méi)有開(kāi)始雙寫(xiě),還是寫(xiě)舊表,這時(shí)候會(huì)有讀新表、寫(xiě)舊表的中間狀態(tài),新舊表存在同步延遲。

2)為了避免主庫(kù)壓力,新表數(shù)據(jù)是從舊表獲取變更、然后反查舊表只讀實(shí)例的數(shù)據(jù)進(jìn)行同步的,主從庫(kù)本身存在一定延遲。

解決方案一般有兩種:

1)數(shù)據(jù)同步改為雙寫(xiě)邏輯。

2)在讀接口做補(bǔ)償,如果新表查不到,到舊表再查一次。

4.5.2 數(shù)據(jù)庫(kù)中間件唯一ID替換自增主鍵(劃重點(diǎn),敲黑板)

由于分表后,繼續(xù)使用單表的自增主鍵,會(huì)導(dǎo)致全局主鍵沖突。因此,需要使用分布式唯一ID來(lái)代替自增主鍵。各種算法網(wǎng)上比較多,本項(xiàng)目采用的是數(shù)據(jù)庫(kù)自增sequence生成方式。

數(shù)據(jù)庫(kù)自增sequence的分布式ID生成器,是一個(gè)依賴(lài)Mysql的存在, 它的基本原理是在Mysql中存入一個(gè)數(shù)值, 每有一臺(tái)機(jī)器去獲取ID的時(shí)候,都會(huì)在當(dāng)前ID上累加一定的數(shù)量比如說(shuō)2000, 然后把當(dāng)前的值加上2000返回給服務(wù)器。這樣每一臺(tái)機(jī)器都可以繼續(xù)重復(fù)此操作獲得唯一id區(qū)間。

但是僅僅有全局唯一ID就大功告成了嗎?顯然不是,因?yàn)檫@里還會(huì)存在新舊表的id沖突問(wèn)題。

因?yàn)榉?wù)比較多,為了降低風(fēng)險(xiǎn)需要分批上線。因此,存在一部分服務(wù)還是單寫(xiě)舊表的邏輯,一部分服務(wù)是雙寫(xiě)的邏輯。

這樣的狀態(tài)中,舊表的id策略使用的是auto_increment。如果只有單向數(shù)據(jù)來(lái)往的話(舊表到新表),只需要給舊表的id預(yù)留一個(gè)區(qū)間段,sequence從一個(gè)較大的起始值開(kāi)始就能避免沖突。

但該項(xiàng)目中,還有新表數(shù)據(jù)和舊表數(shù)據(jù)的雙寫(xiě),如果采用上述方案,較大的id寫(xiě)入到舊表,舊表的auto_increment將會(huì)被重置到該值,這樣單寫(xiě)舊表的服務(wù)產(chǎn)生的遞增id的記錄必然會(huì)出現(xiàn)沖突。

所以這里交換了雙方的區(qū)間段,舊庫(kù)從較大的auto_increment起始值開(kāi)始,新表選擇的id(也就是sequence的范圍)從大于舊表的最大記錄的id開(kāi)始遞增,小于舊表auto_increment即將設(shè)置的起始值,很好的避免了id沖突問(wèn)題。

1)切換前:

sequence的起始id設(shè)置為當(dāng)前舊表的自增id大小,然后舊表的自增id需要改大,預(yù)留一段區(qū)間,給舊表的自增id繼續(xù)使用,防止未升級(jí)業(yè)務(wù)寫(xiě)入舊表的數(shù)據(jù)同步到新庫(kù)后產(chǎn)生id沖突;

2)切換后

無(wú)需任何改造,斷開(kāi)數(shù)據(jù)同步即可

3)優(yōu)點(diǎn)

只用一份代碼;

切換可以使用開(kāi)關(guān)進(jìn)行,不用升級(jí)改造;

如果萬(wàn)一中途舊表的autoincrement被異常數(shù)據(jù)變大了,也不會(huì)造成什么問(wèn)題。

4)缺點(diǎn)

如果舊表寫(xiě)失敗了,新表寫(xiě)成功了,需要日志輔助處理

4.6 本章小結(jié)

完成舊表下線后,整個(gè)分庫(kù)分表的改造就完成了。

在這個(gè)過(guò)程中,需要始終保持對(duì)線上業(yè)務(wù)的敬畏,仔細(xì)思考每個(gè)可能發(fā)生的問(wèn)題,想好快速回滾方案(在三個(gè)階段提到了projectdb的jar包版本迭代,從1.0.0-SNAPSHOT到3.0.0-SNAPSHOT,包含了每個(gè)階段不同的變更,在不同階段的分批上線的過(guò)程中,通過(guò)jar包版本的方式進(jìn)行回滾,發(fā)揮了巨大作用),避免造成重大故障。

5.穩(wěn)定性保障

這一章主要再次強(qiáng)調(diào)穩(wěn)定性的保障手段。作為本次項(xiàng)目的重要目標(biāo)之一,穩(wěn)定性其實(shí)貫穿在整個(gè)項(xiàng)目周期內(nèi),基本上在上文各個(gè)環(huán)節(jié)都已經(jīng)都有提到,每一個(gè)環(huán)節(jié)都要引起足夠的重視,仔細(xì)設(shè)計(jì)和評(píng)估方案,做到心中有數(shù),而不是靠天吃飯:

1)新表設(shè)計(jì)必須跟業(yè)務(wù)方充分溝通、保證review。

2)對(duì)于“數(shù)據(jù)同步”,必須有數(shù)據(jù)校驗(yàn)保障數(shù)據(jù)正確性,可能導(dǎo)致數(shù)據(jù)不正確的原因上文已經(jīng)提到來(lái)很多,包括實(shí)時(shí)性、一致性的問(wèn)題。保證數(shù)據(jù)正確是上線的大前提。

3)每一階段的變動(dòng),都必須做好快速回滾都預(yù)案。

4)上線過(guò)程,都以分批上線的形式,從非核心業(yè)務(wù)開(kāi)始做試點(diǎn),避免故障擴(kuò)大。

5)監(jiān)控告警要配置全面,出現(xiàn)問(wèn)題及時(shí)收到告警,快速響應(yīng)。不要忽略,很重要,有幾次出現(xiàn)過(guò)數(shù)據(jù)的問(wèn)題,都是通過(guò)告警及時(shí)發(fā)現(xiàn)和解決的。6)單測(cè),業(yè)務(wù)功能測(cè)試等要充分

6.項(xiàng)目管理之跨團(tuán)隊(duì)協(xié)作

關(guān)于“跨團(tuán)隊(duì)協(xié)作”,本文專(zhuān)門(mén)拎出來(lái)作為一章。

因?yàn)樵谶@樣一個(gè)跨團(tuán)隊(duì)的大型項(xiàng)目改造過(guò)程中,科學(xué)的團(tuán)隊(duì)協(xié)作是保障整體項(xiàng)目按時(shí)、高質(zhì)量完成的不可缺少的因素。

下面,分享幾點(diǎn)心得與體會(huì)。

6.1 一切文檔先行

團(tuán)隊(duì)協(xié)作最忌“空口無(wú)憑”。

無(wú)論是團(tuán)隊(duì)分工、進(jìn)度安排或是任何需要多人協(xié)作的事情,都需要有一個(gè)文檔記錄,用于追蹤進(jìn)度,把控流程。

6.2 業(yè)務(wù)溝通與確認(rèn)

所有的表結(jié)構(gòu)改造,必須跟相關(guān)業(yè)務(wù)方溝通,對(duì)于可能存在的歷史邏輯,進(jìn)行全面梳理;

所有討論確定后的字段改造,必須由每個(gè)服務(wù)的Owner進(jìn)行確認(rèn)。

6.3 責(zé)任到位

對(duì)于多團(tuán)隊(duì)多人次的合作項(xiàng)目,每個(gè)團(tuán)隊(duì)都應(yīng)該明確一個(gè)對(duì)接人,由項(xiàng)目總負(fù)責(zé)人與團(tuán)隊(duì)唯一對(duì)接人溝通,明確團(tuán)隊(duì)完整進(jìn)度和完成質(zhì)量。

7.展望

其實(shí),從全文的篇幅就能夠看出,本次的分庫(kù)分表項(xiàng)目由于復(fù)雜的業(yè)務(wù)邏輯改造,費(fèi)大量的時(shí)間和精力,并且非常容易在改造過(guò)程中,引起不穩(wěn)定的線上問(wèn)題。

本文復(fù)盤(pán)了整個(gè)分庫(kù)分表從拆分、設(shè)計(jì)、上線的整體過(guò)程,希望能對(duì)大家有所幫助。

看到這里,我們會(huì)想問(wèn)一句。所以,有沒(méi)有更好的方式呢?

也許,未來(lái)還是需要去結(jié)合業(yè)界新的數(shù)據(jù)庫(kù)中間件技術(shù),能夠快速實(shí)現(xiàn)分庫(kù)分表。

也許,未來(lái)還可以引入新的數(shù)據(jù)存儲(chǔ)技術(shù)與方案(polardb、tidb、hbase),根本不再需要分庫(kù)分表呢?

繼續(xù)跟進(jìn)新技術(shù)的發(fā)展,我相信會(huì)找到答案。

 

責(zé)任編輯:姜華 來(lái)源: 阿丸筆記
相關(guān)推薦

2022-10-10 17:37:59

分庫(kù)分表訂單業(yè)務(wù)

2024-07-10 08:42:39

2022-06-30 07:34:46

分庫(kù)分表外賣(mài)訂單系統(tǒng)

2022-07-01 10:37:18

分庫(kù)分表數(shù)據(jù)庫(kù)

2021-09-08 09:48:39

數(shù)據(jù)庫(kù)工具技術(shù)

2022-10-11 17:51:49

分庫(kù)分表數(shù)據(jù)庫(kù)

2020-07-30 17:59:34

分庫(kù)分表SQL數(shù)據(jù)庫(kù)

2024-07-19 08:34:18

2024-02-26 08:39:39

分庫(kù)分表數(shù)量

2022-10-13 17:43:10

MySQL存放數(shù)據(jù)

2021-07-28 15:44:52

Java開(kāi)發(fā)數(shù)據(jù)庫(kù)

2022-07-04 23:24:28

sql優(yōu)化監(jiān)控

2019-11-12 09:54:20

分庫(kù)分表數(shù)據(jù)

2022-07-08 08:57:36

數(shù)據(jù)優(yōu)化垂直拆分數(shù)據(jù)庫(kù)

2022-01-26 07:59:07

緩存分庫(kù)分表

2020-06-24 09:00:43

分庫(kù)分表MySQL

2022-07-03 19:01:19

磁盤(pán)IOMySQL

2022-01-21 07:56:39

MySQL索引數(shù)據(jù)

2022-01-28 08:59:59

分庫(kù)分表數(shù)據(jù)

2022-10-09 18:14:31

訂單系統(tǒng)分庫(kù)分表
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)