引言
我們先來講一個段子
面試官:“有并發(fā)的經(jīng)驗沒?”
應聘者:“有一點。”
面試官:“那你們?yōu)榱颂幚聿l(fā),做了哪些優(yōu)化?”
應聘者:“前后端分離啊,限流啊,分庫分表啊。。”
面試官:"談談分庫分表吧?"
應聘者:“bala。bala。bala。?!?/strong>
面試官心理活動:這個仁兄講的怎么這么像網(wǎng)上的博客抄的,容我再問問。
面試官:“你們分庫分表后,如何部署上線的?”
應聘者:“這?。。。。?!”
在網(wǎng)上看了很多關于 分庫分表的文章,很神奇的是,都在講怎么進行分庫分表,卻不說分完以后,怎么部署上線的。這樣在面試的時候就比較尷尬了。
你們自己摸著良心想一下,如果你真的做過分庫分表,你會不知道如何部署的么?因此我們來學習一下如何部署吧。
ps:我發(fā)現(xiàn)一個很神奇的現(xiàn)象。因為很多公司用的技術(shù)比較low,那么一些求職者為了提高自己的競爭力,就會將一些高大上的技術(shù)寫進自己的low項目中。然后呢,他出去面試害怕碰到從這個公司出來的人,畢竟從這個公司出來的人,一定知道自己以前公司的項目情形。因此為了圓謊,他就會說:“他們從事的是這個公司的老項目改造工作,用了很多新技術(shù)進去!”
那么,請你好好思考一下,你們的老系統(tǒng)是如何平滑升級為新系統(tǒng)的!
如何部署
停機部署法
大致思路就是,掛一個公告,半夜停機升級,然后半夜把服務停了,跑數(shù)據(jù)遷移程序,進行數(shù)據(jù)遷移。
步驟如下:
(1)出一個公告,比如“今晚00:00~6:00進行停機維護,暫停服務”
(2)寫一個遷移程序,讀db-old數(shù)據(jù)庫,通過中間件寫入新庫db-new1和db-new2,具體如下圖所示

(3)校驗遷移前后一致性,沒問題就切該部分業(yè)務到新庫。
順便科普一下,這個中間件?,F(xiàn)在流行的分庫分表的中間件有兩種,一種是proxy形式的,例如mycat,是需要額外部署一臺服務器的。還有一種是client形式的,例如當當出的Sharding-JDBC,就是一個jar包,使用起來十分輕便。我個人偏向Sharding-JDBC,這種方式,無需額外部署,無其他依賴,DBA也無需改變原有的運維方式。
評價:
大家不要覺得這種方法low,我其實一直覺得這種方法可靠性很強。而且我相信各位讀者所在的公司一定不是什么很牛逼的互聯(lián)網(wǎng)公司,如果你們的產(chǎn)品凌晨1點的用戶活躍數(shù)還有超過1000的,你們握個爪!畢竟不是所有人都在什么電商公司的,大部分產(chǎn)品半夜都沒啥流量。所以此方案,并非沒有可取之處。
但是此方案有一個缺點,累!不止身體累,心也累!你想想看,本來定六點結(jié)束,你五點把數(shù)據(jù)庫遷移好,但是不知怎么滴,程序切新庫就是有點問題。于是,眼瞅著天就要亮了,趕緊把數(shù)據(jù)庫切回老庫。第二個晚上繼續(xù)這么干,簡直是身心俱疲。
ps:這里教大家一些技巧啊,如果你真的沒做過分庫分表,又想吹一波,漲一下工資,建議答這個方案。因為這個方案比較low,low到?jīng)]什么東西可以深挖的,所以答這個方案,比較靠譜。
另外,如果面試官的問題是
你們怎么進行分庫分表的?
這個問題問的很泛,所以回答這個問題建議自己主動把分表的策略,以及如何部署的方法講出來。因為這么答,顯得嚴謹一些。
不過,很多面試官為了賣弄自己的技術(shù),喜歡這么問
分表有哪些策略???你們用哪種?。?/strong>
ok。。這個問題具體指向了分庫分表的某個方向了,你不要主動答如何進行部署的。等面試官問你,你再答。如果面試官沒問,在面試最后一個環(huán)節(jié),面試官會讓你問讓幾個問題。你就問
你剛才剛好有提到分庫分表的相關問題,我們當時部署的時候,先停機。然后半夜遷移數(shù)據(jù),然后第二天將流量切到新庫,這種方案太累,不知道貴公司有沒有什么更好的方案?
那么這種情況下,面試官會有兩種回答。第一種,面試官硬著頭皮隨便扯。第二種,面試官真的做過,據(jù)實回答。記住,面試官怎么回答的不重要。重點的是,你這個問題出去,會給面試官一種錯覺:"這個小伙子真的做過分庫分表。"
如果你擔心進去了,真派你去做分庫分表怎么辦?OK,不要怕。我賭你試用期碰不到這個活。因為能進行分庫分表,必定對業(yè)務非常熟。還在試用期的你,必定對業(yè)務不熟,如果領導給你這種活,我只能說他有一顆大心臟。
ok,指點到這里。面試本來就是一場斗智斗勇的過程,扯遠了,回到我們的主題。
雙寫部署法(一)
這個就是不停機部署法,這里我需要先引進兩個概念:歷史數(shù)據(jù)和增量數(shù)據(jù)。
假設,我們是對一張叫做test_tb的表進行拆分,因為你要進行雙寫,系統(tǒng)里頭和test_tb表有關的業(yè)務之前必定會加入一段雙寫代碼,同時往老庫和新庫中寫,然后進行部署,那么
歷史數(shù)據(jù):在該次部署前,數(shù)據(jù)庫表test_tb的有關數(shù)據(jù),我們稱之為歷史數(shù)據(jù)。
增量數(shù)據(jù):在該次部署后,數(shù)據(jù)庫表test_tb的新產(chǎn)生的數(shù)據(jù),我們稱之為增量數(shù)據(jù)。
然后遷移流程如下
(1)先計算你要遷移的那張表的max(主鍵)。在遷移過程中,只遷移db-old中test_tb表里,主鍵小等于該max(主鍵)的值,也就是所謂的歷史數(shù)據(jù)。
這里有特殊情況,如果你的表用的是uuid,沒法求出max(主鍵),那就以創(chuàng)建時間作為劃分歷史數(shù)據(jù)和增量數(shù)據(jù)的依據(jù)。如果你的表用的是uuid,又沒有創(chuàng)建時間這個字段,我相信機智的你,一定有辦法區(qū)分出歷史數(shù)據(jù)和增量數(shù)據(jù)。
(2)在代碼中,與test_tb有關的業(yè)務,多加一條往消息隊列中發(fā)消息的代碼,將操作的sql發(fā)送到消息隊列中,至于消息體如何組裝,大家自行考慮。需要注意的是,只發(fā)寫請求的sql,只發(fā)寫請求的sql,只發(fā)寫請求的sql。重要的事情說三遍!
原因有二:
(1)只有寫請求的sql對恢復數(shù)據(jù)才有用。
(2)系統(tǒng)中,絕大部分的業(yè)務需求是讀請求,寫請求比較少。
注意了,在這個階段,我們不消費消息隊列里的數(shù)據(jù)。我們只發(fā)寫請求,消息隊列的消息堆積情況不會太嚴重!
(3)系統(tǒng)上線。另外,寫一段遷移程序,遷移db-old中test_tb表里,主鍵小于該max(主鍵)的數(shù)據(jù),也就是所謂的歷史數(shù)據(jù)。
上面步驟(1)~步驟(3)的過程如下

