Kevin.Zhong

          彪悍的人生不需要解釋,彪悍的代碼不需要測試。

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            17 隨筆 :: 12 文章 :: 14 評論 :: 0 Trackbacks

          memcached全面剖析–4. memcached的分布式算法

          作者:charlee  來源:idv2.com  時間:2008-09-28  閱讀:48 次  原文鏈接   [收藏]  

          發(fā)表日:2008/7/23
          作者:長野雅廣(Masahiro Nagano)
          原文鏈接:http://gihyo.jp/dev/feature/01/memcached/0004

          我是Mixi的長野。 第2次第3次 由前坂介紹了memcached的內(nèi)部情況。本次不再介紹memcached的內(nèi)部結(jié)構(gòu),開始介紹memcached的分布式。

          memcached的分布式

          正如第1次中介紹的那樣, memcached雖然稱為“分布式”緩存服務(wù)器,但服務(wù)器端并沒有“分布式”功能。服務(wù)器端僅包括 第2次第3次 前坂介紹的內(nèi)存存儲功能,其實(shí)現(xiàn)非常簡單。至于memcached的分布式,則是完全由客戶端程序庫實(shí)現(xiàn)的。這種分布式是memcached的最大特點(diǎn)。

          memcached的分布式是什么意思?

          這里多次使用了“分布式”這個詞,但并未做詳細(xì)解釋。現(xiàn)在開始簡單地介紹一下其原理,各個客戶端的實(shí)現(xiàn)基本相同。

          下面假設(shè)memcached服務(wù)器有node1~node3三臺,應(yīng)用程序要保存鍵名為“tokyo”“kanagawa”“chiba”“saitama”“gunma” 的數(shù)據(jù)。


          圖1 分布式簡介:準(zhǔn)備

          首先向memcached中添加“tokyo”。將“tokyo”傳給客戶端程序庫后,客戶端實(shí)現(xiàn)的算法就會根據(jù)“鍵”來決定保存數(shù)據(jù)的memcached服務(wù)器。服務(wù)器選定后,即命令它保存“tokyo”及其值。


          圖2 分布式簡介:添加時

          同樣,“kanagawa”“chiba”“saitama”“gunma”都是先選擇服務(wù)器再保存。

          接下來獲取保存的數(shù)據(jù)。獲取時也要將要獲取的鍵“tokyo”傳遞給函數(shù)庫。函數(shù)庫通過與數(shù)據(jù)保存時相同的算法,根據(jù)“鍵”選擇服務(wù)器。使用的算法相同,就能選中與保存時相同的服務(wù)器,然后發(fā)送get命令。只要數(shù)據(jù)沒有因為某些原因被刪除,就能獲得保存的值。


          圖3 分布式簡介:獲取時

          這樣,將不同的鍵保存到不同的服務(wù)器上,就實(shí)現(xiàn)了memcached的分布式。 memcached服務(wù)器增多后,鍵就會分散,即使一臺memcached服務(wù)器發(fā)生故障無法連接,也不會影響其他的緩存,系統(tǒng)依然能繼續(xù)運(yùn)行。

          接下來介紹第1次 中提到的Perl客戶端函數(shù)庫Cache::Memcached實(shí)現(xiàn)的分布式方法。

          Cache::Memcached的分布式方法

          Perl的memcached客戶端函數(shù)庫Cache::Memcached是 memcached的作者Brad Fitzpatrick的作品,可以說是原裝的函數(shù)庫了。

          該函數(shù)庫實(shí)現(xiàn)了分布式功能,是memcached標(biāo)準(zhǔn)的分布式方法。

          根據(jù)余數(shù)計算分散

          Cache::Memcached的分布式方法簡單來說,就是“根據(jù)服務(wù)器臺數(shù)的余數(shù)進(jìn)行分散”。求得鍵的整數(shù)哈希值,再除以服務(wù)器臺數(shù),根據(jù)其余數(shù)來選擇服務(wù)器。

          下面將Cache::Memcached簡化成以下的Perl腳本來進(jìn)行說明。

          use strict;
          use warnings;
          use String::CRC32;

          my @nodes = ('node1','node2','node3');
          my @keys = ('tokyo', 'kanagawa', 'chiba', 'saitama', 'gunma');

          foreach my $key (@keys) {
          my $crc = crc32($key); # CRC値
          my $mod = $crc % ( $#nodes + 1 );
          my $server = $nodes[ $mod ]; # 根據(jù)余數(shù)選擇服務(wù)器
          printf "%s => %s\n", $key, $server;
          }

          Cache::Memcached在求哈希值時使用了CRC。

          首先求得字符串的CRC值,根據(jù)該值除以服務(wù)器節(jié)點(diǎn)數(shù)目得到的余數(shù)決定服務(wù)器。上面的代碼執(zhí)行后輸入以下結(jié)果:

          tokyo       => node2
          kanagawa => node3
          chiba => node2
          saitama => node1
          gunma => node1

          根據(jù)該結(jié)果,“tokyo”分散到node2,“kanagawa”分散到node3等。多說一句,當(dāng)選擇的服務(wù)器無法連接時,Cache::Memcached會將連接次數(shù)添加到鍵之后,再次計算哈希值并嘗試連接。這個動作稱為rehash。不希望rehash時可以在生成Cache::Memcached對象時指定“rehash => 0”選項。

          根據(jù)余數(shù)計算分散的缺點(diǎn)

          余數(shù)計算的方法簡單,數(shù)據(jù)的分散性也相當(dāng)優(yōu)秀,但也有其缺點(diǎn)。那就是當(dāng)添加或移除服務(wù)器時,緩存重組的代價相當(dāng)巨大。添加服務(wù)器后,余數(shù)就會產(chǎn)生巨變,這樣就無法獲取與保存時相同的服務(wù)器,從而影響緩存的命中率。用Perl寫段代碼來驗證其代價。

          use strict;
          use warnings;
          use String::CRC32;

          my @nodes = @ARGV;
          my @keys = ('a'..'z');
          my %nodes;

          foreach my $key ( @keys ) {
          my $hash = crc32($key);
          my $mod = $hash % ( $#nodes + 1 );
          my $server = $nodes[ $mod ];
          push @{ $nodes{ $server } }, $key;
          }

          foreach my $node ( sort keys %nodes ) {
          printf "%s: %s\n", $node, join ",", @{ $nodes{$node} };
          }

          這段Perl腳本演示了將“a”到“z”的鍵保存到memcached并訪問的情況。將其保存為mod.pl并執(zhí)行。

          首先,當(dāng)服務(wù)器只有三臺時:

          $ mod.pl node1 node2 nod3
          node1: a,c,d,e,h,j,n,u,w,x
          node2: g,i,k,l,p,r,s,y
          node3: b,f,m,o,q,t,v,z

          結(jié)果如上,node1保存a、c、d、e……,node2保存g、i、k……,每臺服務(wù)器都保存了8個到10個數(shù)據(jù)。

          接下來增加一臺memcached服務(wù)器。

          $ mod.pl node1 node2 node3 node4
          node1: d,f,m,o,t,v
          node2: b,i,k,p,r,y
          node3: e,g,l,n,u,w
          node4: a,c,h,j,q,s,x,z

          添加了node4。可見,只有d、i、k、p、r、y命中了。像這樣,添加節(jié)點(diǎn)后鍵分散到的服務(wù)器會發(fā)生巨大變化。26個鍵中只有六個在訪問原來的服務(wù)器,其他的全都移到了其他服務(wù)器。命中率降低到23%。在Web應(yīng)用程序中使用memcached時,在添加memcached服務(wù)器的瞬間緩存效率會大幅度下降,負(fù)載會集中到數(shù)據(jù)庫服務(wù)器上,有可能會發(fā)生無法提供正常服務(wù)的情況。

          mixi的Web應(yīng)用程序運(yùn)用中也有這個問題,導(dǎo)致無法添加memcached服務(wù)器。但由于使用了新的分布式方法,現(xiàn)在可以輕而易舉地添加memcached服務(wù)器了。這種分布式方法稱為 Consistent Hashing。

          Consistent Hashing

          關(guān)于Consistent Hashing的思想,mixi株式會社的開發(fā)blog等許多地方都介紹過,這里只簡單地說明一下。

          Consistent Hashing的簡單說明

          Consistent Hashing如下所示:首先求出memcached服務(wù)器(節(jié)點(diǎn))的哈希值,并將其配置到0~232的圓(continuum)上。然后用同樣的方法求出存儲數(shù)據(jù)的鍵的哈希值,并映射到圓上。然后從數(shù)據(jù)映射到的位置開始順時針查找,將數(shù)據(jù)保存到找到的第一個服務(wù)器上。如果超過232仍然找不到服務(wù)器,就會保存到第一臺memcached服務(wù)器上。


          圖4 Consistent Hashing:基本原理

          從上圖的狀態(tài)中添加一臺memcached服務(wù)器。余數(shù)分布式算法由于保存鍵的服務(wù)器會發(fā)生巨大變化而影響緩存的命中率,但Consistent Hashing中,只有在continuum上增加服務(wù)器的地點(diǎn)逆時針方向的第一臺服務(wù)器上的鍵會受到影響。


          圖5 Consistent Hashing:添加服務(wù)器

          因此,Consistent Hashing最大限度地抑制了鍵的重新分布。而且,有的Consistent Hashing的實(shí)現(xiàn)方法還采用了虛擬節(jié)點(diǎn)的思想。使用一般的hash函數(shù)的話,服務(wù)器的映射地點(diǎn)的分布非常不均勻。因此,使用虛擬節(jié)點(diǎn)的思想,為每個物理節(jié)點(diǎn)(服務(wù)器)在continuum上分配100~200個點(diǎn)。這樣就能抑制分布不均勻,最大限度地減小服務(wù)器增減時的緩存重新分布。

          通過下文中介紹的使用Consistent Hashing算法的memcached客戶端函數(shù)庫進(jìn)行測試的結(jié)果是,由服務(wù)器臺數(shù)(n)和增加的服務(wù)器臺數(shù)(m)計算增加服務(wù)器后的命中率計算公式如下:

          (1 - n/(n+m)) * 100

          支持Consistent Hashing的函數(shù)庫

          本連載中多次介紹的Cache::Memcached雖然不支持Consistent Hashing,但已有幾個客戶端函數(shù)庫支持了這種新的分布式算法。第一個支持Consistent Hashing和虛擬節(jié)點(diǎn)的memcached客戶端函數(shù)庫是名為libketama的PHP庫,由last.fm開發(fā)。

          至于Perl客戶端,連載的第1次 中介紹過的Cache::Memcached::Fast和Cache::Memcached::libmemcached支持 Consistent Hashing。

          兩者的接口都與Cache::Memcached幾乎相同,如果正在使用Cache::Memcached,那么就可以方便地替換過來。Cache::Memcached::Fast重新實(shí)現(xiàn)了libketama,使用Consistent Hashing創(chuàng)建對象時可以指定ketama_points選項。

          my $memcached = Cache::Memcached::Fast->new({
          servers => ["192.168.0.1:11211","192.168.0.2:11211"],
          ketama_points => 150
          });

          另外,Cache::Memcached::libmemcached 是一個使用了Brain Aker開發(fā)的C函數(shù)庫libmemcached的Perl模塊。 libmemcached本身支持幾種分布式算法,也支持Consistent Hashing,其Perl綁定也支持Consistent Hashing。

          總結(jié)

          本次介紹了memcached的分布式算法,主要有memcached的分布式是由客戶端函數(shù)庫實(shí)現(xiàn),以及高效率地分散數(shù)據(jù)的Consistent Hashing算法。下次將介紹mixi在memcached應(yīng)用方面的一些經(jīng)驗,和相關(guān)的兼容應(yīng)用程序。

          posted on 2008-10-15 11:37 Kevin.Zhong 閱讀(155) 評論(0)  編輯  收藏 所屬分類: memcache
          主站蜘蛛池模板: 苏尼特右旗| 临武县| 无棣县| 河北区| 大英县| 文水县| 东源县| 开阳县| 冷水江市| 邢台县| 丹寨县| 玉溪市| 铜鼓县| 昌邑市| 乌拉特后旗| 建湖县| 徐水县| 石林| 定边县| 凤阳县| 交口县| 平原县| 新巴尔虎右旗| 昭通市| 金平| 德惠市| 长宁区| 江口县| 同仁县| 天津市| 如皋市| 柳州市| 乐安县| 宿州市| 垣曲县| 娄烦县| 昌图县| 宕昌县| 桐梓县| 安达市| 寿阳县|