數(shù)據(jù)庫拆分實戰(zhàn)

前言

對遺留系統(tǒng)的微服務(wù)化改造粹断,從整體上來說派敷,整個過程包含兩個部分:一癌压,通過某一種方法論將系統(tǒng)進(jìn)行微服務(wù)劃分仰泻,比如DDD倡導(dǎo)的限界上下文劃分方法。根據(jù)系統(tǒng)的特點和運行狀態(tài)滩届,又分為具體的兩種實施策略集侯,絞殺者模式和修繕模式。二帜消,數(shù)據(jù)庫的拆分棠枉,只有在數(shù)據(jù)層面也拆分開,才能真正達(dá)到服務(wù)化的目的泡挺。具體也可以分為术健,與業(yè)務(wù)服務(wù)拆分同時進(jìn)行,或者等業(yè)務(wù)服務(wù)拆分后再單獨進(jìn)行兩種策略粘衬。

似曾相識的步驟

如果不考慮在拆庫的同時引入新功能荞估,拆庫其實也是一種重構(gòu)。Martin Fowler《Refacotring》中強調(diào)數(shù)據(jù)庫具有高度的耦合性稚新,數(shù)據(jù)庫重構(gòu)存在相當(dāng)?shù)碾y度勘伺。不過好在還有另一本權(quán)威著作來為此背書,那就是《Refactoring Databases》褂删。

來看看這本書提到的數(shù)據(jù)庫重構(gòu)步驟:

  1. Verify that a database refactoring is appropriate
  2. Choose the most appropriate database refactoring
  3. Deprecate the original database schema
  4. Test before, during, and after
  5. Modify the database schema
  6. Migrate the source data
  7. Refactor external access program(s)
  8. Run your regression tests
  9. Version control your work
  10. Announce the refactoring
  11. What you have learned

是不是和代碼的重構(gòu)似曾相識飞醉,分析->測試->修改->測試……

同時也看看我們的數(shù)據(jù)庫拆分實踐是否能和這些步驟有所呼應(yīng)。

背景介紹

我們曾經(jīng)對某客戶企業(yè)的系統(tǒng)做服務(wù)化改造屯阀。根據(jù)其組織架構(gòu)和系統(tǒng)特點缅帘,最終采取了先服務(wù)拆分,再數(shù)據(jù)庫拆分的演進(jìn)路線难衰。

到服務(wù)化改造基本完成時钦无,系統(tǒng)邏輯結(jié)構(gòu)如下圖所示:

右邊的圖完全就是《Refactoring Databases》里說的Multi-Application Database

接下來就是數(shù)據(jù)庫的重構(gòu)了盖袭,也是本文的重點失暂。

分析在前

系統(tǒng)數(shù)據(jù)庫采用MySQL,由于之前是一個大單體鳄虱,所有的數(shù)據(jù)都存在一個數(shù)據(jù)庫里弟塞。隨著業(yè)務(wù)的增長,單庫雖然已經(jīng)使用了頂級的硬件拙已,性能仍顯不足决记。所以不管從架構(gòu)上,還是性能上倍踪,拆庫都迫在眉睫系宫。這也就回答了Verify that a database refactoring is appropriate的問題索昂。

數(shù)據(jù)庫重構(gòu)相對于代碼重構(gòu)畢竟影響更廣,風(fēng)險更大笙瑟。直接采用XP的模式風(fēng)險太大楼镐,必要的分析必不可少癞志,整個過程力求一次正確往枷。

首先必須了解數(shù)據(jù)庫的全貌,經(jīng)過一番溝通梳理出架構(gòu)圖如下:


整個數(shù)據(jù)庫由主庫凄杯,備庫错洁,歷史庫,歸檔庫組成戒突,備庫主要用于監(jiān)控和BI等屯碴,歷史庫用于存放達(dá)到某個狀態(tài)后的訂單數(shù)據(jù),主庫和歸檔庫由于歷史原因都會被業(yè)務(wù)服務(wù)訪問膊存。歸檔庫性能相對較差导而,只用于歸檔數(shù)據(jù)。