等到
db-old
中的歷史數(shù)據(jù)遷移完畢,則開始遷移增量數(shù)據(jù),也就是在消息隊列里的數(shù)據(jù)。
(4)將遷移程序下線,寫一段訂閱程序訂閱消息隊列中的數(shù)據(jù)
(5)訂閱程序?qū)⒂嗛喌降綌?shù)據(jù),通過中間件寫入新庫
(6)新老庫一致性驗證,去除代碼中的雙寫代碼,將涉及到
test_tb
表的讀寫操作,指向新庫。
上面步驟(4)~步驟(6)的過程如下

這里大家可能會有一個問題,在步驟(1)~步驟(3),系統(tǒng)對歷史數(shù)據(jù)進行操作,會造成不一致的問題么?
OK,不會。這里我們對delete操作和update操作做分析,因為只有這兩個操作才會造成歷史數(shù)據(jù)變動,insert進去的數(shù)據(jù)都是屬于增量數(shù)據(jù)。
(1)對db-old中test_tb表的歷史數(shù)據(jù)發(fā)出delete操作,數(shù)據(jù)還未刪除,就被遷移程序給遷走了。此時delete操作在消息隊列里還有記錄,后期訂閱程序訂閱到該delete操作,可以進行刪除。
(2)對db-old中test_tb表的歷史數(shù)據(jù)發(fā)出delete操作,數(shù)據(jù)已經(jīng)刪除,遷移程序遷不走該行數(shù)據(jù)。此時delete操作在消息隊列里還有記錄,后期訂閱程序訂閱到該delete操作,再執(zhí)行一次delete,并不會對一致性有影響。
對update的操作類似,不贅述。
雙寫部署法(二)
上面的方法有一個硬傷,注意我有一句話
(2)在代碼中,與test_tb有關的業(yè)務,多加一條往消息隊列中發(fā)消息的代碼,將操作的sql發(fā)送到消息隊列中,至于消息體如何組裝,大家自行考慮。
大家想一下,這么做,是不是造成了嚴重的代碼入侵。將非業(yè)務代碼嵌入業(yè)務代碼,這么做,后期刪代碼的時候特別累。
有沒什么方法,可以避免這個問題的?
有的,訂閱binlog日志。關于binlog日志,我盡量下周寫一篇《研發(fā)應該掌握的binlog知識》,這邊我就介紹一下作用
記錄所有數(shù)據(jù)庫表結(jié)構(gòu)變更(例如CREATE、ALTER TABLE…)以及表數(shù)據(jù)修改(INSERT、UPDATE、DELETE…)的二進制日志。binlog不會記錄SELECT和SHOW這類操作,因為這類操作對據(jù)本身并沒有修改。
還記得我們在雙寫部署法(一)里介紹的,往消息隊列里發(fā)的消息,都是寫操作的消息。而binlog日志記錄的也是寫操作。所以訂閱該日志,也能滿足我們的需求。
于是步驟如下
(1)打開binlog日志,系統(tǒng)正常上線就好
(2)還是寫一個遷移程序,遷移歷史數(shù)據(jù)。步驟和上面類似,不啰嗦了。
步驟(1)~步驟(2)流程圖如下

(3)寫一個訂閱程序,訂閱binlog(mysql中有canal)。然后將訂閱到的數(shù)據(jù)通過中間件,寫入新庫。
(4)檢驗一致性,沒問題就切庫。
步驟(3)~步驟(4)流程圖如下
