Free & open source, high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.
memcached是一个免费开源、高性能、分布式的内存对象缓存系统,本质上是通用的,但旨在经过加速动态Web应用程序来减轻数据库负载。php
Memcache是一款开发工具,其设计思想主要反映如下几个方面:html
1.客户端二者区别:web
2.服务器端二者区别:算法
ps:本文说明的内容是关于memcache服务器的,请不要跟客户端的叫法混淆。数据库
1. Memcached 的高性能缓存
首先从内存模型来研究memcached:C++里分配内存有两种方式,预先分配和动态分配内存,显然预先分配内存会使程序比较快,可是它的缺点是不能有效利用内存;而动态分配能够有效利用内存,可是会使程序运行效率降低,memcached的内存分配就是基于以上原理,显然为了得到更快的速度,有时候咱们不得不以空间换时间。
Memcached的高性能源于两阶段哈希(two-stage-hash)结构。Memcached就像一个巨大的、存储了不少<key, value>对的哈希表,经过key能够存储或查询任意的数据。客户端能够把数据存储在多台memcached上,当查询数据时,客户端首先参考节点列表计算出key的哈希值(阶段一哈希),进而选中一个节点;客户端将请求发送给选中的节点,而后memcached节点经过一个内部的哈希算法(阶段二哈希),查找真正的数据(item)并返回个客户端。从实现的角度看,memcached是一个非阻塞、基于事件驱动的服务器程序。
为了提升性能,memcacahed中保存的数据都存储在memcached内置的内存存储空间中。因为数据仅存在于内存中,所以重启memcached、重启操做系统会致使所有数据丢失。另外,内存容量达到指定值以后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。memcached自己是为了缓存而设计的服务器,所以并无过多考虑数据的永久性问题。服务器
2. Slab Allocator 内存分配、管理机制app
1.在该机制出现之前,内存的分配是经过对全部记录简单地进行malloc和free 来进行的。可是,这种方式会致使内存碎片,加剧操做系统内存管理器的负担,最坏的状况下,会致使操做系统比memcached 进程自己还慢。Slab Allocator 就是为解决该问题而诞生的, 它按照预先规定的大小,将分配的内存分割成特定长度的块,以完成解决内存碎片的问题。存储结构图以下:框架
Memcached的存储涉及到slab、page、chunk三个概念
chunk:固定大小的内存空间,用于缓存记录,默认为88Byte。
page:分配给Slab的内存空间,默认是1M。分配给Slab以后根据Slab的大小切成chunk。
slab:一样大小的chunk组成一类slab。分布式
2.在Slab中缓存记录的原理
memcached 根据收到的数据大小,选择最适合数据大小的Slab,memcached中保存着Slab内空闲chunk的列表,根据该列表选择chunk,而后将数据缓存其中。Slab allocator分配的内存不会释放,而是重复利用。
3.Slab Allocator 的缺点
因为分配的是特定长度的内存,所以没法有效的利用分配的内存。对与该问题没有完美的解决方案,但能够调节slab class的大小差异来减小空间浪费。
4.使用Growth Factor进行调优
memcached在启动时制定Growth Factor因子(经过f选项),就能够在某种程度上控制slab之间的差别。默认值时1.25.
memcached是缓存,不须要永久的保存到服务器上,下面介绍它的删除机制:
1.Lazy Expiration
memcached 不会释放已经分配的内存,记录过时后,客户端没法在看到这条记录,其存储空间能够再利用。memcached内部不会监视记录是否过时,而是在get时查看记录的时间戳,检查记录是否过时。这种技术被称为Lazy Expiration。所以,memcached不会在过时监视上耗费CPU时间。
2.LRU(Least Recently Used)
memcached会优先使用已超时的记录空间,但即便如此,也会发生追加新记录时空间不足的状况,此时就要使用名为LRU机制来分配空间。顾名思义,这是删除“最近最少使用”记录的机制。所以,当memcahced的内存空间不足时(没法从slab class获取新的空间时),就从最近未被使用的记录中搜索,并将其空间分配给新的记录。
1.Memcached "分布式"
特别澄清一个问题:MemCache虽然被称为"分布式缓存",可是MemCache自己彻底不具有分布式的功能,Memcache集群之间不会相互通讯(与之造成对比的,好比JBoss Cache,某台服务器有缓存数据更新时,会通知集群中其余机器更新缓存或清除缓存数据),所谓的"分布式",彻底依赖于客户端程序的实现。前面已经讲过memcached的两段哈希了,数据的保存和获取都使用相同的算法。这样将不一样的键保存到不一样的服务器上,就实现了memcached的分布式。Memcached服务器增多后,键就会分散,即便一台memcached服务器发生故障没法链接,也不会影响其余的缓存,系统依然能继续进行。
2.余数分布式算法
就是“根据服务器台数的余数进行分散”。求得键的整数哈希值,再除以服务器台数,根据其他数来选择服务器。
余数算法的缺点:余数计算的方法简单,数据的分散性也至关优秀,但也有其缺点。那就是当添加或移除服务器时,缓存重组的代价至关巨大。添加服务器后,余数就会产生巨变,这样就没法获取与保存时相同的服务器,从而影响缓存的命中率。
3.Consistent hashing(一致哈希)
首先求出memcached 服务器(节点)的哈希值,并将其配置到0~2^32-1 的圆(continuum)上。而后用一样的方法求出存储数据的键的哈希值,并映射到圆上。而后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。若是超过2^32-1仍然找不到服务器,就会保存到第一台memcached 服务器上。
从上图的状态中添加一台Memcached 服务器。余数分布式算法因为保存键的服务器会发生巨大变化,而影响缓存的命中率,但Consistent Hashing中,只有在continuum 上增长服务器的地点逆时针方向的第一台服务器上的键会受到影响。
Consistent Hashing(添加服务器):
所以,Consistent Hashing 最大限度地抑制了键的从新分布。并且,有的Consistent Hashing 的实现方法还采用了虚拟节点的思想。使用通常的hash函数的话,服务器的映射地点的分布很是不均匀。所以,使用虚拟节点的思想,为每一个物理节点(服务器)在continum上分配100~200 个点。这样就能抑制分布不均匀,最大限度地减少服务器增减时的缓存从新分布。
经过上文中介绍的使用Consistent Hashing 算法的Memcached 客户端函数库进行测试的结果是,由服务器台数(n)和增长的服务器台数(m)计算增长服务器后的命中率计算公式: (1 -m/(n+m)) * 100
1.存储及删除命令,简单易用,使用语法以下:
command <key> <flags> <expiration time> <bytes>
<value>
参数说明以下:
command set/add/replace
key key 用于查找缓存值
flags 能够包括键值对的整型参数,客户机使用它存储关于键值对的额外信息
expiration time 在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
bytes 在缓存中存储的字节数
value 存储的值(始终位于第二行)
2.读取命令
3.统计命令
1.stats指令解读
stats是一个比较重要的指令,用于列出当前MemCache服务器的状态,返回的参数反映着Memcache服务器的基本信息,他们的意思是:
参 数 名 | 做 用 |
---|---|
pid | MemCache服务器的进程id |
uptime | 服务器已经运行的秒数 |
time | 服务器当前的UNIX时间戳 |
version | MemCache版本 |
pointer_size | 当前操做系统指针大小,反映了操做系统的位数,64意味着MemCache服务器是64位的 |
rusage_user | 进程的累计用户时间 |
rusage_system | 进程的累计系统时间 |
curr_connections | 当前打开着的链接数 |
total_connections | 当服务器启动之后曾经打开过的链接数 |
connection_structures | 服务器分配的链接构造数 |
cmd_get | get命令总请求次数 |
cmd_set | set命令总请求次数 |
cmd_flush | flush_all命令总请求次数 |
get_hits | 总命中次数,重要,缓存最重要的参数就是缓存命中率,以get_hits / (get_hits + get_misses)表示,好比这个缓存命中率就是99.2% |
get_misses | 总未命中次数 |
auth_cmds | 认证命令的处理次数 |
auth_errors | 认证失败的处理次数 |
bytes_read | 总读取的字节数 |
bytes_written | 总发送的字节数 |
limit_maxbytes | 分配给MemCache的内存大小(单位为字节) |
accepting_conns | 是否已经达到链接的最大值,1表示达到,0表示未达到 |
listen_disabled_num | 统计当前服务器链接数曾经达到最大链接的次数,这个次数应该为0或者接近于0,若是这个数字不断增加, 就要当心咱们的服务了 |
threads | 当前MemCache总线程数,因为MemCache的线程是基于事件驱动机制的,所以不会一个线程对应一个用户请求 |
bytes | 当前服务器存储的items总字节数 |
current_items | 当前服务器存储的items总数量 |
total_items | 自服务器启动之后存储的items总数量 |
比较关注的点:get_hits 和 get_misses,命中率:get_hits/(get_hits + get_misses) 是衡量memcache服务器的一个重要指标。
2.stats slabs 指令解读
参 数 名 | 做 用 |
---|---|
chunk_size | 当前slab每一个chunk的大小,单位为字节 |
chunks_per_page | 每一个page能够存放的chunk数目,因为每一个page固定为1M即10241024字节,因此这个值就是(10241024/chunk_size) |
total_pages | 分配给当前slab的page总数 |
total_chunks | 当前slab最多可以存放的chunk数,这个值是total_pages*chunks_per_page |
used_chunks | 已经被分配给存储对象的chunks数目 |
free_chunks | 曾经被使用过可是由于过时而被回收的chunk数 |
free_chunks_end | 新分配但尚未被使用的chunk数,这个值不为0则说明当前slab历来没有出现过容量不够的时候 |
mem_requested | 当前slab中被请求用来存储数据的内存空间字节总数,(total_chunks*chunk_size)-mem_requested表示有多少内存在当前slab中是被闲置的,这包括未用的slab+使用的slab中浪费的内存 |
get_hits | 当前slab中命中的get请求数 |
cmd_set | 当前slab中接收的全部set命令请求数 |
delete_hits | 当前slab中命中的delete请求数 |
incr_hits | 当前slab中命中的incr请求数 |
decr_hits | 当前slab中命中的decr请求数 |
cas_hits | 当前slab中命中的cas请求数 |
cas_badval | 当前slab中命中可是更新失败的cas请求数 |
经过此命令返回的信息,能够查看slab class的分布状况,以及每一个slab中chunk使用状况;根据slab的分布,能够判断增加因子的设置的是否合理等。
3.stats items 命令解读
参数名 | 做用 |
---|---|
outofmemory | slab class为新item分配空间失败的次数。这意味着你运行时带上了-M或者移除操做失败 |
number | 存放的数据总数 |
age | 存放的数据中存放时间最久的数据已经存在的时间,以秒为单位 |
evicted | 不得不从LRU中移除未过时item的次数 |
evicted_time | 自最后一次清除过时item起所经历的秒数,即最后被移除缓存的时间,0表示当前就有被移除,用这个来判断数据被移除的最近时间 |
evicted_nonzero | 没有设置过时时间(默认30天),但不得不从LRU中清除该未过时的item的次数 |
由于memcached的内存分配策略致使一旦memcached的总内存达到了设置的最大内存,表示全部的slab可以使用的page都已经固定,这时若是还有数据放入,将致使memcached使用LRU策略剔除数据。而LRU策略不是针对全部的slabs,而是只针对新数据应该被放入的slab,例若有一个新的数据要被放入slab 3,则LRU只对slab 3进行,经过stats items就能够观察到这些剔除的状况。
注意evicted_time:并非发生了LRU就表明memcached负载过载了,由于有些时候在使用cache时会设置过时时间为0,这样缓存将被存放30天,若是内存满了还持续放入数据,而这些为过时的数据好久没有被使用,则可能被剔除。把evicted_time换算成标准时间看下是否已经达到了你能够接受的时间,例如:你认为数据被缓存了2天是你能够接受的,而最后被剔除的数据已经存放了3天以上,则能够认为这个slab的压力其实能够接受的;可是若是最后被剔除的数据只被缓存了20秒,不用考虑,这个slab已经负载太重了。
https://www.cnblogs.com/xrq73...
http://zhihuzeye.com/archives...