浅谈memcached

Memcached简介

Memcached 是一个高性能的分布式内存对象缓存系统,现在很多的大型web应用程序包括Facebook LiveJournalmixi, Digg等等都在使用memcached来支持他们每天数亿级的页面访问。通过把cache层与他们的web架构集成,他们的应用程序在提高了性能的同 时,还大大降低了数据库的负载。

memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。现在已成为 mixi、 hatena、 Facebook、 Vox、LiveJournal等众多服务中提高Web应用扩展性的重要因素。 
    许多Web应用都将数据保存到RDBMS中,应用服务器从中读取数据并在浏览器中显示。 但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、 网站显示延迟等重大影响。 

这时就该memcached大显身手了。memcached是高性能的分布式内存缓存服务器。 一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、 提高可扩展性。

浅谈memcached

memcached的特征

memcached作为高速运行的分布式缓存服务器,具有以下的特点。

  • 协议简单
  • 基于libevent的事件处理
  • 内置内存存储方式
  • memcached不互相通信的分布式

协议简单

memcached的服务器客户端通信并不使用复杂的XML等格式, 而使用简单的基于文本行的协议。因此,通过telnet 也能在memcached上保存数据(set)、取得数据(get)。

基于libevent的事件处理

libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能 封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。 memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。

Memcached的工作原理

Memcached处理的原子是每一个(keyvalue)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找、对比以及做到尽可能的散列。同时,memcached用的是一个二级散列,通过一张大hash表来维护。

Memcached有两个核心组件组成:服务器端(server)和客户端(client),在一个memcached的查询中,client先通过计算keyhash值来确定kv对所处在的server位置。当server确定后,客户端就会发送一个查询请求给对应的server,让它来查找确切的数据。因为这之间没有交互以及多播协议,所以memcached交互带给网络的影响是最小化的。

举例说明:考虑以下这个场景,有三个client分别是c1c2c3,还有三个ms分别是s1s2s3

设置kv

c1想设置key=com,value=iQiyi

c1拿到server列表,并对keyhash转化,根据hash值确定kv对所存的server位置

s2被选中了

c1连接上s2s2收到请求,把(key=com,value=iQiyi”)存了起来

获取kv

c3想得到key=com”的value

c3用相同的hash算法算出hash值,并确定key=aa”的值存在s2

c3连接上s2,并从s2那边得到value=iQiyi

其他任何从c1c2c3的想得到key=com”的值的请求都会发向s2


memcached不互相通信的分布式

memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。 各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢? 这完全取决于客户端的实现。 
浅谈memcached 
添加数据:根据客户端实现的算法,根据数据的“键”来选择保存数据的memcached服务器,选定好服务器,再将数据写入memcached中。 
eg:set(gyz”,data); 
获取数据:获取时,将获取数据的键‘gyz’传递给函数库。函数库通过与数据保存时相同的算法,根据键来选择服务器。使用的算法相同就能保证与保存时相同的服务器,然后发送get命令。

这样,不同的键保存到了不同的服务器,就实现了memcached的分布式。 
服务器和具体的请求均可以使用一致性哈希来进行实现


Memcached的内存管理机制

先了解几个基本概念:

1slab class:在memcached中,对元素的管理是以slab为单元进行管理的。每个slab class对应一个或多个空间大小相同的chunk。参考下图。

           浅谈memcached

2chunk:存放元素的最小单元。用户数据itemkeyvalue等)最终会保存在chunk中。memcached会根据元素大小将其放到合适的slab class中。每一个slab class中的chunk空间大小是一样的,所以元素存放进来后,chunk可能会有部分空间剩余。参考下图。

             浅谈memcached

                    浅谈memcached

3、page:大小固定为1MB。当slab class空间不足时,就会申请page,并将page按chunk的大小进行切割。

memcached使用自己的内存管理机制,可以有效避免系统内存碎片,避免给操作系统带来负担。启动memcached时,以-m指定大小的内存将会用于数据的存放。默认情况下,这些内存会被分隔成1M的page。每个page在必要时分配给slab class,然后根据slab class里chunk的大小,将page分隔成chunk。一旦一个page被赋给一个slab class后,它将不会再被移动。因为内存空间有限,如果在slab class 3中使用了较多的page,那么在slab class 4中就只能使用较少的page。可以这么认为,memcached是一个有很多更小的相互独立的缓存系统,每一个更小的缓存系统实际上就是slab class。每个slab class都有它自己的统计信息以及自己的LRU。

在启动memcached时,指定-vv参数,可以在启动日志中查看每个slab class的chunk大小。

$ ./memcached -vv
     slab class   1: chunk size        80 perslab   13107
     slab class   2: chunk size       104 perslab   10082
     slab class   3: chunk size       136 perslab    7710
     slab class   4: chunk size       176 perslab    5957
     slab class   5: chunk size       224 perslab    4681
     slab class   6: chunk size       280 perslab    3744
     slab class   7: chunk size       352 perslab    2978
     slab class   8: chunk size       440 perslab    2383
     slab class   9: chunk size       552 perslab    1899
     slab class  10: chunk size       696 perslab    1506
     [...etc...]

在slab class 1中,每个chunk大小为80字节,每个slab class包含13107个chunk(或item)。这两个数相乘,应该最近接1个page的大小(默认是1MB)。

当存储元素时,它们将被放到最合适的slab class中。例如,50字节的元素(包含key、value等信息)将被存放到slab class 1中的chunk,根据之前的说明,此chunk中将会有30字节的空间浪费。如果存放数据总共有90字节,将被存放到slab class 2中,此时会有14字节的空间浪费。

启动时,通过设置参数-f,可以设置每个slab class下chunk的空间增长率。

另外,memcached因为一些其他功能也会使用内存空间。例如:用来查找元素的hash table;连接时也会使用一些缓存。当然,这些功能只会占用很少一部分内存空间。

如果获取了一个已失效的元素,memcached将会释放内存。后续再将新的元素存放进来时,将会重用此内存。(前提是新元素也是要存放在同一slab class中。)

由于LRU,元素将被踢出以让给新的元素,或发现有过期的元素,它们的内存将被重用。

内置内存存储方式

为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。 由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。 另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。 memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。

内存管理采用采用Slab Allocator机制分配内存、管理内存。 
基本原理:按照预先规定的大小,将分配的内存分割成特定长度的块 
(类似于空间配置器),默认内存大小为64MB,将内存切分成chunk 
memcache根据收到的数据的大小,选择最适合数据大小的块(memcached保存着空闲块的列表) 
缺点:分配的是特定长度的内存,无法有效利用分配的内存。 
解决:预先知道客户端发送数据的大小,动态调整内存分割块的大小。 
可以使用growth factor选项。 
memcached在启动时指定Growth Factor因子(-f选项),可以控制slab分割块之间的差异,可以调整分割块之间的间距。 
可以使用stats查看memcached内部状态。 
memcached在数据删除方面有效利用资源,不会释放已经分配的内存,记录超时后,客户端就无法看见该记录,其存储空间可重复使用。当内存空间不足时,从“最近最少使用”LRU搜索,并将其空间分配给新的记录。内部不会监视记录是否过期,而是在get时查看记录的时间戳。不会在过期监视上耗费CPU时间。