主庫隔崎,歷史庫今艺,歸檔庫之間可以互相遷移數(shù)據(jù),遷移代碼是完全自研的爵卒,支持單個訂單的一系列數(shù)據(jù)遷移虚缎,也支持批量的訂單遷移。

業(yè)務(wù)服務(wù)的遷移歷經(jīng)一年多的時間钓株,單體也進(jìn)化成了十幾個微服務(wù)实牡,要想一次性把數(shù)據(jù)庫全部拆開不太現(xiàn)實,風(fēng)險也不可控轴合。最好的方式是找出當(dāng)前數(shù)據(jù)庫的瓶頸创坞,先將業(yè)務(wù)上訪問量最大的部分拆開。經(jīng)過調(diào)研受葛,決定先將數(shù)據(jù)庫一分為二摆霉,先將發(fā)貨單拆出去,類似于修繕模式奔坟,訂單及其他數(shù)據(jù)先保留携栋。這也呼應(yīng)了Choose the most apporiate database refactoring,所以設(shè)想拆分后的數(shù)據(jù)庫應(yīng)該如下圖所示:


從圖上不難看出咳秉,需要修改的點包括:

1. 業(yè)務(wù)代碼

1.1 發(fā)貨單服務(wù)的數(shù)據(jù)庫配置

1.2 所有類似join查詢的級聯(lián)操作婉支,主要集中在頁面查詢,導(dǎo)出澜建,報表等向挖。(寫入操作在微服務(wù)拆分時基本已經(jīng)修改)

2. 數(shù)據(jù)

2.1 新建發(fā)貨單數(shù)據(jù)庫蝌以,schema和用戶

2.2 已有的發(fā)貨單相關(guān)的數(shù)據(jù)遷移至新數(shù)據(jù)庫

3. 遷移代碼

3.1 源庫和目標(biāo)庫都要支持多源配置,現(xiàn)在是一分二何之,將來會更多

3.2 遷移邏輯跟畅,要保證數(shù)據(jù)一致性。即一個訂單的數(shù)據(jù)要么全在主庫溶推,要么全在歷史庫或歸檔庫

4. 監(jiān)控和BI

4.1 多源配置

4.2 監(jiān)控邏輯修改

中間過程

通知上下游

這一點非常重要徊件,在一個涉及多部門的系統(tǒng)上做數(shù)據(jù)庫遷移。有些影響絕不是靠自己就能考慮周全的蒜危,在做所有遷移之前就要通知各方評估各自的影響和改動周期虱痕。

業(yè)務(wù)代碼的修改

  1. 測試先行

重構(gòu)最重要的一點是不改變程序的外在表現(xiàn)。對于數(shù)據(jù)庫重構(gòu)來說辐赞,只需要保證對外暴露的API在特定輸入下部翘,輸出是一致的。

在這個點上响委,測試是比較容易寫的新思。自動化測試和人工測試同時展開,以黑盒集成測試為主赘风。在每個API修改之前先根據(jù)現(xiàn)有結(jié)果編寫測試夹囚,同時QA記錄輸入,輸出贝次,注意各種邊界情況的測試崔兴。在這個階段基本忽略現(xiàn)有代碼邏輯的正確性,先保證拆庫前后API的行為一致蛔翅。

當(dāng)然敲茄,這是理想的情況,在真正開始做以后山析,就會發(fā)現(xiàn)情況并不是這么簡單堰燎。

  1. 業(yè)務(wù)代碼修改

