帶你深入瞭解Web站點數據庫的分佈存儲

在Web 2.0時代,網站將會經常面臨着快速增加的訪問量,但是我們的應用如何滿足用戶的訪問需求,而且基本上我們看到的情況都是性能瓶頸都是在數據庫上,這個不 怪數據庫,畢竟要滿足很大訪問量確實對於任何一款數據庫都是很大的壓力,不論是商業數據庫Oracle、MS sql Server、DB2之類,還是開源的MySQL、PostgreSQL,都是很大的挑戰,解決的方法很簡單,就是把數據分散在不同的數據庫上(可以是硬 件上的,也可以是邏輯上的),本文就是主要討論如何數據庫分散存儲的的問題。

目前主要分佈存儲的方式都是按照一定的方式進行切分,主要是垂直切分(縱向)和水平切分(橫向)兩種方式,當然,也有兩種結合的方式,達到更貼切的切分粒度。

 

1. 垂直切分(縱向)數據是數據庫切分按照網站業務、產品進行切分,比如用戶數據、博客文章數據、照片數據、標籤數據、羣組數據等等每個業務一個獨立的數據庫或者數據庫服務器。

2. 水平切分(橫向)數據是把所有數據當作一個大產品,但是把所有的平面數據按照某些Key(比如用戶名)分散在不同數據庫或者數據庫服務器上,分散對數據訪問的壓力,這種方式也是本文主要要探討的。

 

本文主要針對的的 MySQL/PostgreSQL 類的開源數據庫,同時平臺是在 linux/FreeBSD,使用 php/Perl/Ruby/Python 等腳本語言,搭配 Apache/Lighttpd 等Web服務器 的平臺下面的Web應用,不討論靜態文件的存儲,比如視頻、圖片、CSS、JS,那是另外一個話題。

 

說明:下面將會反覆提到的一個名次“節點”(Node),指的是一個數據庫節點,可能是物理的一臺數據庫服務器,也可能是一個數據庫,一般情況是指一臺數據庫服務器,並且是具有 Master/Slave 結構的數據庫服務器,我們查看一下圖片,瞭解這樣節點的架構:

 

一、基於散列的分佈方式

 

1. 散列方式介紹

基 於散列(Hash)的分佈存儲方式,主要是依賴主要Key和散列算法,比如以用戶爲主的應用主要的角色就是用戶,那麼做Key的就可以是用戶ID或者是用 戶名、郵件地址之類(該值必須在站點中隨處傳遞),使用這個唯一值作爲Key,通過對這個Key進行散列算法,把不同的用戶數據分散在不同的數據庫節點 (Node)上。

 

我們通過簡單的實例來描述這個問題:比如有一個應用,Key是用戶ID,擁有10個 數據庫節點,最簡單的散列算法是我們 用戶ID數模以我們所有節點數,餘數就是對應的節點機器,算法:所在節點 = 用戶ID % 總節點數,那麼,用戶ID爲125的用戶所在節點:125 % 10 = 5,那麼應該在名字爲5的節點上。同樣的,可以構造更爲強大合理的Hash算法來更均勻的分配用戶到不同的節點上。

 

我們查看一下采用散列分佈方式的數據結構圖:

2. 散列分佈存儲方式的擴容

 

我們知道既然定義了一個散列算法,那麼這些Key就會按部就班的分散到指定節點上,但是如果目前的所有節點不夠滿足要求怎麼辦?這就存在一個擴容的問題,擴容首當其衝的就是要修改散列算法,同時數據也要根據散列算法進修遷移或者修改。

 

(1) 遷移方式擴容:修 改散列算法以後,比如之前是10個節點,現在增加到20個節點,那麼Hash算法就是[模20],相應的存在一個以前的節點被分配的數據會比較多,但是新 加入的節點數據少的不平衡的狀態,那麼可以考慮使用把以前數據中的數據按照Key使用新的Hash算法進行運算出新節點,把數據遷移到新節點,缺點但是這 個成本相應比較大,不穩定性增加;好處是數據比較均勻,並且能夠充分利用新舊節點。

 

(2) 充分利用新節點:增 加新節點以後,Hash算法把新加入的數據全部Hash到新節點上,不再往舊節點上分配數據,這樣不存在遷移數據的成本。優點是只需要修改Hash算法, 無須遷移數據就能夠簡單的增加節點,但是在查詢數據的時候,必須使用考慮到舊Key使用舊Hash算法,新增加的Key使用新的Hash算法,不然無法查 找到數據所在節點。缺點很明顯,一個是Hash算法複雜度增加,如果頻繁的增加新節點,算法將非常複雜,無法維護,另外一個方面是舊節點無法充分利用資源 了,因爲舊節點只是單純的保留舊Key數據,當然了,這個也有合適的解決方案。

 

總結來說,散列方式分佈數據,要新增節點比較困難和繁瑣,但是也有很多適合的場合,特別適合能夠預計到未來數據量大小的應用,但是普遍 Web2.0 網站都無法預計到數據量。

 

二、基於全局節點分配方式

 

1. 全局節點分配方式介紹

 

就是把所有Key信息與數據庫節點之間的映射關係記錄下來,保存到全局表中,當需要訪問某個節點的時候,首先去全局表中查找,找到以後再定位到相應節點。全局表的存儲方式一般兩種:

 

(1) 採用節點數據庫本身(MySQL/PostgreSQL)存儲節點信息,能夠遠程訪問,爲了保證性能,同時配合使用 Heap(MEMORY) 內存表,或者是使用 Memcached 緩存方式來緩存,加速節點查找

 

