背景
一般來說,單mysql的寫入瓶頸在3000-5000條每秒寞酿,讀qps在3w-10w。單表的存儲極限在2000w條脱柱,全庫的極限在4-5億條伐弹。
我們在構(gòu)建一個新業(yè)務(wù)時,會評估這個業(yè)務(wù)的總體數(shù)據(jù)量榨为,讀寫高峰請求量等等惨好。大多數(shù)時候,對于to B業(yè)務(wù)柠逞,單庫單表策略昧狮,是可以滿足需求的;對于to C業(yè)務(wù)板壮,單庫分表策略一般也足夠了逗鸣。
用數(shù)據(jù)庫去抗特別大的用戶流量,往往都不是一個好的設(shè)計绰精,一般都是通過增加redis/memcache/localCache的緩存層來解決撒璧。
但對于一些比較特殊的業(yè)務(wù),比如涉及到金錢的笨使,對一致性要求特別高的業(yè)務(wù)卿樱,如果這個時候使用緩存的策略,就非常容易產(chǎn)生不一致的問題硫椰,雖然也有一些辦法可以解決繁调,但復(fù)雜性比較高,而且往往會有一些意想不到的坑靶草。這種時候蹄胰,使用數(shù)據(jù)庫直接去抗用戶請求,看起來很“笨拙”奕翔,可也許是性價比最高的做法裕寨。
我們最近的一個業(yè)務(wù),就有這種一致性的要求派继,而且是to C的業(yè)務(wù)宾袜,在第一期實(shí)現(xiàn)的時候,使用了單庫分表的策略驾窟,運(yùn)行良好庆猫。但隨著業(yè)務(wù)量不斷增大,單庫壓力越來越大绅络,在一次活動時阅悍,庫被打滿好渠,造成服務(wù)出現(xiàn)10分鐘左右的不可用。后來短期的解決方案是在接口層增加限流节视,超過限流時拳锚,犧牲一定的用戶體驗。
長期的解決方案寻行,就是拆庫霍掺,把原來的一個庫,拆分成10個或者更多個庫拌蜘。
難點(diǎn)
1 服務(wù)不可停杆烁,用戶無感知。就好比給行進(jìn)中的公交車換輪子一樣简卧。
2 數(shù)據(jù)必須強(qiáng)一致兔魂,不能出現(xiàn)數(shù)據(jù)錯誤的情況。
3 方案必須可回滾举娩,在出現(xiàn)問題時析校,必須回滾,讓風(fēng)險可控铜涉。
拆庫前置條件
這里需要說明一下拆庫的前置條件:
1 程序?qū)?shù)據(jù)庫的訪問必須經(jīng)由zoomkeeper智玻,如果是通過代碼寫死ip:port的方式訪問db,必須先改成可動態(tài)配置的模式芙代。
2 必須已完成分表吊奢,一般這種to C業(yè)務(wù)的表,都把userId作為分庫分表的shardId纹烹。舉個例子页滚,在單庫100個分表的情況下,用userId對100取模铺呵,比如userId=1的逻谦,在table_1 (1 % 100)中,userId=1003的陪蜻,在table_3 (1003 % 100)中。這個時候贱鼻,將單庫拆成10個庫時宴卖,依舊使用userId對庫分組,比如userId=1的邻悬,在 database_1 (1 % 10)中的table_1中症昏,userId=1003的,在database_3 (1003 % 10) 的table_3中父丰。
拆庫流程
當(dāng)時一共設(shè)計了3個方案肝谭,最終使用了第3個方案掘宪,整個拆庫的過程很成功,這里貼一下大概的流程攘烛。
1 向dba申請10個新的庫魏滚,并基于dba的工具完成3張表的數(shù)據(jù)同步
(數(shù)據(jù)同步一般是這樣做的,首先把10個新庫都作為老庫的備庫坟漱,通過mysql的binlog及snapshot機(jī)制鼠次,自動完成數(shù)據(jù)的同步)
2 新庫數(shù)據(jù)及讀驗證
(可以通過一個離線的腳本簡單驗證一下,由于這是mysql的同步機(jī)制芋齿,所以準(zhǔn)確性及可靠性非常高腥寇。只驗證讀是正常的,保證配置正常就可以觅捆,注意:不能驗證寫赦役,寫會導(dǎo)致新老庫數(shù)據(jù)不一致)
3 切換使用binlog的服務(wù)源
(對于使用binlog的一些服務(wù),這個時候需要切換到新庫的binlog配置)
4 增加一個新的dataSource栅炒,先讓它指向老庫
(這里解釋一下掂摔,原來的庫A的dataSourceA配置在zk中,程序中訪問庫的代碼都通過dataSourceA完成职辅。這個時候棒呛,新配置一個dataSourceB,讓dataSourceB的配置和dataSourceA一致)
5 修改DAO層代碼域携,將使用dataSourceA的地方統(tǒng)一改成dataSourceB
(正常的編碼規(guī)范簇秒,對數(shù)據(jù)庫的操作都會放在DAO層,因此改動是非常收斂的秀鞭,如果發(fā)現(xiàn)需要改的地方非常多趋观,一定是代碼寫的太隨意)
6 上線所有訪問庫的服務(wù)
(這個上線過程也需要灰度上線,一旦發(fā)現(xiàn)問題锋边,很可能是配置問題皱坛,需要立刻回滾)
7 將dataSourceB的zk配置切成新的庫
(這是最核心的一步,通過zk的觀察者模式豆巨,自動將新的配置通知到所有客戶端剩辟,有一定的風(fēng)險性,最好在流量低峰期進(jìn)行往扔。同時要看著監(jiān)控贩猎,一旦出現(xiàn)問題,立刻把配置刷回來)
8 關(guān)閉新老庫的數(shù)據(jù)同步
(把新庫從老庫的備庫列表摘除就可以)
9 刪除老庫中的表