memcached的总结和分布式一致性hash

标签: memcached 分布 一致性 | 发表时间:2014-08-08 19:51 | 作者:love398146779
出处:http://www.iteye.com
当前很多大型的web系统为了减轻数据库服务器负载,会采用memchached作为缓存系统以提高响应速度。

目录:

memchached简介
hash
取模
一致性hash
虚拟节点
源码解析
参考资料
1. memchached简介
memcached是一个开源的高性能分布式内存对象缓存系统。
其实思想还是比较简单的,实现包括server端(memcached开源项目一般只单指server端)和client端两部分:

server端本质是一个in-memory key-value store,通过在内存中维护一个大的hashmap用来存储小块的任意数据,对外通过统一的简单接口(memcached protocol)来提供操作。
client端是一个library,负责处理memcached protocol的网络通信细节,与memcached server通信,针对各种语言的不同实现分装了易用的API实现了与不同语言平台的集成。
web系统则通过client库来使用memcached进行对象缓存。
2. hash
memcached的分布式主要体现在client端,对于server端,仅仅是部署多个memcached server组成集群,每个server独自维护自己的数据(互相之间没有任何通信),通过daemon监听端口等待client端的请求。
而在client端,通过一致的hash算法,将要存储的数据分布到某个特定的server上进行存储,后续读取查询使用同样的hash算法即可定位。

client端可以采用各种hash算法来定位server:
取模
最简单的hash算法

targetServer = serverList[hash(key) % serverList.size]

直接用key的hash值(计算key的hash值的方法可以自由选择,比如算法CRC32、MD5,甚至本地hash系统,如java的hashcode)模上server总数来定位目标server。这种算法不仅简单,而且具有不错的随机分布特性。

但是问题也很明显,server总数不能轻易变化。因为如果增加/减少memcached server的数量,对原先存储的所有key的后续查询都将定位到别的server上,导致所有的cache都不能被命中而失效。

一致性hash
为了解决这个问题,需要采用一致性hash算法(consistent hash)
相对于取模的算法,一致性hash算法除了计算key的hash值外,还会计算每个server对应的hash值,然后将这些hash值映射到一个有限的值域上(比如0~2^32)。通过寻找hash值大于hash(key)的最小server作为存储该key数据的目标server。如果找不到,则直接把具有最小hash值的server作为目标server。

为了方便理解,可以把这个有限值域理解成一个环,值顺时针递增。

如上图所示,集群中一共有5个memcached server,已通过server的hash值分布到环中。

如果现在有一个写入cache的请求,首先计算x=hash(key),映射到环中,然后从x顺时针查找,把找到的第一个server作为目标server来存储cache,如果超过了2^32仍然找不到,则命中第一个server。比如x的值介于A~B之间,那么命中的server节点应该是B节点

可以看到,通过这种算法,对于同一个key,存储和后续的查询都会定位到同一个memcached server上。

那么它是怎么解决增/删server导致的cache不能命中的问题呢?
假设,现在增加一个server F,如下图


此时,cache不能命中的问题仍然存在,但是只存在于B~F之间的位置(由C变成了F),其他位置(包括F~C)的cache的命中不受影响(删除server的情况类似)。尽管仍然有cache不能命中的存在,但是相对于取模的方式已经大幅减少了不能命中的cache数量。

虚拟节点
但是,这种算法相对于取模方式也有一个缺陷:当server数量很少时,很可能他们在环中的分布不是特别均匀,进而导致cache不能均匀分布到所有的server上。

如图,一共有3台server – 1,2,4。命中4的几率远远高于1和2。
为解决这个问题,需要使用虚拟节点的思想:为每个物理节点(server)在环上分配100~200个点,这样环上的节点较多,就能抑制分布不均匀。
当为cache定位目标server时,如果定位到虚拟节点上,就表示cache真正的存储位置是在该虚拟节点代表的实际物理server上。

另外,如果每个实际server的负载能力不同,可以赋予不同的权重,根据权重分配不同数量的虚拟节点。

// 采用有序map来模拟环   
this.consistentBuckets = new TreeMap();  
  
MessageDigest md5 = MD5.get();//用MD5来计算key和server的hash值   
  
// 计算总权重   
if ( this.totalWeight   for ( int i = 0; i < this.weights.length; i++ )  
        this.totalWeight += ( this.weights[i] == null ) ? 1 : this.weights[i];  
} else if ( this.weights == null ) {  
    this.totalWeight = this.servers.length;  
}  
  
