<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 100  文章 - 50  trackbacks - 0
    <2015年10月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(3)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    收藏夾

    我收藏的一些文章!

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    由于redis是單點(diǎn),但是項(xiàng)目中不可避免的會(huì)使用多臺(tái)Redis緩存服務(wù)器,那么怎么把緩存的Key均勻的映射到多臺(tái)Redis服務(wù)器上,且隨著緩存服務(wù)器的增加或減少時(shí)做到最小化的減少緩存Key的命中率呢?這樣就需要我們自己實(shí)現(xiàn)分布式。

      Memcached對(duì)大家應(yīng)該不陌生,通過(guò)把Key映射到Memcached Server上,實(shí)現(xiàn)快速讀取。我們可以動(dòng)態(tài)對(duì)其節(jié)點(diǎn)增加,并未影響之前已經(jīng)映射到內(nèi)存的Key與memcached Server之間的關(guān)系,這就是因?yàn)槭褂昧艘恢滦怨!?br />因?yàn)镸emcached的哈希策略是在其客戶端實(shí)現(xiàn)的,因此不同的客戶端實(shí)現(xiàn)也有區(qū)別,以Spymemcache、Xmemcache為例,都是使用了KETAMA作為其實(shí)現(xiàn)。

      因此,我們也可以使用一致性hash算法來(lái)解決Redis分布式這個(gè)問(wèn)題。在介紹一致性hash算法之前,先介紹一下我之前想的一個(gè)方法,怎么把Key均勻的映射到多臺(tái)Redis Server上。

      由于LZ水平有限且對(duì)Redis研究的不深,文中有寫的不對(duì)的地方請(qǐng)指正。

    方案一

    該方案是前幾天想的一個(gè)方法,主要思路是通過(guò)對(duì)緩存Key中的字母和數(shù)字的ascii碼值求sum,該sum值對(duì)Redis Server總數(shù)取余得到的數(shù)字即為該Key映射到的Redis Server,該方法有一個(gè)很大的缺陷就是當(dāng)Redis Server增加或減少時(shí),基本上所有的Key都映射不到對(duì)應(yīng)的的Redis Server了。代碼如下:

    復(fù)制代碼
        /// <summary>         /// 根據(jù)緩存的Key映射對(duì)應(yīng)的Server         /// </summary>         /// <param name="Key"></param>         /// <returns></returns>         public static RedisClient GetRedisClientByKey(string Key)         {             List<RedisClientInfo> RedisClientList = new List<RedisClientInfo>();             RedisClientList.Add(new RedisClientInfo() { Num = 0, IPPort = "127.0.0.1:6379" });             RedisClientList.Add(new RedisClientInfo() { Num = 1, IPPort = "127.0.0.1:9001" });              char[] charKey = Key.ToCharArray();             //記錄Key中的所有字母與數(shù)字的ascii碼和             int KeyNum = 0;             //記錄余數(shù)             int Num = 0;             foreach (var c in charKey)             {                 if ((c >= 'a' && 'z' >= c) || (c >= 'A' && 'Z' >= c))                 {                     System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();                     KeyNum = KeyNum + (int)asciiEncoding.GetBytes(c.ToString())[0];                 }                 if (c >= '1' && '9' >= c)                 {                     KeyNum += Convert.ToInt32(c.ToString());                 }             }             Num = KeyNum % RedisClientList.Count;             return new RedisClient(RedisClientList.Where(it => it.Num == Num).First().IPPort);         }         //Redis客戶端信息         public class RedisClientInfo         {             //Redis Server編號(hào)             public int Num { get; set; }             //Redis Server IP地址和端口號(hào)             public string IPPort { get; set; }         }
    復(fù)制代碼

    方案二

    1、分布式實(shí)現(xiàn)

    通過(guò)key做一致性哈希,實(shí)現(xiàn)key對(duì)應(yīng)redis結(jié)點(diǎn)的分布。

    一致性哈希的實(shí)現(xiàn):

    1. hash值計(jì)算:通過(guò)支持MD5與MurmurHash兩種計(jì)算方式,默認(rèn)是采用MurmurHash,高效的hash計(jì)算。
    2. 一致性的實(shí)現(xiàn):通過(guò)java的TreeMap來(lái)模擬環(huán)狀結(jié)構(gòu),實(shí)現(xiàn)均勻分布

    什么也不多說(shuō)了,直接上代碼吧,LZ也是只知道點(diǎn)皮毛,代碼中還有一些看不懂的地方,留著以后慢慢琢磨

    復(fù)制代碼
    public class KetamaNodeLocator     {         //原文中的JAVA類TreeMap實(shí)現(xiàn)了Comparator方法,這里我圖省事,直接用了net下的SortedList,其中Comparer接口方法)         private SortedList<long, string> ketamaNodes = new SortedList<long, string>();         private HashAlgorithm hashAlg;         private int numReps = 160;         //此處參數(shù)與JAVA版中有區(qū)別,因?yàn)槭褂玫撵o態(tài)方法,所以不再傳遞HashAlgorithm alg參數(shù)         public KetamaNodeLocator(List<string> nodes/*,int nodeCopies*/)         {             ketamaNodes = new SortedList<long, string>();             //numReps = nodeCopies;             //對(duì)所有節(jié)點(diǎn),生成nCopies個(gè)虛擬結(jié)點(diǎn)             foreach (string node in nodes)             {                 //每四個(gè)虛擬結(jié)點(diǎn)為一組                 for (int i = 0; i < numReps / 4; i++)                 {                     //getKeyForNode方法為這組虛擬結(jié)點(diǎn)得到惟一名稱                      byte[] digest = HashAlgorithm.computeMd5(node + i);                     /** Md5是一個(gè)16字節(jié)長(zhǎng)度的數(shù)組,將16字節(jié)的數(shù)組每四個(gè)字節(jié)一組,分別對(duì)應(yīng)一個(gè)虛擬結(jié)點(diǎn),這就是為什么上面把虛擬結(jié)點(diǎn)四個(gè)劃分一組的原因*/                     for (int h = 0; h < 4; h++)                     {                         long m = HashAlgorithm.hash(digest, h);                         ketamaNodes[m] = node;                     }                 }             }         }         public string GetPrimary(string k)         {             byte[] digest = HashAlgorithm.computeMd5(k);             string rv = GetNodeForKey(HashAlgorithm.hash(digest, 0));             return rv;         }         string GetNodeForKey(long hash)         {             string rv;             long key = hash;             //如果找到這個(gè)節(jié)點(diǎn),直接取節(jié)點(diǎn),返回                if (!ketamaNodes.ContainsKey(key))             {                 //得到大于當(dāng)前key的那個(gè)子Map,然后從中取出第一個(gè)key,就是大于且離它最近的那個(gè)key 說(shuō)明詳見: http://www.javaeye.com/topic/684087                 var tailMap = from coll in ketamaNodes                               where coll.Key > hash                               select new { coll.Key };                 if (tailMap == null || tailMap.Count() == 0)                     key = ketamaNodes.FirstOrDefault().Key;                 else                     key = tailMap.FirstOrDefault().Key;             }             rv = ketamaNodes[key];             return rv;         }     }     public class HashAlgorithm     {         public static long hash(byte[] digest, int nTime)         {             long rv = ((long)(digest[3 + nTime * 4] & 0xFF) << 24)                     | ((long)(digest[2 + nTime * 4] & 0xFF) << 16)                     | ((long)(digest[1 + nTime * 4] & 0xFF) << 8)                     | ((long)digest[0 + nTime * 4] & 0xFF);             return rv & 0xffffffffL; /* Truncate to 32-bits */         }         /**          * Get the md5 of the given key.          */         public static byte[] computeMd5(string k)         {             MD5 md5 = new MD5CryptoServiceProvider();              byte[] keyBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(k));             md5.Clear();             //md5.update(keyBytes);             //return md5.digest();             return keyBytes;         }     }
    復(fù)制代碼

    2、分布式測(cè)試

    1、假設(shè)有兩個(gè)server:0001和0002,循環(huán)調(diào)用10次看看Key值能不能均勻的映射到server上,代碼如下:

    復(fù)制代碼
        static void Main(string[] args)         {             //假設(shè)的server             List<string> nodes = new List<string>() { "0001","0002" };             KetamaNodeLocator k = new KetamaNodeLocator(nodes);             string str = "";             for (int i = 0; i < 10; i++)             {                 string Key="user_" + i;                 str += string.Format("Key:{0}分配到的Server為:{1}\n\n", Key, k.GetPrimary(Key));             }                          Console.WriteLine(str);                         Console.ReadLine();                       }
    復(fù)制代碼

    程序運(yùn)行兩次的結(jié)果如下,發(fā)現(xiàn)Key基本上均勻的分配到Server節(jié)點(diǎn)上了。

    2、我們?cè)谔砑右粋€(gè)0003的server節(jié)點(diǎn),代碼如下:

    復(fù)制代碼
      static void Main(string[] args)         {             //假設(shè)的server             List<string> nodes = new List<string>() { "0001","0002" ,"0003"};             KetamaNodeLocator k = new KetamaNodeLocator(nodes);             string str = "";             for (int i = 0; i < 10; i++)             {                 string Key="user_" + i;                 str += string.Format("Key:{0}分配到的Server為:{1}\n\n", Key, k.GetPrimary(Key));             }                          Console.WriteLine(str);                         Console.ReadLine();                       }
    復(fù)制代碼

    程序運(yùn)行兩次的結(jié)果如下:

    對(duì)比第一次的運(yùn)行結(jié)果發(fā)現(xiàn)只有user_5,user_7,user_9的緩存丟失,其他的緩存還可以命中。

    3、我們?nèi)サ魋erver 0002,運(yùn)行兩次的結(jié)果如下:

    對(duì)比第二次和本次運(yùn)行結(jié)果發(fā)現(xiàn) user_0,user_1,user_6 緩存丟失。

    結(jié)論

    通過(guò)一致性hash算法可以很好的解決Redis分布式的問(wèn)題,且當(dāng)Redis server增加或減少的時(shí)候,之前存儲(chǔ)的緩存命中率還是比較高的。

    http://www.cnblogs.com/lc-chenlong/p/4194150.html
    http://www.cnblogs.com/lc-chenlong/p/4195033.html
    http://www.cnblogs.com/lc-chenlong/p/3218157.html

    本文參考

    1、http://blog.csdn.net/chen77716/article/details/5949166

    2、http://www.cr173.com/html/6474_2.html


    轉(zhuǎn):http://www.cnblogs.com/lc-chenlong/p/4195814.html?utm_source=tuicool&utm_medium=referral



    posted on 2015-10-20 15:51 fly 閱讀(315) 評(píng)論(0)  編輯  收藏 所屬分類: J2EE-負(fù)載均衡
    主站蜘蛛池模板: 18禁黄网站禁片免费观看不卡| 亚洲一卡一卡二新区无人区| a级亚洲片精品久久久久久久 | 豆国产96在线|亚洲| 亚洲综合无码一区二区痴汉| 在线观看日本亚洲一区| 亚洲欧美国产欧美色欲| 亚洲aⅴ无码专区在线观看| 亚洲a∨国产av综合av下载| 日韩色日韩视频亚洲网站| 337p日本欧洲亚洲大胆人人 | 亚洲精品午夜无码专区| 亚洲色成人中文字幕网站| 亚洲乱码国产乱码精品精| 亚洲av无码成h人动漫无遮挡| 亚洲AV无码一区二区乱子伦| 少妇中文字幕乱码亚洲影视| 亚洲嫩草影院在线观看| 亚洲欧洲精品成人久久曰| 美女被免费视频网站a| jizz免费在线观看| 水蜜桃视频在线观看免费播放高清| 久久免费视频99| 麻豆最新国产剧情AV原创免费| 四虎成人免费网址在线| 亚洲国产成人久久综合区| 亚洲日韩精品无码专区网址| 久久亚洲精品成人av无码网站 | 四虎永久免费网站免费观看| 亚洲毛片av日韩av无码| 久久精品国产亚洲av麻豆| 久久亚洲精品中文字幕| 亚洲人av高清无码| 成年免费a级毛片免费看无码| 少妇太爽了在线观看免费视频| 欧洲精品成人免费视频在线观看| 成人免费视频观看无遮挡| 亚洲人成电影在线播放| 亚洲短视频男人的影院| 久久亚洲精品国产精品婷婷| 边摸边吃奶边做爽免费视频99|