分析Memcached客户端如何把缓存数据分布到多个服务器上

Memcached客户端可以设多个memcached服务器,它是如何把数据分发到各个服务器上,而使各个服务器负载平衡的呢?

可以看看.net版中的客户端中的源码,就可以知道 先看代码:


  1 /// <summary>
  2         /// Returns appropriate SockIO object given
  3         /// string cache key and optional hashcode.
  4         /// 
  5         /// Trys to get SockIO from pool.  Fails over
  6         /// to additional pools in event of server failure.
  7         /// </summary>
  8         /// <param name="key">hashcode for cache key</param>
  9         /// <param name="hashCode">if not null, then the int hashcode to use</param>
 10         /// <returns>SockIO obj connected to server</returns>
 11         public SockIO GetSock(string key, object hashCode)
 12         {
 13             string hashCodeString = "<null>";
 14             if(hashCode != null)
 15                 hashCodeString = hashCode.ToString();
 16 
 17             if(Log.IsDebugEnabled)
 18             {
 19                 Log.Debug(GetLocalizedString("cache socket pick").Replace("$$Key$$", key).Replace("$$HashCode$$", hashCodeString));
 20             }
 21 
 22             if (key == null || key.Length == 0)
 23             {
 24                 if(Log.IsDebugEnabled)
 25                 {
 26                     Log.Debug(GetLocalizedString("null key"));
 27                 }
 28                 return null;
 29             }
 30 
 31             if(!_initialized)
 32             {
 33                 if(Log.IsErrorEnabled)
 34                 {
 35                     Log.Error(GetLocalizedString("get socket from uninitialized pool"));
 36                 }
 37                 return null;
 38             }
 39 
 40             // if no servers return null
 41             if(_buckets.Count == 0)
 42                 return null;
 43 
 44             // if only one server, return it
 45             if(_buckets.Count == 1)
 46                 return GetConnection((string)_buckets[0]);
 47 
 48             int tries = 0;
 49 
 50             // generate hashcode
 51             int hv;
 52             if(hashCode != null)
 53             {
 54                 hv = (int)hashCode;
 55             }
 56             else
 57             {
 58 
 59                 // NATIVE_HASH = 0
 60                 // OLD_COMPAT_HASH = 1
 61                 // NEW_COMPAT_HASH = 2
 62                 switch(_hashingAlgorithm)
 63                 {
 64                     case HashingAlgorithm.Native:
 65                         hv = key.GetHashCode();
 66                         break;
 67 
 68                     case HashingAlgorithm.OldCompatibleHash:
 69                         hv = OriginalHashingAlgorithm(key);
 70                         break;
 71 
 72                     case HashingAlgorithm.NewCompatibleHash:
 73                         hv = NewHashingAlgorithm(key);
 74                         break;
 75 
 76                     default:
 77                         // use the native hash as a default
 78                         hv = key.GetHashCode();
 79                         _hashingAlgorithm = HashingAlgorithm.Native;
 80                         break;
 81                 }
 82             }
 83 
 84             // keep trying different servers until we find one
 85             while(tries++ <= _buckets.Count)
 86             {
 87                 // get bucket using hashcode 
 88                 // get one from factory
 89                 int bucket = hv % _buckets.Count;
 90                 if(bucket < 0)
 91                     bucket += _buckets.Count;
 92 
 93                 SockIO sock = GetConnection((string)_buckets[bucket]);
 94 
 95                 if(Log.IsDebugEnabled)
 96                 {
 97                     Log.Debug(GetLocalizedString("cache choose").Replace("$$Bucket$$", _buckets[bucket].ToString()).Replace("$$Key$$", key));
 98                 }
 99 
100                 if(sock != null)
101                     return sock;
102 
103                 // if we do not want to failover, then bail here
104                 if(!_failover)
105                     return null;
106 
107                 // if we failed to get a socket from this server
108                 // then we try again by adding an incrementer to the
109                 // current key and then rehashing 
110                 switch(_hashingAlgorithm)
111                 {
112                     case HashingAlgorithm.Native:
113                         hv += ((string)("" + tries + key)).GetHashCode();
114                         break;
115 
116                     case HashingAlgorithm.OldCompatibleHash:
117                         hv += OriginalHashingAlgorithm("" + tries + key);
118                         break;
119 
120                     case HashingAlgorithm.NewCompatibleHash:
121                         hv += NewHashingAlgorithm("" + tries + key);
122                         break;
123 
124                     default:
125                         // use the native hash as a default
126                         hv += ((string)("" + tries + key)).GetHashCode();
127                         _hashingAlgorithm = HashingAlgorithm.Native;
128                         break;
129                 }
130             }
131 
132             return null;
133         }
134