// 为每个server分配虚拟节点   
for ( int i = 0; i < servers.length; i++ ) {  
    // 计算当前server的权重   
    int thisWeight = 1;  
    if ( this.weights != null && this.weights[i] != null )  
        thisWeight = this.weights[i];  
  
    // factor用来控制每个server分配的虚拟节点数量   
    // 权重都相同时,factor=40   
    // 权重不同时,factor=40*server总数*该server权重所占的百分比   
    // 总的来说,权重越大,factor越大,可以分配越多的虚拟节点   
    double factor = Math.floor( ((double)(40 * this.servers.length * thisWeight)) / (double)this.totalWeight );  
  
    for ( long j = 0; j < factor; j++ ) {  
        // 每个server有factor个hash值   
        // 使用server的域名或IP加上编号来计算hash值   
        // 比如server - "172.45.155.25:11111"就有factor个数据用来生成hash值:   
        // 172.45.155.25:11111-1, 172.45.155.25:11111-2, ..., 172.45.155.25:11111-factor   
        byte[] d = md5.digest( ( servers[i] + "-" + j ).getBytes() );  
  
        // 每个hash值生成4个虚拟节点   
        for ( int h = 0 ; h < 4; h++ ) {  
            Long k =  
                ((long)(d[3+h*4]&0xFF) << 24)  
                  | ((long)(d[2+h*4]&0xFF) << 16)  
                  | ((long)(d[1+h*4]&0xFF) << 8 )  
                  | ((long)(d[0+h*4]&0xFF));  
  
            // 在环上保存节点   
            consistentBuckets.put( k, servers[i] );  
        }  
  
    }  
    // 每个server一共分配4*factor个虚拟节点   
}  
// 采用有序map来模拟环
this.consistentBuckets = new TreeMap();

MessageDigest md5 = MD5.get();//用MD5来计算key和server的hash值

// 计算总权重
if ( this.totalWeight 	for ( int i = 0; i < this.weights.length; i++ )
		this.totalWeight += ( this.weights[i] == null ) ? 1 : this.weights[i];
} else if ( this.weights == null ) {
	this.totalWeight = this.servers.length;
}

// 为每个server分配虚拟节点
for ( int i = 0; i < servers.length; i++ ) {
	// 计算当前server的权重
	int thisWeight = 1;
	if ( this.weights != null && this.weights[i] != null )
		thisWeight = this.weights[i];

	// factor用来控制每个server分配的虚拟节点数量
	// 权重都相同时,factor=40
	// 权重不同时,factor=40*server总数*该server权重所占的百分比
	// 总的来说,权重越大,factor越大,可以分配越多的虚拟节点
	double factor = Math.floor( ((double)(40 * this.servers.length * thisWeight)) / (double)this.totalWeight );

	for ( long j = 0; j < factor; j++ ) {
		// 每个server有factor个hash值
		// 使用server的域名或IP加上编号来计算hash值
		// 比如server - "172.45.155.25:11111"就有factor个数据用来生成hash值:
		// 172.45.155.25:11111-1, 172.45.155.25:11111-2, ..., 172.45.155.25:11111-factor
		byte[] d = md5.digest( ( servers[i] + "-" + j ).getBytes() );

		// 每个hash值生成4个虚拟节点
		for ( int h = 0 ; h < 4; h++ ) {
			Long k =
				((long)(d[3+h*4]&0xFF) << 24)
			      | ((long)(d[2+h*4]&0xFF) << 16)
			      | ((long)(d[1+h*4]&0xFF) << 8 )
			      | ((long)(d[0+h*4]&0xFF));

			// 在环上保存节点
			consistentBuckets.put( k, servers[i] );
		}

	}
	// 每个server一共分配4*factor个虚拟节点
}


// 用MD5来计算key的hash值   
MessageDigest md5 = MD5.get();  
md5.reset();  
md5.update( key.getBytes() );  
byte[] bKey = md5.digest();  
  
// 取MD5值的低32位作为key的hash值   
long hv = ((long)(bKey[3]&0xFF) << 24) | ((long)(bKey[2]&0xFF) << 16) | ((long)(bKey[1]&0xFF) << 8 ) | (long)(bKey[0]&0xFF);  
  