指導(dǎo)思想是將級聯(lián)查詢修改為API調(diào)用補齊數(shù)據(jù)。然而這里面有一個特殊情況笋轨,當(dāng)遇到j(luò)oin秆剪,groupBy,有where條件爵政,再加上分頁的場景仅讽,API調(diào)用補齊數(shù)據(jù)的方式就不能很好的處理。
說說當(dāng)時的幾種處理辦法:

  • 非批量的查詢钾挟,通過API補齊數(shù)據(jù)洁灵。
  • 批量查詢,但是級聯(lián)的數(shù)據(jù)不在過濾條件中掺出,通過API補齊數(shù)據(jù)徽千。根據(jù)性能和調(diào)用頻率考慮加緩存苫费。
  • 批量查詢,級聯(lián)數(shù)據(jù)在過濾條件中双抽,沒有分頁(隱含的意思是數(shù)據(jù)量邪倏颉),通過API先拿到數(shù)據(jù)牍汹,在內(nèi)存中處理铐维。
  • 批量查詢,有過濾柑贞,有分頁方椎。跟業(yè)務(wù)溝通是否能在查詢結(jié)果中刪除級聯(lián)的數(shù)據(jù),如果不行,是否能在過濾條件中刪除級聯(lián)的數(shù)據(jù)笔时。

實際操作下來搁胆,發(fā)現(xiàn)其實業(yè)務(wù)上并沒有設(shè)想的那么難。首先只有個別API存在這種情況匈庭,其次這些API的一些字段可能是一些歷史原因造成的,刪除對現(xiàn)有的業(yè)務(wù)影響并不大。

當(dāng)然如果最終無法在業(yè)務(wù)上達(dá)成一致书幕,那就要考慮在報表庫和數(shù)據(jù)湖層面做聚合了,方法總是有的揽趾。

數(shù)據(jù)遷移

  1. 開發(fā)過程
    過程中有三種方法:
  • 同一個物理庫台汇,保持相同的schema,不同的用戶通過授權(quán)不同的表篱瞎,達(dá)到邏輯劃分的目的苟呐。例如為發(fā)貨單服務(wù)新建一個數(shù)據(jù)庫用戶,只把發(fā)貨單相關(guān)的表授權(quán)給它訪問俐筋。當(dāng)前用戶收回訪問發(fā)貨單表的權(quán)限牵素。

優(yōu)點:簡單易操作,開發(fā)過程無需做數(shù)據(jù)遷移澄者。

缺點:邏輯劃分畢竟不能完全模擬真實的生產(chǎn)環(huán)境笆呆。例如有些表是多個服務(wù)共享的,開發(fā)時只能多個用戶同時授權(quán)粱挡。如果業(yè)務(wù)代碼修改不徹底赠幕,就會出現(xiàn)一個服務(wù)寫入,其他服務(wù)讀取的情況询筏。一旦上了生產(chǎn)榕堰,表做了物理隔離,就會造成讀取不到數(shù)據(jù)的事故屈留。
grant select,insert,update,delete on existing_schema.existing_table to 'new_user'@'%';

  • 同一個物理庫局冰,不同的schema测蘑,可以保持相同的用戶,這樣修改較少康二。

優(yōu)點:幾乎可以模擬生產(chǎn)數(shù)據(jù)庫碳胳,業(yè)務(wù)代碼好排查,畢竟新加的schema在之前的業(yè)務(wù)代碼里沒有沫勿,很容易測試發(fā)現(xiàn)問題挨约。

缺點:需要將部分表遷移至新schema。如果是MySQL产雹,在不同schema之間遷移表還是比較容易的诫惭。例如:
alter table existing_schema.existing_table rename new_schema.existing_table;

  • 不同的物理庫,schema是否修改取決于當(dāng)前名稱是否表意蔓挖。

優(yōu)點:完全模擬生產(chǎn)數(shù)據(jù)庫

缺點:不同物理庫之間要做數(shù)據(jù)遷移

回頭看夕土,有條件的情況下第三種方法最為保險。第二種方法性價比最高瘟判。

  1. 生產(chǎn)數(shù)據(jù)遷移過程