(2) 採用 BDB(BerkeleyDB)、DBM/GDBM/NDBM 這類本地文件數據庫,基於 key=>value 哈希數據庫,查找性能比較高,同時結合 APC、Memcached 之類的緩存加速。

 

第 一種存儲方式是容易查詢(包括遠程查詢),缺點是性能不太好(這個是所有關係型數據庫的通病);第二種方式的有點是本地查詢速度很快(特別是hash型數 據庫,時間複雜度是O(1),比較快),缺點是無法遠程使用,並且無法在多臺機器中間同步共享數據,存在數據一致的情況。

 

我們來描述實施 大概結構:假如我們有10個數據庫節點,一個全局數據庫用於存儲Key到節點的映射信息,假設全局數據庫有一個表叫做 AllNode ,包含兩個字段,Key 和 NodeID,假設我們繼續按照上面的案例,用戶ID是Key,並且有一個用戶ID爲125的用戶,它對應的節點,我們查詢表獲得:

Key  NodeID 
13 2
148 5
22 9
125 6

可以確認這個用戶ID爲125的用戶,所在的節點是6,那麼就可以迅速定位到該節點,進行數據的處理。

 

我們來查看一下分佈存儲結構圖:

2. 全局節點分佈方式的擴容

 

全局節點分配方式同樣存在擴容的問題,不過它早就考慮到這個問題,並且這麼設計就是爲了便於擴容,主要的擴容方式是兩種:

 

(1) 通過節點自然增加來分配Key到節點的映射擴容

這 種是最典型、最簡單、最節約機器資源的擴容方式,大致就是按照每個節點分配指定的數據量,比如一個節點存儲10萬用戶數據,第一個節點存儲0-10w用戶 數據,第二個節點存儲10w-20w用戶數據,第三個節點存儲20w-30w用戶信息,依此類推,用戶增加到一定數據量就增加節點服務器,同時把Key分 配到新增加的節點上,映射關係記錄到全局表中,這樣可以無限的增加節點。存在的問題是,如果早期的節點用戶訪問頻率比較低,而後期增加的節點用戶訪問頻率 比較高,則存在節點服務器負載不均衡的現象,這個也是可以想方案解決的。

 

 

(2) 通過概率算法來映射Key到節點的的擴容

這種方式是在既然有的節點基礎上,給每個節點設定一個被分配到Key的概率,然後分配Key的時候,按照每個節點被指定的概率進行分配,如果每個節點平均的數據容量超過了指定的百分比,比如50%,那麼這時候就考慮增加新節點,那麼新節點增加Key的概率要大於舊節點。

一般情況下,對於節點的被分配的概率也是記錄在數據庫中的,比如我們把所有的概率爲100,共有10個節點,那麼設定每個節點被分配的數據的概率爲10,我們查看數據表結構:

NodeID Weight 
1 10
2 10
3 10

現在新加入了一個 節點,新加入的節點,被分配Key的機率要大於舊節點,那麼就必須對這個新加入的節點進行概率計算,計算公式:10х+у=100, у>х,得出:у{10...90},х{1...9},x是單箇舊節點的概率,舊節點的每個節點的概率是一樣的,y是新節點的概率,按照這個計算 公式,推算出新節點y的概率的範圍,具體按照具體不同應用的概率公式進行計算。

 

三、存在的問題

 

現在我們來分析和解決一下我們上面兩種分佈存儲方式的存在的問題,便於在實際考慮架構的時候能夠避免或者是融合一些問題和缺點。

 

1. 散列和全局分配方式都存在問題

 

(1) 散列方式擴容不是很方便,必須修改散列算法,同時可能還需要對數據進行遷移,它的優點是從Key定位一個節點非常快,O(1)的時間複雜度,而且基本不需要查詢數據庫,節約響應時間。

(2) 全局分配方式存在的問題最明顯的是單點故障,全局數據庫down掉將影響所有應用。另外一個問題是查詢量大,對每個Key節點的操作都必須經過全局數據庫,壓力很大,優點是擴容方便,增加節點簡單。

 

 

2. 分佈存儲帶來的搜索和統計問題

 

(1) 一般搜索或統計都是對所有數據進行處理,但因爲拆分以後,數據分散在不同節點機器上,無法進行全局查找和統計。解決方案一是對主要的基礎數據存儲在全局表中,便於查找和統計,但這類數據不宜太多,部分核心數據。

(2) 採用站內搜索引擎來索引和記錄全部數據,比如採用 Lucene 等開源索引系統進行所有數據的索引,便於搜索。 對於統計操作可以採用後臺非實時統計,可採用遍歷所有節點的方式,但效率低下。

 

 

3. 性能優化問題

 

(1) 散列算法,節點概率和分配等爲了提高性能都可以使用編譯語言開發,做成lib或者是所有php擴展形式。

(2) 對於採用 MySQL 的情況,可以採用自定義的數據庫連接池,採用 Apache Module 形式加載,能夠自由定製的採用各種連接方式。

(3) 對於全局數據或都頻繁訪問的數據,可以採用APC、Memcache、DBM、BDB、共享內存、文件系統等各種方式進行緩存,減少數據庫的訪問壓力。

(4) 採用數據本身的強大處理機制,比如 MySQL5 的表分區或者是 MySQL5 的Cluster 。另外建議在實際架構中採用InnoDB表引擎作爲主要存儲引擎,MyISAM作爲一些日誌、統計數據等場合,不論在安全、可靠性、速度都有保障。

發佈了30 篇原創文章 · 獲贊 0 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章