負載均衡備註二

Lvs或者F5的負載體系中,所有的請求需要請過dispatcher,從體系上說,它就是所謂的熱點設備,無論這個設備的性能多麼卓越,遲早會成爲性能瓶頸。看過memcached的系統架構後,結合自身的系統特點,也可以做到客戶端來實現負載。memcached雖然稱爲分佈式緩存服務器,但服務器端並沒有分佈式功能,是完全由客戶端程序庫實現的。
[b]初步方案:[/b]
[img]http://dl.iteye.com/upload/attachment/226676/1e29cb8d-eafe-3161-b5bb-846d8cb9d320.png[/img]
如上圖所示:
前提:
1.Cluster對外有多個固定IP(ClusterIP1,ClusterIP2)供訪問,實際是映射到cluster中的某幾臺Server.
2.圖中的每個Server,其實也是個1+N的cluster,同時只有一個服務器位於cluster中服務。
3.Server之間以組播心跳方式發送負載信息,使所有的server都知道cluster中每個server的負載情況,以及服務器的活動狀態。

訪問步驟:
1.初始狀態,Client只知道固定的cluster地址(IP1),向其中任何一個發送請求,獲取cluster的負載信息。
2.clusterIP對應的server獲取負載信息,返回給Client.
3.Client以某些算法分析出最輕負載server。
4.Client轉向連到負載最輕的server.再次重新dispatch之前,一直訪問這臺server。
5.Client定時獲取cluster負載信息,轉向重連,這個時候cluster地址(IP1)就不會用到了。

算法:
第三步的負載信息分析,可以用lvs的負載算法,也可以用memcached的取餘散列算法。
根據服務器臺數的餘數進行分散。但是當添加或移除服務器時,緩存重組的代價相當巨大。添加服務器後,餘數就會產生鉅變,這樣就無法獲取與保存時相同的服務器,從而影響緩存的命中率。基本不考慮。

問題:按照這個做法,在一定的時間內,client端上所有請求還是分發到一臺server上,如果在這個時間段內,數據量特別大,就會造成server之間負載不均衡。要改進這個問題
1) 最好就是把轉發過程平均到每個請求上去
2) Client端不需要反覆地取負載信息,也就是負載算法可以根據最初獲取的Server信息,均勻的分攤到所有的Server上去。

改進:採用memcached的Consistent Hashing算法。首先求出Server的哈希值,並將其配置到0~232的圓上。然後用同樣的方法求出存儲數據的鍵的哈希值,並映射到圓上。然後從數據映射到的位置開始順時針查找,將數據保存到找到的第一個Server上。如果超過232仍然找不到Server,就會保存到第一臺Server上。
[img]http://dl.iteye.com/upload/attachment/226567/16063547-0cfd-3b17-9d0d-afcbd1880499.png[/img]
Consistent Hashing最大限度地抑制了鍵的重新分佈。而且,有的Consistent Hashing的實現方法還採用了虛擬節點的思想。使用一般的hash函數的話,服務器的映射地點的分佈非常不均勻。因此,使用虛擬節點的思想,爲每個Server在continuum上分配100~200個點。這樣就能抑制分佈不均勻,最大限度地減小Server增減時的緩存重新分佈。

因此可能的改進如下:
1.Client定時向ClusterIP請求最新的服務器列表,同時client也維護上一個服務器信息,以交集爲準。
2.Client對於每次請求,根據Client端存儲的服務器列表,按照Consistent Hashing算法分析出目標Server地址,然後發送請求道這臺指定server.
3.Consistent Hashing消耗要比較低,速度要比較快。

哈希函數的測試數據:拷貝自[url]http://dennis-zane.iteye.com/blog/346682[/url]
測試場景:
從一篇英文小說(《黃金羅盤》前三章)進行單詞統計,並將最後的統計結果存儲到memcached,以單詞爲key,以次數爲value。單詞個數爲 3061,memcached原來節點數爲10,運行在局域網內同一臺服務器上的不同端口,在存儲統計結果後,增加兩個memcached節點(也就是從10個節點增加到12個節點),統計此時的緩存命中率並查看數據的分佈情況。
結果如下表格,命中率一行表示增加節點後的命中率情況(增加前爲100%),後續的行表示各個節點存儲的單詞數。
CRC32_HASH表示採用CRC32 散列函數。
KETAMA_HASH是基於md5的散列函數也是默認情況下一致性哈希的推薦算法。
FNV1_32_HASH就是FNV 32位散列函數。
NATIVE_HASH就是java.lang.String.hashCode()方法返回的long取32位的結 果。
MYSQL_HASH是xmemcached添加的傳說來自於mysql源碼中的哈希函數。

CRC32_HASH KETAMA_HASH FNV1_32_HASH NATIVE_HASH MYSQL_HASH
命中率 78.5% 83.3% 78.2% 99.89% 86.9%
節點1 319 366 546 3596 271
節點2 399 350 191 1 233
節點3 413 362 491 0 665
節點4 393 364 214 1 42
節點5 464 403 427 1 421
節點6 472 306 299 0 285
節點7 283 347 123 0 635
節點8 382 387 257 2 408
節點9 238 341 297 0 55
節點10 239 375 756 0 586
範圍 200~500 300~400 150~750 0~3600 50~650


結果分析:

1、命中率最高看起來是NATIVE_HASH,然而NATIVE_HASH情況下數據集中存儲在第一個節點,顯然沒有實際使用價值。爲什麼會集中存儲在 第一個節點呢?這是由於在查找存儲的節點的過程中,會比較hash(key)和hash(節點IP地址),而在採用了NATIVE_HASH的情況下,所 有連接的hash值會呈現一個遞增狀況(因爲String.hashCode是乘法散列函數),如:
192.168.0.100:12000 736402923
192.168.0.100:12001 736402924
192.168.0.100:12002 736402925
192.168.0.100:12003 736402926
如果這些值很大的會,那麼單詞的hashCode()會通常小於這些值的第一個,那麼查找就經常只找到第一個節點並存儲數據,當然,這裏有測試的侷限性, 因爲memcached都跑在一個臺機器上只是端口不同造成了hash(節點IP地址)的連續遞增,將分佈不均勻的問題放大了。

2、從結果上看,KETAMA_HASH維持了一個最佳平衡,在增加兩個節點後還能訪問到83.3%的單詞,並且數據分佈在各個節點上的數目也相對平均,難怪作爲默認散列算法。

3、最後,單純比較下散列函數的計算效率:

CRC32_HASH:3266
KETAMA_HASH:7500
FNV1_32_HASH:375
NATIVE_HASH:187
MYSQL_HASH:500

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