由于是線上運行系統(tǒng)怨绣,版本上線窗口時間有限,不可能在上線當(dāng)晚執(zhí)行全部的數(shù)據(jù)遷移拷获,所以必須提前做篮撑,這樣數(shù)據(jù)質(zhì)量也有保證。


這里也有兩種方法來做主備遷移:

  • 利用MySQL的主從機制來同步匆瓜,需要注意的是赢笨,在發(fā)貨單主庫(上線之前是主庫的從庫之一)需要打開--log-slave-updates,否則無法再接一個從庫驮吱。
  • 利用第三方數(shù)據(jù)庫同步工具茧妒,這類工具常常會帶有UI,相對比較友好糠馆。

這樣在上線前就可以不斷檢查數(shù)據(jù)遷移的質(zhì)量嘶伟,上線當(dāng)晚只需要很短時間的停機,甚至不停機又碌。上線后兩個主庫都包含了很多彼此的歷史數(shù)據(jù)九昧,可以不急于刪除,以防需要回滾毕匀。

主備庫的遷移代碼修改

之前要遷移的表都在一個數(shù)據(jù)庫里铸鹰,遷移可以用一個事務(wù)來保證同時成功或失敗。現(xiàn)在分布在兩個庫里皂岔,只能通過最終一致性來保證蹋笼。

像以往的AP系統(tǒng)的處理方法,事件表加消息隊列,訂單的遷移觸發(fā)發(fā)貨單的遷移剖毯。實際修改過程中還碰到很多具體問題圾笨,發(fā)了兩次消息才最終達(dá)成一致,不過這些都是細(xì)節(jié)了逊谋。

這里需要提醒的是擂达,遷移程序的數(shù)據(jù)庫信息最好都是可配置的,以防上線過程中數(shù)據(jù)庫地址胶滋、schema板鬓、用戶名等臨時變更。

監(jiān)控和BI

這些在當(dāng)時的上下文下優(yōu)先級偏低究恤,在第一次上線時俭令,對于不能及時調(diào)整的,都先做了屏蔽處理部宿,不過處理的思路和上面類似抄腔。

以上的這些步驟基本上和《Refactoring Databases》中提到的如下步驟不謀而合。

Test before, during, and after

Modify the database schema

Migrate the source data

Refactor external access program(s)

Run your regression tests

Version control your work

Announce the refactoring

上線

因為上線之前發(fā)貨單的數(shù)據(jù)庫一直在同步主庫的數(shù)據(jù)窟赏,并且上線過程中同步仍然保持妓柜,理論上上線可以做到不停機箱季。但是如果存在高并發(fā)的情況涯穷,主從binlog同步延遲大,很可能會造成部分臟數(shù)據(jù)藏雏,保險起見短暫的停機上線比較安全拷况。

其實上面提到的問題,理論上是新老兩個版本同時在線上運行造成的掘殴。其中一個典型的例子就是灰度發(fā)布赚瘦,但是灰度發(fā)布往往在數(shù)據(jù)上是隔離的,唯一要考慮的是配置項能不能區(qū)分開奏寨,因為當(dāng)前微服務(wù)往往會從配置服務(wù)器中取配置起意。如果配置項中不支持灰度實例配置項,就要特別注意病瞳。

這里也有兩種方法可以選擇:

  • 新版本讀取另一個變量揽咕。例如老版本讀取DB_URL,新版本讀取DB_URL_NEW套菜。通過一份配置亲善,多個配置項名稱的方式區(qū)分開。
  • 如果是容器化部署的話逗柴,容器的環(huán)境變量通常優(yōu)先級更高蛹头,在容器層設(shè)置變量覆蓋掉Configuration server中的變量。

第二種方法會導(dǎo)致配置項分散,所以優(yōu)先選擇第一種渣蜗。

