日期:2014-05-16 浏览次数:20604 次
Memcached是高性能的分布式内存缓存服务器。它的主要目的不是基于本地缓存的,而主要用在分布式系统中。Memcached中保存的数据都存储在Memcached内置的内存存储空间中。由于数据仅存在于内存中,因此重启Memcached、重启操作系统会导致全部数据消失。Memcached是记录级的缓存,之前调研报告里提到过与MySQL、Server等页级缓存会缓存无效数据的,记录级的缓存则使内存利用率更高。
一、Memcached的内存管理方式
memcached默认情况下采用了Slab Allocator的机制分配、管理内存。在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,Slab Allocator就是为解决该问题而诞生的。
SlabAllocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。下面我会介绍内存分配过程。
Memcached将内存分成多个slab,每个slab是一次申请内存的最小单位(大小为1M不过好像可以设置),slab内部又分为若干个chunk。chunk的大小分为很多种,以80字节为初始大小(称为id-0的slab),第二类大小为第一类的f倍(f为参数可调,称这类为id-1的slab),随id递增每一类比上一类的chunk大小大f倍。所以一个chunk最大只可能为1M。每个chunk里包含了一个item结构体(key和value),用来存放缓存数据。chunk大小相同的已分配空间的slab组成一个链表,所有的slab链表都保存在一个slabclass的数组上(相当于二维链表),每一种slab在初始化时默认都会创建一个。
二、Memcached缓存记录的原理
memcached接收到一条记录时,首先会知道item的大小,然后就会在slabclass数组上去找能包含这个item的最小的chunk的slab链表(由于memcached的各链表之间chunk大小的倍数关系,可以快速定位到链表的起始位置),找到空闲的chunk缓存这条记录。当一个slab的chunk都用完,则新申请新的slab用来存item,申请的slab的大小是倍数变化的(和std::vector一样),链表大小由1变2,由2变4、8、16……如果空间已满,不能申请更多slab,则按照LRU原则,将空间分配给新的记录。
三、Memcached的内存浪费
memcached的内存主要表现有以下三处:
1、一个slab分配1M空间,而slab中所有chunks大小加起来可能到不了1M;
2、每类slab在初始化时默认都会创建一个,但是如果这类slab从未被使用过,则这1M就浪费了;
3、一个item的大小总是不大于chunk大小的,当小于时则发生内存冗余。
第一类可以通过参数调优尽量减少这种内存冗余,第二类应该也可以减少,第三类看起来就不太少避免。
四、Memcached的分布式算法
Memcached的服务器端主要是用来记录缓存和读取,而分布式算法即选择哪台服务器来寻找数据/写入内存则由客户端程序库实现。方法是用Consistent Hash。
ConsistentHashing如下所示:首先求出memcached服务器(节点)的哈希值(memcached用CRC计算哈希值),并将其配置到0-232的圆上。然后用同样的方法求出存储数据的键的哈希值,并映射到圆上。然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。