背景
隨著公司業(yè)務(wù)增長,如果每天1000多萬筆訂單的話,3個月將有約10億的訂單量良蛮,之前數(shù)據(jù)庫采用單庫單表的形式已經(jīng)不滿足于業(yè)務(wù)需求,數(shù)據(jù)庫改造迫在眉睫悍赢。
訂單數(shù)據(jù)如何劃分决瞳?
我們可以將訂單數(shù)據(jù)劃分成兩大類型:分別是熱數(shù)據(jù)和冷數(shù)據(jù)。
熱數(shù)據(jù):3個月內(nèi)的訂單數(shù)據(jù)左权,查詢實時性較高皮胡。
冷數(shù)據(jù)A:3個月 ~ 12個月前的訂單數(shù)據(jù),查詢頻率不高赏迟。
冷數(shù)據(jù)B:1年前的訂單數(shù)據(jù)屡贺,幾乎不會查詢,只有偶爾的查詢需求锌杀。
可能這里有個疑惑為什么要將冷數(shù)據(jù)分成兩類甩栈,因為根據(jù)實際場景需求,用戶基本不會去查看1年前的數(shù)據(jù)糕再,如果將這部分數(shù)據(jù)還存儲在db中量没,那么成本會非常高,而且也不便于維護突想。另外如果真遇到有個別用戶需要查看1年前的訂單信息殴蹄,可以讓用戶走離線數(shù)據(jù)查看究抓。
對于這三類數(shù)據(jù)的存儲,目前規(guī)劃如下:
熱數(shù)據(jù): 使用mysql進行存儲饶套,當(dāng)然需要分庫分表
冷數(shù)據(jù)A: 對于這類數(shù)據(jù)可以存儲在ES中漩蟆,利用搜索引擎的特性基本上也可以做到比較快的查詢。
冷數(shù)據(jù)B: 對于這類不經(jīng)常查詢的數(shù)據(jù)妓蛮,可以存放到hive中
MySql 如何分庫分表
一怠李、按業(yè)務(wù)拆分
在業(yè)務(wù)初始階段,為了加快應(yīng)用上線和快速迭代蛤克,很多應(yīng)用都采用集中式的架構(gòu)捺癞。但是隨著業(yè)務(wù)系統(tǒng)的擴大,系統(tǒng)匾額越來越復(fù)雜构挤,越來越難以維護髓介,開發(fā)效率變得越來越低,并且對資源的消耗也變得越來越大筋现,通過硬件提高系統(tǒng)性能的成本會變得更高唐础。
通常一般的電商平臺,包含了用戶矾飞、商品一膨、訂單等幾大模塊,簡單的做法是在同一個庫中分別建4張表洒沦,如下圖所示:
但是隨著業(yè)務(wù)的提升豹绪,將所有業(yè)務(wù)都放在一個庫中已經(jīng)變得越來越難以維護,因此我們建議申眼,將不同業(yè)務(wù)放在不同的庫中瞒津,如下圖所示:
由圖中我們可以看出,我們將不同的業(yè)務(wù)放到不同的庫中括尸,將原來所有壓力由同一個庫中分散到不同的庫中巷蚪,提升了系統(tǒng)的吞吐量。
二濒翻、分庫與分表
我們知道每臺機器無論配置多么好它都有自身的物理上限钓辆,所以當(dāng)我們應(yīng)用已經(jīng)能觸及或遠遠超出單臺機器的某個上限的時候,我們惟有尋找別的機器的幫助或者繼續(xù)升級的我們的硬件肴焊,但常見的方案還是通過添加更多的機器來共同承擔(dān)壓力。
我們還得考慮當(dāng)我們的業(yè)務(wù)邏輯不斷增長功戚,我們的機器能不能通過線性增長就能滿足需求娶眷?因此,使用數(shù)據(jù)庫的分庫分表啸臀,能夠立竿見影的提升系統(tǒng)的性能届宠,關(guān)于為什么要使用數(shù)據(jù)庫的分庫分表的其他原因這里不再贅述烁落,主要講具體的實現(xiàn)策略。
1)豌注、分表策略
我們以訂單表為例伤塌,在訂單表中,訂單id肯定是不可重復(fù)的轧铁,因此將該字段當(dāng)做shard key 是非常適合的,其他表類似每聪。假設(shè)訂單表的字段如下:
1createtableorder(
2order_idbigint(11)?,
3user_idbigint(11),
4phonevarchar(15),
5...
6)
我們假設(shè)預(yù)估單個庫需要分配100個表滿足我們的業(yè)務(wù)需求,我們可以簡單的取模計算出訂單在哪個子表中齿风,例如: order_id % 100,
這時候可能會有人問了药薯,如果我根據(jù)order_id 進行分表規(guī)則,但是我想根據(jù)user_id 查詢相應(yīng)的訂單救斑,不是定位不到哪個子表了嗎童本,的確是這樣,一旦確定shard key脸候,就只能根據(jù)shard key定位到子表進而查詢該子表下的數(shù)據(jù)穷娱;如果確實想根據(jù)user_id 去查詢相關(guān)訂單,那應(yīng)該將shard key設(shè)置為user_id, ?那分表規(guī)則也相應(yīng)的變更為: user_id % 100;
2)运沦、分庫實現(xiàn)策略
數(shù)據(jù)庫分表能夠解決單表數(shù)據(jù)量很大的時候數(shù)據(jù)查詢的效率問題泵额,但是無法給數(shù)據(jù)庫的并發(fā)操作帶來效率上的提高,因為分表的實質(zhì)還是在一個數(shù)據(jù)庫上進行的操作茶袒,很容易受數(shù)據(jù)庫IO性能的限制梯刚。
因此,如何將數(shù)據(jù)庫IO性能的問題平均分配出來薪寓,很顯然將數(shù)據(jù)進行分庫操作可以很好地解決單臺數(shù)據(jù)庫的性能問題亡资。
分庫策略與分表策略的實現(xiàn)很相似,最簡單的都是可以通過取模的方式進行路由向叉。
我們還是以order表舉例锥腻,
例如:order_id % 庫容量,
如果order_id 不是整數(shù)類型,可以先hash 在進行取模母谎,
例如: hash(order_id) % 庫容量
3)瘦黑、分庫分表結(jié)合使用策略
數(shù)據(jù)庫分表可以解決單表海量數(shù)據(jù)的查詢性能問題,分庫可以解決單臺數(shù)據(jù)庫的并發(fā)訪問壓力問題奇唤。有時候幸斥,我們需要同時考慮這兩個問題,因此咬扇,我們既需要對單表進行分表操作甲葬,還需要進行分庫操作,以便同時擴展系統(tǒng)的并發(fā)處理能力和提升單表的查詢性能懈贺,就是我們使用到的分庫分表经窖。
如果使用分庫分表結(jié)合使用的話坡垫,不能簡單進行order_id 取模操作,需要加一個中間變量用來打散到不同的子表画侣,公式如下:
1中間變量”啤=?shard?key?%(庫數(shù)量*單個庫的表數(shù)量);
2庫序號 = 取整(中間變量/單個庫的表數(shù)量);
3表序號∨渎摇= 中間變量%單個庫的表數(shù)量;
例如:數(shù)據(jù)庫有10個溉卓,每一個庫中有100個數(shù)據(jù)表,用戶的order_id=1001宪卿,按照上述的路由策略的诵,可得:
1中間變量 =?1001?%(10*100)=?1;
2庫序號∮蛹亍= 取整(1/100)=?0;
3表序號∥靼獭= 1%100=1;
這樣的話,對于order_id=1001休溶,將被路由到第1個數(shù)據(jù)庫的第2個表中(索引0 代表1代赁,依次類推)。
整體架構(gòu)設(shè)計
從圖中我們將請求分成read和write請求兽掰,write請求比較簡單芭碍,就是根據(jù)分庫分表規(guī)則寫入db即可。
對于read請求孽尽,我們需要計算出查詢的是熱數(shù)據(jù)還是冷數(shù)據(jù)窖壕,一般order_id生成規(guī)則如下,“商戶所在地區(qū)號+時間戳+隨機數(shù)”杉女,我們可以根據(jù)時間戳計算出查詢的是熱數(shù)據(jù)還是冷數(shù)據(jù)瞻讽,(當(dāng)然具體業(yè)務(wù)需要具體對待,這里不再詳細闡述)
另外架構(gòu)圖中的冷數(shù)據(jù)指的是3個月~12個月前的數(shù)據(jù)熏挎,如果是查詢一年前的數(shù)據(jù)速勇,建議直接離線查hive即可。
圖中有一個定時Job坎拐,主要用來定時遷移訂單數(shù)據(jù)烦磁,需要將冷數(shù)據(jù)分別遷移到ES和hive中。