另外屠尊,上線之后生產(chǎn)環(huán)境的測試必不可少。測試環(huán)境再如何測試也不能百分百保證生產(chǎn)環(huán)境一定沒問題耕拷。條件允許的情況下知染,多測一些修改過的地方和系統(tǒng)的關(guān)鍵功能。

總結(jié)

回顧整個拆庫流程斑胜,整體的策略還是對的控淡。先找到數(shù)據(jù)庫的瓶頸,把一部分拆分出去止潘,梳理清楚整個流程掺炭,之后進(jìn)一步的細(xì)分,就水到渠成了凭戴。

但是數(shù)據(jù)庫重構(gòu)和代碼重構(gòu)有相似之處涧狮,也有不同之處。

相似之處在于修改的過程中基本的思路是一致的么夫,測試->修改->測試者冤,小步快跑,反復(fù)迭代档痪。

不同之處在于拆庫還依賴于硬件的基礎(chǔ)設(shè)施涉枫,這就更要求測試環(huán)境盡量去模擬生產(chǎn)環(huán)境「總結(jié)下來愿汰,整個過程出了兩個問題都是沒有完全模擬生產(chǎn)環(huán)境導(dǎo)致的:

  • 測試環(huán)境通過不同用戶授權(quán)的形式做邏輯劃分,導(dǎo)致有一張表存在一個服務(wù)寫入乐纸,其他服務(wù)讀取的情況沒有在測試環(huán)境發(fā)現(xiàn)衬廷。
  • 測試環(huán)境的schema和生成環(huán)境的名稱不一致,導(dǎo)致漏掉了歷史庫遷移程序的修改汽绢。

好在這兩個問題都及時發(fā)現(xiàn)吗跋,并很快糾正了過來。

在實際中宁昭,可能每個拆庫的場景都不盡相同跌宛,沒有絕對適用的流程方法,需要因地制宜久窟,靈活操作秩冈。

最后,不管是業(yè)務(wù)代碼的修改還是數(shù)據(jù)庫的修改斥扛,最怕的是有些場景沒想到入问。一旦想到了丹锹,解決的辦法總是有的。

希望本文介紹的實踐對你有幫助芬失。


文/Thoughtworks馮博
原文鏈接:https://insights.thoughtworks.cn/database-split-practice/
更多精彩洞見楣黍,請關(guān)注微信公眾號Thoughtworks洞見。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末棱烂,一起剝皮案震驚了整個濱河市租漂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌颊糜,老刑警劉巖哩治,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異衬鱼,居然都是意外死亡业筏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門鸟赫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒜胖,“玉大人,你說我怎么就攤上這事抛蚤√ㄐ唬” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵岁经,是天一觀的道長朋沮。 經(jīng)常有香客問我,道長蒿偎,這世上最難降的妖魔是什么朽们? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮诉位,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菜枷。我一直安慰自己苍糠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布啤誊。 她就那樣靜靜地躺著岳瞭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚊锹。 梳的紋絲不亂的頭發(fā)上瞳筏,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音牡昆,去河邊找鬼姚炕。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柱宦。 我是一名探鬼主播些椒,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掸刊!你這毒婦竟也來了免糕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤忧侧,失蹤者是張志新(化名)和其女友劉穎石窑,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚓炬,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡尼斧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了试吁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棺棵。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖熄捍,靈堂內(nèi)的尸體忽然破棺而出烛恤,到底是詐尸還是另有隱情,我是刑警寧澤余耽,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布缚柏,位于F島的核電站,受9級特大地震影響碟贾,放射性物質(zhì)發(fā)生泄漏币喧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一袱耽、第九天 我趴在偏房一處隱蔽的房頂上張望杀餐。 院中可真熱鬧,春花似錦朱巨、人聲如沸史翘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琼讽。三九已至,卻和暖如春洪唐,著一層夾襖步出監(jiān)牢的瞬間钻蹬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工凭需, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留问欠,地道東北人肝匆。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像溅潜,于是被迫代替她去往敵國和親术唬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容