memcached 之 哈希一致性 和 虚拟节点 分析
2015-06-11 16:04
761 查看
memcached 是一个简单高效的分布式内存缓存系统。
对于memcached service 来说,行为比较简单,存储key-value数据,并且memcached 相互之间并不通信,所以不能提供HA, load balance的功能。
这里的分布式实际是由客户端连接到不同的memcached,来做数据set/get 操作,这种数据分发行为完全是有客户端来做的, 客户端使用了哈希一致性和虚拟机节点来做这种数据分发。
考虑下面的场景
memcached 可以将一台或多台机器上的内存提供给多个application 使用。
如下图, 2个应用程序,3个memcached service, app1 和app2 可以使用3个service 提供的内存做cache,其操作api比较简单,get, set。
这里数据的distribution, 是由客户端app1 来做的,app 将数据以key 分发到不同的memcached。
客户端app并不记录某个数据记录在某个service 上(这样在数据量很大的时候,会出现性能的问题),
所以app 去get/set 某个数据的时候, app以key分配到不同的service上, 考虑最简答的模运算,如上场景有3个service(srvCount=3), 插入12个记录,分别hash(key) 分别为:
0,1,2,3,4,5,6,7,8,9,11,11
srvID = hash(key) % srv0 那么这六个数据的分布就是
srv0: 0,3,6,9
srv1: 1,4,7,10
srv2: 2,5,8,11
当我们增加一个memcached service (srv3)后,同样算法,就出现如下分布:
srv0: 0,4,8,
srv1: 1,5,9
srv2: 2,6,10
srv3: 3,7,11
这样发现数据分布跟之前的差距非常大,
考虑测试场景
1. app连接3个service, 插入10k个数据
2. 减少一个service, 查询插入的10k个数据,查看命中的几率
3. 增加一个service, 查询插入的10k个数据,查看命中的几率
像我们那样简答的模运算,当出现客户端app使用的service 出现增删的时候,就会出现大量数据不能命中,导致大量cache 失效, 哈希一致性算法就能增加这种增删service 时的命中率。
这里使用 memcache 的两个python 客户端库 python-memcached 和 pylibmc 来进行我们设定的场景测试
https://github.com/trumanz/pymemcached/blob/master/scale_test.py
python-memcached 只是简单取模运算, pylibmc 使用了 ketama 哈希一致性算法:结果如下
可以看出 ketama 哈希一直算法,在删除节点后,理论剩余66.67%的数据,其仍然能命中绝大部分剩余的数据,增加节点也能命中较多的数据,而python-memcached则有较大的cache失效。
1. ketama 哈希一致性算法
http://cn.last.fm/user/RJ/journal/2007/04/10/rz_libketama_-_a_consistent_hashing_algo_for_memcache_clients
哈希一致性算法,需要将server 地址做hash 运算,如下如图圆环A所示, 每个service 在圆环中占据不同的位置,数据做hash运算后在这个圆环上分配,分配到某个service的范围内是将数据分发到这个service上,(这样就在增删节点的时候server在圆圈上的位置不变从而增加了命中率)。
考虑增加节点的情况A-->B ,新加入的srv4 占据了一定范围的圆环,黄色部分原本分配到srv0的数据,现在指向了srv4, 这部分cache 就出现失效。
2. 虚拟节点
如上图,圆环A,B存在一个问题,就是数据不能均匀的分配到service上,并且当多个service 的内存大小不同时,也不能按照权重分配数据,这里引入了虚拟节点来解决这个问题,其实非常简单,就是将每个servie的地址由一个转变成更多的地址,
如srv1转变成srv1-0 和srv1-1 两种地址(也就是两个虚拟节点), 这样srv1 就能得到两个hash值,然后在圆环中占据两个位置,这样就变成了圆环C的情况,这样针对每个service 增加更多的虚拟节点就能实现按照权重的分配数据,如srv0有100M内存,srv1
有50M内存,那么
srv0可以由srv0-0,srv0-1, ..., srv0-99, 100个虚拟节点组成
srv1可以由srv1-0,srv1-1, ..., srv1-50, 50个虚拟节点组成
这样这些虚拟节点在圆环上的随机分配,一般就能保证数据按照权重分配到不同的service上。
测试代码如下,结果基本与pylibmc 一致 https://github.com/trumanz/pymemcached/blob/master/myketama.py
root@test:~/pymemcached# ./myketama.py
After set 10K in 3 server
len of srvs_points=480
[SRV_A, has 3184, SRV_B, has 3300, SRV_C, has 3516]
Test Add one Server
len of srvs_points=640
hit=7245
Test del one Server
len of srvs_points=320
hit=6484
对于memcached service 来说,行为比较简单,存储key-value数据,并且memcached 相互之间并不通信,所以不能提供HA, load balance的功能。
这里的分布式实际是由客户端连接到不同的memcached,来做数据set/get 操作,这种数据分发行为完全是有客户端来做的, 客户端使用了哈希一致性和虚拟机节点来做这种数据分发。
考虑下面的场景
memcached 可以将一台或多台机器上的内存提供给多个application 使用。
如下图, 2个应用程序,3个memcached service, app1 和app2 可以使用3个service 提供的内存做cache,其操作api比较简单,get, set。
这里数据的distribution, 是由客户端app1 来做的,app 将数据以key 分发到不同的memcached。
客户端app并不记录某个数据记录在某个service 上(这样在数据量很大的时候,会出现性能的问题),
所以app 去get/set 某个数据的时候, app以key分配到不同的service上, 考虑最简答的模运算,如上场景有3个service(srvCount=3), 插入12个记录,分别hash(key) 分别为:
0,1,2,3,4,5,6,7,8,9,11,11
srvID = hash(key) % srv0 那么这六个数据的分布就是
srv0: 0,3,6,9
srv1: 1,4,7,10
srv2: 2,5,8,11
当我们增加一个memcached service (srv3)后,同样算法,就出现如下分布:
srv0: 0,4,8,
srv1: 1,5,9
srv2: 2,6,10
srv3: 3,7,11
这样发现数据分布跟之前的差距非常大,
考虑测试场景
1. app连接3个service, 插入10k个数据
2. 减少一个service, 查询插入的10k个数据,查看命中的几率
3. 增加一个service, 查询插入的10k个数据,查看命中的几率
像我们那样简答的模运算,当出现客户端app使用的service 出现增删的时候,就会出现大量数据不能命中,导致大量cache 失效, 哈希一致性算法就能增加这种增删service 时的命中率。
这里使用 memcache 的两个python 客户端库 python-memcached 和 pylibmc 来进行我们设定的场景测试
https://github.com/trumanz/pymemcached/blob/master/scale_test.py
python-memcached 只是简单取模运算, pylibmc 使用了 ketama 哈希一致性算法:结果如下
pylibmc | python-memcached | |
删除memcached节点 | 65.370000 | 33.470000 |
增加memcached 节点 | 73.700000 | 25.620000 |
1. ketama 哈希一致性算法
http://cn.last.fm/user/RJ/journal/2007/04/10/rz_libketama_-_a_consistent_hashing_algo_for_memcache_clients
哈希一致性算法,需要将server 地址做hash 运算,如下如图圆环A所示, 每个service 在圆环中占据不同的位置,数据做hash运算后在这个圆环上分配,分配到某个service的范围内是将数据分发到这个service上,(这样就在增删节点的时候server在圆圈上的位置不变从而增加了命中率)。
考虑增加节点的情况A-->B ,新加入的srv4 占据了一定范围的圆环,黄色部分原本分配到srv0的数据,现在指向了srv4, 这部分cache 就出现失效。
2. 虚拟节点
如上图,圆环A,B存在一个问题,就是数据不能均匀的分配到service上,并且当多个service 的内存大小不同时,也不能按照权重分配数据,这里引入了虚拟节点来解决这个问题,其实非常简单,就是将每个servie的地址由一个转变成更多的地址,
如srv1转变成srv1-0 和srv1-1 两种地址(也就是两个虚拟节点), 这样srv1 就能得到两个hash值,然后在圆环中占据两个位置,这样就变成了圆环C的情况,这样针对每个service 增加更多的虚拟节点就能实现按照权重的分配数据,如srv0有100M内存,srv1
有50M内存,那么
srv0可以由srv0-0,srv0-1, ..., srv0-99, 100个虚拟节点组成
srv1可以由srv1-0,srv1-1, ..., srv1-50, 50个虚拟节点组成
这样这些虚拟节点在圆环上的随机分配,一般就能保证数据按照权重分配到不同的service上。
测试代码如下,结果基本与pylibmc 一致 https://github.com/trumanz/pymemcached/blob/master/myketama.py
#!/usr/bin/env python import sys from binascii import crc32 import hashlib def myhash(key): d = hashlib.md5(key).hexdigest() return [int(d[0:8], 16), int(d[8:16], 16), int(d[16:24], 16), int(d[24:32], 16)] return ((crc32(key) & 0xffffffff >> 16) & 0x7fff) or 1 class SRV: def __init__(self, addr): self.addr = addr self.keys = set() self.real_weight = 0 def __str__(self): return self.addr + ", has " + str(len(self.keys)) def __repr__(self): return str(self) srvs = [] srvs_points = [] def getSrv(key): l = 0 r = len(srvs_points) -1; v = myhash(key)[0] if r == 0: return k[0] if v >= srvs_points[r][0] or v < srvs_points[0][0]: return srvs_points[r][1] while (r - l) > 1: m = (l + r)/2 if v < srvs_points[m][0]: r = m else: l = m return srvs_points[l][1] def addSrv(addr): s = SRV(addr) srvs.append(s) for i in range(0,40): haddr = addr + '-' + str(i) hcode = myhash(haddr) for h in hcode: srvs_points.append((h, s)) srvs_points.sort(lambda p1, p2 : cmp(p1[0], p2[0])) def delSrv(addr): global srvs_points tmp = [x for x in srvs_points if x[1].addr != addr ] srvs_points = tmp if __name__ == '__main__': srvaddrs = ['SRV_A', 'SRV_B', 'SRV_C'] for addr in srvaddrs: addSrv(addr) for key in range(0, 10*1000): s = getSrv(str(key)) s.keys.add(key) print "After set 10K in 3 server" print "len of srvs_points=%d"%(len(srvs_points)) print srvs print "Test Add one Server" addSrv('SRV_D') print "len of srvs_points=%d"%(len(srvs_points)) hit = 0 for key in range(0, 10*1000): s = getSrv(str(key)) if key in s.keys: hit = hit+1 print " hit=%d"%(hit) print "Test del one Server" delSrv('SRV_D') delSrv('SRV_C') print "len of srvs_points=%d"%(len(srvs_points)) hit = 0 for key in range(0, 10*1000): s = getSrv(str(key)) if key in s.keys: hit = hit+1 print " hit=%d"%(hit)
root@test:~/pymemcached# ./myketama.py
After set 10K in 3 server
len of srvs_points=480
[SRV_A, has 3184, SRV_B, has 3300, SRV_C, has 3516]
Test Add one Server
len of srvs_points=640
hit=7245
Test del one Server
len of srvs_points=320
hit=6484
相关文章推荐
- 完全卸载memcached的方法(CentOS)
- 深入研究memcache 特性和限制
- MEMCACHE类使用方法及参数详解
- Memcached源码分析之三
- 利用magent搭建memcached集群
- 关于Memcached的CAS和Set方法造成Socket泄漏的问题
- memcache真实项目缓存实例
- memcached的基本命令(安装、卸载、启动、配置相关)
- memcache基本讲解
- memcached完全剖析
- Nginx+tomcat+memcached集群session共享(win7下)
- ubuntu 14.04 memcached配置
- Memcached 的工作原理
- [置顶] Spring使用memcached
- Memcached源码分析之二
- Memcached源码分析之一
- memcache
- Memcache缓存数据库
- NOSQL----memcached(临时键值存储数据库)在centos下的安装与启动
- Memcached与Redis