前言
大家应该都知道Memcached要想实现分布式只能在客户端来完成,目前比较流行的是通过一致性hash算法来实现.常规的方法是将server的hash值与server的总台数进行求余,即hash%N,这种方法的弊端是当增减服务器时,将会有较多的缓存需要被重新分配且会造成缓存分配不均匀的情况(有可能某一台服务器分配的很多,其它的却很少).
今天分享一种叫做”ketama”的一致性hash算法,它通过虚拟节点的概念和不同的缓存分配规则有效的抑制了缓存分布不均匀,并最大限度地减少服务器增减时缓存的重新分布。
实现思路
假设我们现在有N台Memcached的Server,如果我们用统一的规则对memcached进行Set,Get操作. 使具有不同key的object很均衡的分散存储在这些Server上,Get操作时也是按同样规则去对应的Server上取出object,这样各个Server之间不就是一个整体了吗?
那到底是一个什么样的规则?
如下图所示,我们现在有5台(A,B,C,D,E)Memcached的Server,我们将其串联起来形成一个环形,每一台Server都代表圆环上的一个点,每一个点都具有唯一的Hash值,这个圆环上一共有2^32个点.
那么该如何确定每台Server具体分布在哪个点上? 这里我们通过”Ketama”的Hash算法来计算出每台Server的Hash值,拿到Hash值后就可以对应到圆环上点了.(可以用Server的IP地址作为Hash算法的Key.)
这样做的好处是,如下图当我新增Server F时,那么我只需要将hash值落在C和F之间的object从原本的D上重新分配到F上就可以了,其它的server上的缓存不需要重新分配,并且新增的Server也能及时帮忙缓冲其它Server的压力.
到此我们已经解决了增减服务器时大量缓存需要被重新分配的弊端.那该如何解决缓存分配不均匀的问题呢?因为现在我们的server只占据圆环上的6个点,而圆环上总共有2^32个点,这极其容易导致某一台server上热点非常多,某一台上热点很少的情况.
”虚拟节点”的概念很好的解决了这种负载不均衡的问题.通过将每台物理存在的Server分割成N个虚拟的Server节点(N通常根据物理Server个数来定,这里有个比较好的阈值为250).这样每个物理Server实际上对应了N个虚拟的节点. 存储点多了,各个Server的负载自然要均衡一些.就像地铁站出口一样,出口越多,每个出口出现拥挤的情况就会越少.
代码实现:
//保存所有虚拟节点信息, key : 虚拟节点的hash key, value: 虚拟节点对应的真实server private Dictionary<uint, string> hostDictionary = new Dictionary<uint, string>(); //保存所有虚拟节点的hash key, 已按升序排序 private uint[] ketamaHashKeys = new uint[] { }; //保存真实server主机地址 private string[] realHostArr = new string[] { }; //每台真实server对应虚拟节点个数 private int VirtualNodeNum = 250; public KetamaVirtualNodeInit(string[] hostArr) { this.realHostArr = hostArr; this.InitVirtualNodes(); } /// <summary> /// 初始化虚拟节点 /// </summary> private void InitVirtualNodes() { hostDictionary = new Dictionary<uint, string>(); List<uint> hostKeys = new List<uint>(); if (realHostArr == null || realHostArr.Length == 0) { throw new Exception("不能传入空的Server集合"); } for (int i = 0; i < realHostArr.Length; i++) { for (int j = 0; j < VirtualNodeNum; j++) { byte[] nameBytes = Encoding.UTF8.GetBytes(string.Format("{0}-node{1}", realHostArr[i], j)); //调用Ketama hash算法获取hash key uint hashKey = BitConverter.ToUInt32(new KetamaHash().ComputeHash(nameBytes), 0); hostKeys.Add(hashKey); if (hostDictionary.ContainsKey(hashKey)) { throw new Exception("创建虚拟节点时发现相同hash key,请检查是否传入了相同Server"); } hostDictionary.Add(hashKey, realHostArr[i]); } } hostKeys.Sort(); ketamaHashKeys = hostKeys.ToArray(); }
一致性hash算法的分配规则
到此我们已经知道了所有虚拟节点的Hash值, 现在让我们来看下当我们拿到一个对象时如何存入Server, 或是拿到一个对象的Key时该如何取出对象.
Set一个对象时,先将对象的Key作为”Ketama”算法的Key,计算出Hash值后我们需要做下面几个步骤.
1:首先检查虚拟节点当中是否有与当前对象Hash值相等的,如有则直接将对象存入那个Hash值相等的节点,后面的步骤就不继续了.
2:如没有,则找出第一个比当前对象Hash值要大的节点,(节点的Hash值按升序进行排序,圆环上对应按照顺时针来排列),即离对象最近的节点,然后将对象存入该节点.
3:如果没有找到Hash值比对象要大的Server,证明对象的Hash值是介于最后一个节点和第一个节点之间的,也就是圆环上的E和A之间.这种情况就直接将对象存入第一个节点,即A.
代码实现:
/// <summary> /// 根据hash key 获取对应的真实Server /// </summary> /// <param name="hash"></param> /// <returns></returns> public string GetHostByHashKey(string key) { byte[] bytes = Encoding.UTF8.GetBytes(key); uint hash = BitConverter.ToUInt32(new KetamaHash().ComputeHash(bytes), 0); //寻找与当前hash值相等的Server. int i = Array.BinarySearch(ketamaHashKeys, hash); //如果i小于零则表示没有hash值相等的虚拟节点 if (i < 0) { //将i继续按位求补,得到数组中第一个大于当前hash值的虚拟节点 i = ~i; //如果按位求补后的i大于等于数组的大小,则表示数组中没有大于当前hash值的虚拟节点 //此时直接取第一个server if (i >= ketamaHashKeys.Length) { i = 0; } } //根据虚拟节点的hash key 返回对应的真实server host地址 return hostDictionary[ketamaHashKeys[i]]; }
Get一个对象,同样也是通过”Ketama”算法计算出Hash值,然后与Set过程一样寻找节点,找到之后直接取出对象即可.
那么这个”Ketama”到底长什么样呢,让我们来看看代码实现.
/// <summary> /// Ketama hash加密算法 /// 关于HashAlgorithm参见MSDN链接 /// http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.hashalgorithm%28v=vs.110%29.aspx /// </summary> public class KetamaHash : HashAlgorithm { private static readonly uint FNV_prime = 16777619; private static readonly uint offset_basis = 2166136261; protected uint hash; public KetamaHash() { HashSizeValue = 32; } public override void Initialize() { hash = offset_basis; } protected override void HashCore(byte[] array, int ibStart, int cbSize) { int length = ibStart + cbSize; for (int i = ibStart; i < length; i++) { hash = (hash * FNV_prime) ^ array[i]; } } protected override byte[] HashFinal() { hash += hash << 13; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; return BitConverter.GetBytes(hash); } }
测试性能
最后我把自己参考BeitMemcached写的算法与老代(Discuz!代震军)参考SPYMemcached写的做了一下对比.
源码在后面有下载.
结果:查找5W个key的时间比老代的版本快了100多倍,但在负载均衡方面差了一些.
测试数据:
1:真实Server都是5台
2:随机生成5W个字符串key(生成方法直接拿老代的)
3:虚拟节点都是250个
我的版本:
老代的版本:
参考资料
老代: 一致性Hash算法(KetamaHash)的c#实现
测试代码下载:Memcached-ketama
相关推荐
一致性哈希算法是一种分布式哈希(Distributed Hash Table, DHT)技术,旨在解决在分布式环境中数据分布不均匀的问题。Ketama算法是基于一致性哈希的一种优化实现,由Last.fm公司的Simon Willison提出,其目标是在...
一致性Hash算法通过巧妙的设计,不仅解决了传统哈希方法在动态环境中存在的问题,还为分布式系统的稳定性、可扩展性和性能提供了有力支持。通过理解其核心原理和应用,我们可以更好地应对分布式环境下的挑战,并构建...
一致性哈希算法(Consistent Hashing)是一种在分布式系统中平衡数据分布的策略,尤其适用于缓存服务如Memcached或Redis。它的核心思想是通过哈希函数将对象映射到一个固定大小的环形空间中,然后将服务器也映射到这个...
此外,一致性哈希算法在分布式缓存如Memcached、Redis中也得到了广泛应用。它不仅简化了数据分布的逻辑,还允许动态扩展和收缩集群规模,无需大规模的数据迁移。 在文件名为“distribute-mysql”的压缩包中,可能...
一致性哈希(Consistent Hashing)是一种分布式哈希表(DHT)的算法,它主要应用于分布式缓存、负载均衡等场景,旨在解决在动态扩展或收缩系统规模时,尽量减少数据迁移的问题。在这个简单的实现中,我们将探讨如何...
在实际应用中,一致性哈希算法不仅用于分布式缓存系统,还可以用于分布式数据库、CDN内容分发网络等场景,有效解决了动态扩展性和负载均衡的问题。它通过最小化数据迁移,降低了系统在节点变化时的复杂性和性能影响...
一致性哈希(Consistent Hashing)是一种分布式哈希算法,主要应用于分布式缓存、负载均衡等领域,以解决在分布式环境中动态添加或删除节点时,尽可能少地改变已有的哈希映射关系。在这个Java实现中,我们看到的是...
一致性哈希算法应用及优化是IT领域中分布式系统设计的核心技术之一,特别是在处理大规模数据分布与缓存系统中,其重要性不言而喻。本文将深入探讨一致性哈希算法的基本概念、工作原理以及在实际场景中的应用和优化...
一致性哈希算法是一种分布式哈希(Distributed Hash Table, DHT)技术,它解决了在分布式环境中数据分片和负载均衡的问题。在传统的哈希算法中,如果增加或减少服务器节点,会导致大量数据重新分配,而一致性哈希...
在实际应用中,一致性哈希常用于构建分布式缓存系统(如Memcached和Redis)、分布式数据库(如Cassandra)以及CDN内容分发网络等场景。它的核心在于解决动态扩展和收缩的分布式系统中数据分片的问题,以实现高效的...
1. "一致性哈希对缓存命中率的影响实验报告.doc":这份文档可能详细介绍了如何使用一致性哈希算法来分配和检索数据在Memcached中的存储,以及该算法如何影响缓存的命中率。一致性哈希是解决分布式缓存中数据分布不均...
在实际应用中,一致性哈希算法通常用于分布式缓存系统中,如Redis、Memcached集群。通过一致性哈希,系统能够实现以下目标: - **节点动态扩展**:当系统需要扩展节点时,一致性哈希可以保证只有部分缓存数据需要...
### 分布式Session解决方案与一致性Hash详解 #### 一、问题背景及提出 ...同时,一致性Hash算法作为一种优化数据分布的技术,也在分布式环境中发挥着重要作用。开发者应根据实际场景和技术栈的特点选择最适合的方案。
一致性哈希(Consistent Hashing)是一种分布式哈希表(DHT, Distributed Hash Table)的算法,主要用于解决在分布式系统中数据分片、负载均衡、缓存分发等问题。在云计算和大数据领域,一致性哈希算法有着广泛的应用,...
一致性哈希(Consistent Hashing)是一种分布式哈希(Distributed Hash Table,DHT)算法,主要用于解决在分布式系统中的数据存储和检索问题。在云计算、缓存系统(如Redis、Memcached)以及负载均衡等领域广泛应用...
memcached 还支持一致性 Hash 算法,以确保数据的分布式存储。 memcached 的应用场景 memcached 广泛应用于各种 Web 应用程序中,包括社交媒体、电商平台、博客平台等。memcached 可以用于缓存用户数据、文章数据...
一致性哈希算法(Consistent Hashing)是一种在分布式系统中实现负载均衡的算法,尤其在分布式缓存如Memcached和Redis等场景下广泛使用。它解决了传统哈希算法在节点增减时导致的大量数据迁移问题,提高了系统的可用...
在实际应用中,一致性哈希常用于分布式缓存(如Memcached、Redis集群)、分布式数据库分片、负载均衡器等场景,帮助构建可扩展、高可用的分布式系统。 总之,`libconhash`是一个方便的C语言实现的一致性哈希库,...