当前位置: 首页 > 数据存储 > mecached 基础(一)--memcached介绍

mecached 基础(一)--memcached介绍

参考文献:
《Memcached 全面剖析.pdf》长野雅广、前坂徹著charlee 译版本1.0
《Memcached 源码剖析笔记.pdf》Xguru
《Memcached 使用手册.pdf》 陈海涛、肖成志、袁晨阳

  一致性hash算法 - consistent hashing


mecached 基础(一)--memcached介绍



 


(一)什么是memcached
Memcached是国外社区网站LiveJournal的开发团队开发的高性能分布式内存缓存服务器。

(二)哪些公司在使用memcached


(三)memcached的特点

(1)协议简单
memcached的客户端使用TCP或UDP链接与服务器通讯。
一个运行中的memcached服务器监控一些端口,客户端连接到这些端口,发送命令到服务器,读取回应。命令完成后可以关闭链接也可以缓存链接,当然重新建立链接的开销可以忽略不计。

(2)基于libevent的事件处理
libevent是一套跨平台的事件处理接口的封装,即使对服务器的连接数增加,也能发挥O(1)的性能。 
memcached使用libevent来进行网络并发连接的处理,能够保持在很大并发情况下,仍旧能够保持快速的响应能力。

(3)内置内存存储方式
为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。但是可以利用memcacheDB实现事务恢复、持久化和分布式复制等功能。
memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存,Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块(chunk),并把尺寸相同的块分成组(chunk的集合)如图所示。

而且,slab allocator还有重复使用已分配的内存的目的。也就是说,分配到的内存不会释放,而是重复利用。
分配给slab的所有内存空间叫做Page,它默认是1MB。


下面说明memcached如何针对客户端发送的数据选择slab并缓存到chunk中。
memcached根据收到的数据的大小,选择最适合数据大小的slab(如图所示)。memcached中保存着
slab内空闲chunk的列表,根据该列表选择chunk,然后将数据缓存于其中。



但是这种机制也给memcached带来了新的问题:由于分配的是特定长度的内存,因此无法有效利用分配的内存。例如,将100字节的数据缓存到128字节的chunk中,剩余的28字节就浪费了(如下图)。


目前针对此问题还没有完美的解决方案,但是memcached文档中给出一种思路:
如果预先知道客户端发送的数据的公用大小,或者仅缓存大小相同的数据的情况下,只要使用适合数据大小的组的列表,就可以减少浪费。(但是这种情况太极端-。-)
另外,我们可以通过使用Growth Facor 进行调优,在某种程度上控制slab之间的差异。因此,在使用memcached前最好能够重新计算一下数据的预期平均长度,调整growth factor ,以获得最恰当的设置。

(4)memcached不互相通信的分布式
memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息,它是通过memcached客户端来实现分布式的。实现原理如下图:

客户端程序通过一个hash算法确定每一个保存命名将会在哪台memcached进行,并使用相同的hash算法来获取保存的值。
比如说客户端使用的是最简单的通过取余的方式来确定使用哪台memcached。程序如下:

//按照KEY值,获取一个服务器ID
Int getServerId(char* key,int serverTotal){
Int c,hash = 0;
While(c = *key++){
Hash += c;
}
Return hash % serverTotal;
}
//服务器列表
Node[0] = 10.6.1.1:11211
Node[1] = 10.6.1.2:11211
Node[2] = 10.6.1.3:11211
//获取key 是test的服务器ID
Int  id = getServerId(“test”,3);
//得出的结果是1,那么对应机器是
‘node[id] = mode[1];

上面的程序中,我们为了获取key=“test”的数据的值,进行了hash计算,getServerId(“test”,3)中test是key名,3指的是有3台memcached。计算后最终获取结果为1,那么将会去1所对应的memcached中查询key=“test”的数据的值。
但是,使用这种hash算法有个致命的缺点,就是在增加新的memcached或者减少memcached时,缓存重组的代价非常大,这样在memcached数量变化的瞬间缓存效率会大幅度下降,负载会集中到数据库服务器上,有可能会发生无法提供正常服务的情况。为了解决这种问题,一些客户端的分布式使用了名为Consistent Hashing(一致性哈希)的hash算法来定位服务器。
Consistent Hashing的原理如下:
如图一所示,求出memcached服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上。

图一

如图二所示,用同样的方法求出存储数据的键的哈希值,并映射到圆上。然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台memcached服务器上。
图二

添加一台memcached  D,如图三所示,只有在B和D之间的键会受到影响。
图三

减少一台memcached,如图四所示,只有在B和A之间的键会受影响。因此,Consistent Hashing最大限度地抑制了键的重新分布。
图四
但是这样有一个问题,就是不能确保key值映射均匀,比如在图四中C服务器相较于A而言存储了过多的key,为了解决这样一个问题,出现了虚拟节点这样一个思想:为每个物理节点(服务器)在圆上分配100~200个点。这样
就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。引入“虚拟节点”后,映射关系就从 { 对象 -> 节点 } 转换到了 { 对象 -> 虚拟节点->物理memcached }对应关系如下图所示:
 




文章来源于网络

转载时请以 超链接的形式 注明:转自疯狂泰克