// hv的tailMap的第一个虚拟节点对应的即是目标server   
SortedMap tmap = this.consistentBuckets.tailMap( hv );  
return ( tmap.isEmpty() ) ? this.consistentBuckets.firstKey() : tmap.firstKey();  


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [memcached 分布 一致性] 推荐:

memcached的总结和分布式一致性hash

- - 开源软件 - ITeye博客
当前很多大型的web系统为了减轻数据库服务器负载,会采用memchached作为缓存系统以提高响应速度. memcached是一个开源的高性能分布式内存对象缓存系统. 其实思想还是比较简单的,实现包括server端(memcached开源项目一般只单指server端)和client端两部分:. server端本质是一个in-memory key-value store,通过在内存中维护一个大的hashmap用来存储小块的任意数据,对外通过统一的简单接口(memcached protocol)来提供操作.

分布式缓存-Memcached

- - 人月神话的BLOG
分布式缓存出于如下考虑,首先是缓存本身的水平线性扩展问题,其次是缓存大并发下的本身的性能问题,再次避免缓存的单点故障问题(多副本和副本一致性). 分布式缓存的核心技术包括首先是内存本身的管理问题,包括了内存的分配,管理和回收机制. 其次是分布式管理和分布式算法,其次是缓存键值管理和路由. 原文: http://wenku.baidu.com/view/8686d46c7e21af45b307a8c3.html.

Memcached分布式实现

- - zzm
memcached 虽然称为 “ 分布式 ” 缓存服务器,但服务器端并没有 “ 分布式 ” 功能. 每个服务器都是完全独立和隔离的服务.  memcached 的分布式,则是完全由客户端程序库实现的.  这种分布式是 memcached 的最大特点. 这里多次使用了 “ 分布式 ” 这个词,但并未做详细解释.

一致性hash算法在memcached中的使用

- - CSDN博客推荐文章
  1、我们的memcache客户端(这里我看的spymemcache的源码),使用了一致性hash算法ketama进行数据存储节点的选择. 与常规的hash算法思路不同,只是对我们要存储数据的key进行hash计算,分配到不同节点存储. 一致性hash算法是对我们要存储数据的服务器进行hash计算,进而确认每个key的存储位置.

memcached+magent实现memcached集群

- - 编程语言 - ITeye博客
首先说明下memcached存在如下问题.   本身没有内置分布式功能,无法实现使用多台Memcache服务器来存储不同的数据,最大程度的使用相同的资源;无法同步数据,容易造成单点故障. (memagent代理实现集群).       在 Memcached中可以保存的item数据量是没有限制的,只要内存足够.

MemCached详解

- - CSDN博客推荐文章
首先,我们来了解一下MemCached与MemCache之间的区别:. Memcache是一个自由和开放源代码、高性能、分配的内存对象缓存系统. 用于加速动态web应用程序,减轻数据库负载. 它可以应对任意多个连接,使用非阻塞的网络IO. 由于它的工作机制是在内存中开辟一块空间,然后建立一个HashTable,Memcached自管理这 些HashTable.

Memcached调优

- - 四火的唠叨
文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》. 项目中有一个对实时响应性比较高的服务,引入了Memcached以减少延迟和减少数据库压力. 但是期间遇到了一些问题,这里记录一些调优细节. 最开始我使用的是 Memcached Java Client,但是最后放弃了,放弃原因包括:.

memcached协议

- - 开源软件 - ITeye博客
旧版: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt. 新版: https://github.com/memcached/memcached/blob/master/doc/protocol.txt.

Java使用memcached

- - 互联网 - ITeye博客
首先到 http://danga.com/memcached下载memcached的windows版本和java客户端jar包,目前最新版本是memcached-1.2.1-win32.zip和java_memcached-release_1.6.zip,分别解压后即可. 然后是安装运行memcached服务器,我们将memcached-1.2.1-win32.zip解压后,进入其目录,然后运行如下命令:c:>;memcached.exe -d install
c:>memcached.exe -l 127.0.0.1 -m 32 -d start.

Spring+memcached整合

- - 行业应用 - ITeye博客
1)  下载memcached服务端memcached-1.2.6-win32-bin.zip,地址:http:. 2)  下载java版客户端 java_memcached-release_2.6.1.zip. 3)  解压缩memcached-1.2.6-win32-bin.zip到指定目录,例如:D:\memcached-1.2.6-win32 ,.