Memcache分布式伸缩扩展算法的一点疑问和想法
近来在看《京东技术揭秘》和李智慧大大的《大型网站技术架构 核心原理与案例分析》,有一种揭起女神裙角,方知神秘造物的感觉。里面有一句话很有激励的感觉,“世界上没有解决不了的问题,只在于你能遇到什么样的问题。解决的问题多了....”。这话好熟悉,不自觉想到以前的一个领导说的话,跟这差不多。现在豁然开朗,最近也处在发展方向的选择之中,想法就是拥抱困难吧。只有扫平荆棘走下去的路才有一往无前的勇气和资本。
闲话不多说,这里遇到个问题,希望诸君大牛小牛们给点指点。
书里提到MemCache分布式部署的hash算法有三种:服务器数的余数算法、一致性hash环算法、还有种比较给力的虚拟环之类的一种技术。其中都把第一种当做反面教材,所谓99台服务器再添加一台服务器会造成99%的命中丢失,然后再然后都去读取数据源,一般是数据库,然后数据库服务器就悲剧的挂了(好吧,有个专业词语叫宕(dang第四声好吧,刚才被同事鄙视了,一直读成tuo,然后半天打不出来,囧)机)。然后作为稍微正面一点的一致性hash环算法,看了半天(对不起数学老师)得到的答案是99%命中,好吧,终究还是有不命中的情况。然后感觉怪怪的,总觉得剧情不应该是这样的啊,哪里不对呢。最后一个方法我回去再仔细研究研究再说。
情况剧情是脑子里一直在想究竟怎么样才能100%命中呢。晚上躺床上想啊想忽然就想到了一种方法,感觉也很简单。但是不知道为啥这么简单没人用,可能是我水平不够吧,但愿有大牛看了指导下问题出在哪里。下面是个简单的代码片段。
1 public string GetData(string key) 2 { 3 //获取需要存放数据的key的hashcode 4 int hashCode = key.GetHashCode(); 5 //通过code%当前服务器数量来获取一个服务器队列的索引 6 int hashkey = hashCode % MachineServiceStatus.CurrentVer.ServiceMachineNum; 7 //通过索引获取对应的服务器,然后获取对应的memcache数据源 这里简单放了个dictionary 8 var cache = Memcaches.GetCache(hashkey); 9 if (cache == null) 10 { 11 throw new Exception("服务器数量设置不正确"); 12 } 13 //第一次就命中的时候 14 if (cache.ContainsKey(key)) 15 { 16 return cache[key] as string; 17 } 18 19 20 //第一次没命中 21 /* 22 是否存在上个版本的服务器状态,如果超时了,获取第一次部署的服务器,当然就没了 23 如果在分布式伸缩扩展的情况下无论用的原来几种算法,都应该可以在第二次命中的情况获取对应的缓存信息 24 没必要从其它地方消耗性能重新获取数据。然后在第一次命中的地方放入第二次命中的数据,这样下次就不需 25 要执行第二次命中了 26 */ 27 if (MachineServiceStatus.PreferVer != null) 28 { 29 var preKey = hashCode % MachineServiceStatus.PreferVer.ServiceMachineNum; 30 //从上个版本的machinelst取出本来应该存放该缓存记录的服务器对应的ip 31 var ip = MachineServiceStatus.PreferVer.MachineLst[preKey]; 32 //判断当前服务器列表中是否存在该ip,如果不存在,说明该服务器挂了 33 if (!MachineServiceStatus.CurrentVer.MachineLst.Contains(ip)) 34 { 35 throw new Exception("本来缓存该记录的机器已然挂了,自觉重新取吧"); 36 } 37 var newCache = Memcaches.GetCache(ip); 38 if (newCache.ContainsKey(key)) 39 { 40 //将第二次命中的数据对应放入第一次命中的位置,那么下次就不需要再来第二次查找了 41 string copyValue = newCache[key]; 42 cache.Add(key, copyValue); 43 //移除第二次命中缓存对应的数据 ---不过并发的情况如果这么移除的话会造成很多请求命中不了。所以一般情况应该不清了吧 44 //newCache.Remove(key); 45 return copyValue; 46 } 47 } 48 throw new Exception("第二次也命不中,算了吧,回去洗洗睡吧"); 49 }
代码也很简单呢,思想就是把上次的服务器列表保存下来,更新的服务器列表之所以会造成缓存不命中按我看书的理解应该就是对应的索引乱掉了。那我下次取的时候就可以从旧的列表中找到对应的机器,然后从那个机器上取出数据,再放入新的索引位置的机器中。这样再下次取就可以从新的位置取了。这样感觉就没有缓存不命中的情况了啊。不过这里有个情况是服务器要增加或者减少机器,那么就要对应的更新MachineServiceStatus,把上次的服务器列表保存下来。原理上看起来应该会好一些。
当然刚刚也有问题说用余数的方法,需要变动的缓存数据毕竟会有很多,就是把A机器上的数据因为索引变动要拷贝到B上去才能适应新的索引。当然我只是写了个余数法的简单例子,同样的方法可以用在一致性hash环算法上的么。
文字比较多,想下班了。在园子里潜水好几年了。第一次发帖。也希望大牛们给点指点。
浙公网安备 33010602011771号