高併發及負載均衡之分表分庫

先從公司的最近的一個大數據項目談起。最近公司有一個項目,設計到大量的數據,設計到數據的存儲查詢同時還需要保證高併發,那現在如何解決存儲和負載均衡的問題?下面的文章將該問題結合目前的數據庫,談談它們在存儲和負載方面有哪些解決方案。

爲什麼要分表分庫

首先是爲什麼要分表分庫?試想假如我們是早期的創業團隊,主要以軟件開發功能爲主,數據量不大,主要是一些客戶業務方面的數據,大概10w至1000w,早期簡單,我們將數據存儲在mysql,postgres中,查詢讀寫速度都沒問題。隨着業績的好轉數據越來越多從原來的1000w狂漲到幾千w甚至上億,此時查詢數據表越來越卡,硬盤容量也越來越小,有時甚至查詢1分鐘至幾分鐘才能查詢出結果,這時將考慮如何將數據分散到不同機器上去以應對大量數據查詢和存儲要求,這個時候,我們通常會做如下設計:

ok,我們將客戶表拆成一個和客戶相關的子表,將業務表拆成同樣一個一個小的子表,拆表拆庫後通過在用戶和數據庫之間插入一個應用插件解決了,解決了單臺機器存儲和查詢數據的問題。但是過了幾個月,公司業績很好,很快我們發現,我們這個設計又有問題了,因爲我們的業務需求表增長速度非常快,幾乎每天就有好幾萬的業務數據量,此時如果我們繼續對業務需求表細分,需要仔細考慮全局數據的情況,而且細分也非常麻煩,此時我們這種架構已經不適合了。那又沒有這樣的數據庫能不要拆表的情況下,將單表數據保存到多臺機器呢?這樣是不是就解決了我們存儲和大量查詢計算所帶來的性能。

hbase

先來看下hbase,hbase是google bigtable的山寨版,它的原理圖是這樣的:

hbase作爲列族式數據庫,將數據表的按rowkey進行拆分,按照業務的規則,將不同的rowkey路由到不同的機器上去,同時還按列族拆成一個個store.需要注意的是hbase中rowkey的保存順序是按字典順序保存的,因此如果要保證業務數據按不同規則拆分需要手動對rowkey“改造“,像hash,前綴、時間倒排等,當然這涉及性能,不在本節討論範圍。將rowkey按分區段進行拆分後,首先是將負載分散,假如有10000個請求,現在將表拆分成了100份,這樣單臺機器的負載就降低至了100 Qps;其次對於數據表的橫向擴展非常的方便,假如新增加了一臺機器,那隻要將新增數據rowkey id號加載到新的機器上就行了。這裏留個問題,那數據查詢時如何能找到數據所在表位置?

mongo

如何用過mongo分佈式數據庫的人肯定會說這和HBase的架構沒區別啊,下面看下mongo如何分表設計的:

mongo sharding

對於一個讀寫操作,mongos知道應該將其路由到哪個複製集上(數據的元數據保存至mongos集羣),mongos通過將片鍵空間劃分爲若干個區間,計算出一個操作的片鍵的所屬區間對應的複製集來實現路由,這樣mongo將表的rowkey劃分成多個分片來保存到不同服務器上,在如上圖,是不是和前面的hbase非常的相似。通常一個shard分片還用一個以上備份,副本機制能有效解決大數據的並行讀寫的問題,試想一下對於一秒上千萬甚至億級別的讀寫,一個備份的一臺主機讀寫的情況下立刻就宕機了,如果我們我們採用多臺主機,其中一臺可讀可寫,其他主機是該主機的備份,在高併發下,因爲多臺主機保存了同樣的數據,這樣就能將負載分攤到多臺機器上,這樣就能在數據層保證高併發,保證了不宕機服務持續服務,同時多副本機制保證數據不丟失,提高了可靠性。這裏留2個問題讓大家思考,爲什麼hbase不需要內部弄個備份sharing?一主多備架構如何保證主備之間數據的一致性以及數據同步的及時性?

elasticsearch

上面是mongo的解決方案,下面看看elasticsearch這種天生分佈式數據集羣它又是如何做分表的。通常es給人的印象是對於上億級的數據存儲,其查詢依舊是毫秒級響應,這和它的詞索引至內存、熱點數據至文件緩存、roaring bitmaps方式壓縮數據和聯合查詢過濾、以及數據分片等有很多關係,這節重點討論它數據分片。幾乎所有的數據分片都離不開數據分片前rowkey的預先劃分,es也逃不掉,但es對rowkey的sharding和前面數據庫又有區別。es默認是通過對文檔id 求hashcode(_id)%sharding_num來確定數據最終存儲在哪個shard上的,這樣理論上能保證所有的數據是負載均衡的,如下圖:

爲了數據的良好的併發和負載,通常需要將es的分片設置爲集羣節點個數,同時每個分片默認都有一個以上的副本。這裏在留個問題讓大家思考,如果es新增了一臺機器後,原來同一個表的數據能否sharding到新的機器上?

關係型數據庫mysql/postgres

上面說的都是非關係型數據庫,對於傳統的關係型數據庫如mysql、postgres他們又如何應對大數據的的分表問題呢?

mysql,postgres並不直接支持分佈式的分表操作,後面的集羣版本後纔開始支持數據庫的分表分庫操作,其拆分的原理和前面的sharing方式一樣,都是對rowkey的拆分。早期mysql、postgres的分表分庫都是通過中間件來完成的,其基本的思路都差不多,都是通過一個應用層或是一個數據庫將數據的劃分方式記錄起來,查詢聚合時應用層啓動路由功能去查找數據。對於一次查詢,通常分爲兩個階段,查詢和聚合。查詢階段,應用層本身不做數據的存儲和查詢,只負責數據的路由轉發,將查詢負載均攤具體存儲機器上;在數據聚合階段,應用層需要對數據的聚合計算求top N,join,連表查詢操作非常麻煩也非常消耗性能。常用的分表分庫插件的設計思路是在用戶與庫之間存在一個連接池(pgbouncer),然後定義一張內部表(pg_fdw)或插件保存用戶劃分數據的規則和路由、建立虛擬的外部表映射至具體的數據表(pg1,pg2),插入數據時,外部表不保存數據,只負責將數據通過規則路由至對應的數據庫中,真正查詢聚合階段將數據取回聚合給用戶,如下圖所示:

總結

上面從hbase、mongo、es、mysql/postgres等常見數據庫它們的分表分庫的原理,講了這些數據庫在處理大數據存儲和負載均衡下的區別和作用,希望對大家理解大數據存儲和分表分庫原理有所幫助。下節將針對上面提到的問題詳細的解答。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章