上面代码是代码文件SockIOPool.cs中的一个方法,从方法签名上可以看出,获取一个socket连接是根据需要缓存数据的唯一键和它的哈希值,因为缓存的数据的键值是唯一的,所以它的哈希代码也是唯一的;

再看看上面方法中的以下代码:

int bucket = hv % _buckets.Count;

if(bucket < 0)

bucket += _buckets.Count;

SockIO sock = GetConnection((string)_buckets[bucket]);

具体的选择服务器的算法是:唯一键值的哈希值与存放服务器列表中服务器(服务器地址记录不是唯一的)的数量进行模数运算来选择服务器的地址的。所以数据缓存在那台服务器取决于缓存数据的唯一键值所产生的哈希值和存放服务器列表中服务器的数量值,所以访问memcached服务的所有客户端操作数据时都必须使用同一种哈希算法和相同的服务器列表配置,否则就会或取不到数据或者重复存取数据。由于不同数据的唯一键所对应的哈希值不同,所以不同的数据就有可能分散到不同的服务器上,达到多个服务器负载平衡的目的。

如果几台服务器当中,负载能力各不同,想根据具体情况来配置各个服务器负载作用,也是可以做到的。看上面代码,可以知道程序是从_buckets中获取得服务器地址的,_buckets存放着服务器的地址信息,服务器地址在_bucket列表中并不是唯一的,它是可以有重复记录的。相同的服务器地址在_bucket重复记录越多,它被选中的机率就越大,相应负载作用也就越大。

怎么设置服务器让它发挥更大的负载作用,如下面代码:

String[] serverlist = {"192.168.1.2:11211", "192.168.1.3:11211"};

int[] weights   = new int[]{5, 2};

SockIOPool pool = SockIOPool.GetInstance();

pool.SetServers(serverlist);

pool.SetWeights(weights);

pool.Initialize();

pool.SetWeights(weights)方法就是设配各个服务器负载作用的系数,系数值越大,其负载作用也就越大。如上面的例子,就设服务器192.168.1.2的负载系数为5,服务器192.168.1.3的负载系数为2,也就说服务器192.168.1.2 比192.168.1.3的负载作用大。

程序中根据缓存数据中的唯一
键标识的哈希值跟服务器列表中服务器记录数量求模运算来确定数据的缓存的位置的方法,算法的优点:能够把数据匀均的分散到各个服务器上数据服务器负载平
衡,当然也可以通过配置使不同服务器有不同的负载作用。但也有缺点:使同类的数据过于分散,同个模块的数据都分散到不同的数据,不好统一管理和唯护;比
如:现在有A、B、C、D四台服务器一起来做缓存服务器,数月后C台服务器突然死掉不可用啦,那么按算法缓存在C台服务器的数据都不可用啦,但客户端还是按原来的四台服务器的算法来取操作数据,所以分布在C服务上的数据在C服务器恢复可用之前都不可用,都必须从数据库中读取数据,并且不能添加到缓存中,因为只要缓存数据的Key不变,它还是会被计算分配到C服务器上。如果想把分配到C服务器就必须全部初始化A、B、D三台服务器上的所有数据,并把C服务器从服务器列表中移除。

如果我们能够把数据分类分布到各个服务器中,同类型的数据分布到相同的服务器;比如说,A服务器存放用户日志模块信息,B服务器存放用户相册模块信息,C服务器存放音乐模块信息,D服务器存放用户基础信息。如果C服务器不可用后,就可以更改下配置使它存放在其它服务器当中,而且并不影响其它服务器的缓存信息。

解决方法1:不同的模块使用不同memcached客户端实例,这样不同模块就可以配置不同的服务器列表,这样不同模块的数据就缓存到了不同的服务器中。这样,当某台服务器不可用后,只会影响到相应memcached客户端实例的数据,而不会影响到其它客户端实例的数据。

解决方法2:修改或添加新的算法,并在数据唯一键中添加命名空间,算法根据配置和数据唯一键中命名空间来选择不同的Socket连接,也就是服务器啦。

数据项唯一键(key)的定义:命名空间.数据项ID,就跟编程中的” 命名空间”一样,经如说用户有一篇日志的ID是”999999”, 那么这条篇日志的唯一键就是:Sns.UserLogs.Log.999999,当然我们存贮的时候考虑性能问题,可以用一个短的数值来代替命名空间。这样在选择Socket的时候就可以根据数据项中的唯一键来选择啦。

