資料庫分庫分表後,如何部署上線?
1. 引言
我們先來講一個段子
面試官:“有併發的經驗沒?”
應聘者:“有一點。”
面試官:“那你們為了處理併發,做了哪些優化?”
應聘者:“前後端分離啊,限流啊,分庫分表啊。。”
面試官:"談談分庫分表吧?"
應聘者:“bala。bala。bala。。”
面試官心理活動:這個仁兄講的怎麼這麼像網上的部落格抄的,容我再問問。
面試官:“你們分庫分表後,如何部署上線的?”
應聘者:“這!!!!!!”
不要驚訝,寫這篇文章前,我特意去網上看了下分庫分表的文章,很神奇的是,都在講怎麼進行分庫分表,卻不說分完以後,怎麼部署上線的。這樣在面試的時候就比較尷尬了。
你們自己摸著良心想一下,如果你真的做過分庫分表,你會不知道如何部署的麼?因此我們來學習一下如何部署吧。
ps: 我發現一個很神奇的現象。因為很多公司用的技術比較low,那麼一些求職者為了提高自己的競爭力,就會將一些高大上的技術寫進自己的low專案中。然後呢,他出去面試害怕碰到從這個公司出來的人,畢竟從這個公司出來的人,一定知道自己以前公司的專案情形。因此為了圓謊,他就會說:“他們從事的是這個公司的老專案改造工作,用了很多新技術進去!”
那麼,請你好好思考一下,你們的老系統是如何平滑升級為新系統的!
2. 如何部署
2.1 停機部署法
大致思路就是,掛一個公告,半夜停機升級,然後半夜把服務停了,跑資料遷移程式,進行資料遷移。
步驟如下:
(1)出一個公告,比如“今晚00:00~6:00進行停機維護,暫停服務”
(2)寫一個遷移程式,讀 db-old 資料庫,通過中介軟體寫入新庫 db-new1 和 db-new2 ,具體如下圖所示
(3)校驗遷移前後一致性,沒問題就切該部分業務到新庫。
順便科普一下,這個中介軟體。現在流行的分庫分表的中介軟體有兩種,一種是 proxy 形式的,例如 mycat ,是需要額外部署一臺伺服器的。還有一種是 client 形式的,例如噹噹出的 Sharding-JDBC ,就是一個jar包,使用起來十分輕便。我個人偏向 Sharding-JDBC ,這種方式,無需額外部署,無其他依賴,DBA也無需改變原有的運維方式。
評價:
大家不要覺得這種方法low,我其實一直覺得這種方法可靠性很強。而且我相信各位讀者所在的公司一定不是什麼很牛逼的網際網路公司,如果你們的產品凌晨1點的使用者活躍數還有超過1000的,你們握個爪!畢竟不是所有人都在什麼電商公司的,大部分產品半夜都沒啥流量。所以此方案,並非沒有可取之處。
但是此方案有一個缺點, 累! 不止身體累,心也累!你想想看,本來定六點結束,你五點把資料庫遷移好,但是不知怎麼滴,程式切新庫就是有點問題。於是,眼瞅著天就要亮了,趕緊把資料庫切回老庫。第二個晚上繼續這麼幹,簡直是身心俱疲。
ps: 這裡教大家一些技巧啊,如果你真的沒做過分庫分表,又想吹一波,漲一下工資,建議答這個方案。因為這個方案比較low,low到沒什麼東西可以深挖的,所以答這個方案,比較靠譜。
另外,如果面試官的問題是
你們怎麼進行分庫分表的?
這個問題問的很泛,所以回答這個問題建議自己主動把分表的策略,以及如何部署的方法講出來。因為這麼答,顯得嚴謹一些。
不過,很多面試官為了賣弄自己的技術,喜歡這麼問
分表有哪些策略啊?你們用哪種啊?
ok。。這個問題具體指向了分庫分表的某個方向了,你不要主動答如何進行部署的。等面試官問你,你再答。如果面試官沒問,在面試最後一個環節,面試官會讓你問讓幾個問題。你就問
你剛才剛好有提到分庫分表的相關問題,我們當時部署的時候,先停機。然後半夜遷移資料,然後第二天將流量切到新庫,這種方案太累,不知道貴公司有沒有什麼更好的方案?
那麼這種情況下,面試官會有兩種回答。第一種,面試官硬著頭皮隨便扯。第二種,面試官真的做過,據實回答。記住,面試官怎麼回答的不重要。重點的是,你這個問題出去,會給面試官一種錯覺:"這個小夥子真的做過分庫分表。"
如果你擔心進去了,真派你去做分庫分表怎麼辦?OK,不要怕。我賭你試用期碰不到這個活。因為能進行分庫分表,必定對業務非常熟。還在試用期的你,必定對業務不熟,如果領導給你這種活,我只能說他有一顆大心臟。
ok,指點到這裡。面試本來就是一場鬥智鬥勇的過程,扯遠了,回到我們的主題。
2.2 雙寫部署法(一)
這個就是不停機部署法,這裡我需要先引進兩個概念: 歷史資料 和 增量資料 。
假設,我們是對一張叫做 test_tb 的表進行拆分,因為你要進行雙寫,系統裡頭和 test_tb表有關的業務之前必定會加入一段雙寫程式碼,同時往老庫和新庫中寫,然後進行部署,那麼
歷史資料:在該次部署前,資料庫表 test_tb 的有關資料,我們稱之為歷史資料。
增量資料:在該次部署後,資料庫表 test_tb 的新產生的資料,我們稱之為增量資料。
然後遷移流程如下
(1)先計算你要遷移的那張表的 max(主鍵) 。在遷移過程中,只遷移 db-old 中 test_tb 表裡,主鍵小於等於該 max(主鍵) 的值,也就是所謂的歷史資料。
這裡有特殊情況,如果你的表用的是uuid,沒法求出 max(主鍵) ,那就以建立時間作為劃分歷史資料和增量資料的依據。如果你的表用的是uuid,又沒有建立時間這個欄位,我相信機智的你,一定有辦法區分出歷史資料和增量資料。
(2)在程式碼中,與 test_tb 有關的業務,多加一條往訊息佇列中發訊息的程式碼,將操作的sql傳送到訊息佇列中,至於訊息體如何組裝,大家自行考慮。 需要注意的是, 只發寫請求的sql,只發寫請求的sql,只發寫請求的sql。重要的事情說三遍!
原因有二:
-
(1)只有寫請求的sql對恢復資料才有用。
-
(2)系統中,絕大部分的業務需求是讀請求,寫請求比較少。
注意了,在這個階段,我們不消費訊息佇列裡的資料。我們只發寫請求,訊息佇列的訊息堆積情況不會太嚴重!
(3)系統上線。另外,寫一段遷移程式,遷移 db-old 中 test_tb 表裡,主鍵小於該 max(主鍵)的資料,也就是所謂的歷史資料。
上面步驟(1)~步驟(3)的過程如下
等到 db-old 中的歷史資料遷移完畢,則開始遷移增量資料,也就是在訊息佇列裡的資料。
(4)將遷移程式下線,寫一段訂閱程式訂閱訊息佇列中的資料
(5)訂閱程式將訂閱到的資料,通過中介軟體寫入新庫
(6)新老庫一致性驗證,去除程式碼中的雙寫程式碼,將涉及到 test_tb 表的讀寫操作,指向新庫。
上面步驟(4)~步驟(6)的過程如下
這裡大家可能會有一個問題,在步驟(1)~步驟(3),系統對歷史資料進行操作,會造成不一致的問題麼?
OK,不會。這裡我們對 delete 操作和 update 操作做分析,因為只有這兩個操作才會造成歷史資料變動, insert 進去的資料都是屬於增量資料。
(1)對 db-old 的 test_tb 錶的歷史資料發出 delete 操作,資料還未刪除,就被遷移程式給遷走了。此時 delete 操作在訊息佇列裡還有記錄,後期訂閱程式訂閱到該 delete 操作,可以進行刪除。
(2)對 db-old 的 test_tb 錶的歷史資料發出 delete 操作,資料已經刪除,遷移程式遷不走該行資料。此時 delete 操作在訊息佇列裡還有記錄,後期訂閱程式訂閱到該 delete 操作,再執行一次 delete ,並不會對一致性有影響。
對 update 的操作類似,不贅述。
2.3 雙寫部署法(二)
上面的方法有一個硬傷,注意我有一句話
(2)在程式碼中,與test_tb有關的業務,多加一條往訊息佇列中發訊息的程式碼,將操作的sql傳送到訊息佇列中,至於訊息體如何組裝,大家自行考慮。
大家想一下,這麼做,是不是造成了嚴重的程式碼入侵。將非業務程式碼嵌入業務程式碼,這麼做,後期刪程式碼的時候特別累。
有沒什麼方法,可以避免這個問題的?
有的,訂閱 binlog 日誌。關於 binlog 日誌,我儘量下週寫一篇《研發應該掌握的binlog知識》,這邊我就介紹一下作用
記錄所有資料庫表結構變更(例如CREATE、ALTER TABLE…)以及表資料修改(INSERT、UPDATE、DELETE…)的二進位制日誌。binlog不會記錄SELECT和SHOW這類操作,因為這類操作對據本身並沒有修改。
還記得我們在 雙寫部署法(一) 裡介紹的,往訊息佇列裡發的訊息,都是寫操作的訊息。而 binlog 日誌記錄的也是寫操作。所以訂閱該日誌,也能滿足我們的需求。
於是步驟如下
(1)開啟binlog日誌,系統正常上線就好
(2)還是寫一個遷移程式,遷移歷史資料。步驟和上面類似,不囉嗦了。
步驟(1)~步驟(2)流程圖如下
(3)寫一個訂閱程式,訂閱binlog(mysql中有 canal 。至於oracle中,大家就隨緣自己寫吧)。然後將訂閱到到資料通過中介軟體,寫入新庫。
(4)檢驗一致性,沒問題就切庫。
步驟(3)~步驟(4)流程圖如下
怎麼驗資料一致性
這裡大概介紹一下吧,這篇的篇幅太長了,大家心裡有底就行。
(1)先驗數量是否一致,因為驗數量比較快。
至於驗具體的欄位,有兩種方法:
(2.1)有一種方法是,只驗關鍵性的幾個欄位是否一致。
(2.2)還有一種是 ,一次取50條(不一定50條,具體自己定,我只是舉例),然後像拼字串一樣,拼在一起。用md5進行加密,得到一串數值。新庫一樣如法炮製,也得到一串數值,比較兩串數值是否一致。如果一致,繼續比較下50條資料。如果發現不一致,用二分法確定不一致的資料在0-25條,還是26條-50條。以此類推,找出不一致的資料,進行記錄即可。