时间: 2024-10-11 18:17:37

分析Memcached客户端如何把缓存数据分布到多个服务器上的相关文章

日志分析-2.发送windows日志到一个远程的rsyslog服务器上

要将一个Windows客户端的日志消息转发到我们的rsyslog服务器,需要一个安装 Windows syslog 代理. 1.SyslogAgent http://download.cnet.com/Datagram-SyslogAgent/3000-2085_4-10370938.html 2.安装SyslogAgent 这里省略安装步骤 3.相应的设置 需要将其配置为作为服务运行,连接install,点 Rsyslog 服务器如何配置,请查看 http://yuanji6699.blog.

Memcached客户端性能评测报告

转载:http://wenku.baidu.com/view/38a195c02cc58bd63186bdcb; 1.      Memcached客户端简介 本次memcached客户端评测以JAVA开源客户项目为评测对象,分别选择Memcached-Java-Client.spymemcached.xmemcached.alisoft xplatform asf cache四个项目,其中分别由两个产品基于堵塞式IO和NOI实现的客户端.两个国内开源项目或两个国外开源项目. 选择的客户端简介如

ASP.Net MVC4+Memcached+CodeFirst实现分布式缓存

ASP.Net MVC4+Memcached+CodeFirst实现分布式缓存 part 1:给我点时间,允许我感慨一下2016年 正好有时间,总结一下最近使用的一些技术,也算是为2016年画上一个完美的句号,回顾2016年,感受颇多,感恩那些帮助我的人.展望2017年,我相信一定会遇到一个更好的自己.附上自己喜欢的一张图片: 好了~~~装逼结束,下面开始说说如何实现分布式缓存在项目中的应用. part2:先分析以下需求 软件架构从单机到分布式遇到的问题(当然这是一个很深的问题,由于能力有限今天

Memcached源代码分析 - Memcached源代码分析之消息回应(3)

文章列表: <Memcached源代码分析 - Memcached源代码分析之基于Libevent的网络模型(1)> <Memcached源代码分析 - Memcached源代码分析之命令解析(2)> <Memcached源代码分析 - Memcached源代码分析之消息回应(3)  > <Memcached源代码分析 - Memcached源代码分析之HashTable(4) > <Memcached源代码分析 - Memcached源代码分析之增删

Memcached分布式内存对象缓存系统

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. 一个用PHP编写的可视化的MemCached管理系统. MemAdmin是一款可视化的Memcached管理与监控工具,使用PHP开发,体积小,

查看、分析memcached使用状态

检查.剖析memcached运用状况 访问量上升,数据库压力大,怎样办?好办法是在中心挡一层缓存!这个缓存需求高效,不能比数据库慢,不然服务质量受影响:假如能把数据用hash打散存储到硬盘,也是能够的,不过在内存越来越廉价的今日,仍是运用内存吧! mysql也有自个的缓存,也是存储在内存的,但是有一个说法是: 以下是引证片段: 只能有一个实例 意味着你能存储内容的上限即是你服务器的可用内存,一台服务器能有多少内存?你又能存多少呢? 只需有写操作,mysql的query cache就失效 只需数据

高性能Web服务器Nginx的配置与部署研究(13)应用模块之Memcached模块+Proxy_Cache双层缓存模式

通过<高性能Web服务器Nginx的配置与部署研究——(11)应用模块之Memcached模块的两大应用场景>一文,我们知道Nginx从Memcached读取数据的方式,如果命中,那么效率是相当高的.那么: 1. 如果不命中呢? 我们可以到相应的数据服务器上读取数据,然后将它缓存到Nginx服务器上,然后再将该数据返回给客户端.这样,对于该资源,只有穿透 Memcached的第一次请求是需要到数据服务器读取的,之后在缓存过期时间之内的所有请求,都是读取Nginx本地的.不过Nginx的 pro

memcached源码分析-----memcached启动参数详解以及关键配置的默认值

转载请注明出处: http://blog.csdn.net/luotuo44/article/details/42672913 本文开启本系列博文的代码分析.本系列博文研究是memcached版本是1.4.21. 本文将给出memcached启动时各个参数的详细解释以及一些关键配置的默认值.以便在分析memcached源码的时候好随时查看.当然也方便使用memcached时可以随时查看各个参数的含义.<如何阅读memcached源码>说到memcached有很多全局变量(也就是关键配置),这些

Memcached 高性能分布式对象缓存系统

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. memcached的服务器客户端通信并不使用复杂的XML等格式,而使用简单的基于文本行的协议. 因此,通过telnet也能在memcached上