1.memcached 基础知识
/usr/bin/memcached -u memcached -p 11211 -m 64 -c 1024
1.启动参数
-d : 以守护进程启动
-u root : 运行memcache的用户
-P : 保存memcache进程的pid文件
-m 1024 : 数据内存数量,不包含memcache本身,单位为MB
-M : 内存不够时,禁用 LRU,报错
-n 48 : 初始 chunk = key + suffix + value + 32 结构体,默认48字节
-f 1.25 : 增长因子,默认1.25
-L : 启用大内存页,可以减低内存浪费,改进性能
-l 127.0.0.1 : 监听的ip地址
-p 11211 : TCP 端口,默认11211
-U 11211 : UDP 端口,默认11211,0为关闭
-c 1024 : 最大同时连接数
-t 4 : 线程数。memcache 采用NIO,并非线程数越大越好,一般线程数和CPU核数一致
-R 20 : 每个 event 连接最大并发数,默认20
-C : 禁用 CAS 命令
2.memcached 常用命令
command <key> <flags> <expiration time> <bytes> <value>
说明:
1.command : 操作命令,常用 set/add/replace/get/delete
2.key : 缓存数据的key, memcached 内部限制不能大于250个字符,不包括空格和控制字符。在memcached.h中定义key的长度:
#define KEY_MAX_LENGTH 250。key 的长度会影响hash查找value的效率以及占用的内存空间。
3.flags : 客户端用来标识数据格式,如JSON,XML,是否压缩等,即数据序列化格式。在memcached里面存储数据为byte,比如get操作。
客户端读取到byte格式的数据,如何进行序列化为对象。
4.expitime time : 存活时间,单位为s,0为不过期。memcached有丰富的数据过期策略,如果设置了时间,memcached会将过期的数据移除。
5.bytes : memcached 中存储的字节数,比如value为1234,则对应的bytes便为4,设置该值的主要目的:TCP协议是stream机制,告诉数据
接收方完整数据包的位置。
6.value : 存储的值。
set qiang 0 0 4 good : qiang 代表key;第一个0代表flag,这里描述为不做序列化转换;第二个0代表过期时间不失效;4代表valud的
长度;good代表设置到memcached的value。
SET : 如论如何都进行存储,如果key已经存在,则更新。执行成功,则返回 STORED
GET : 查看已经添加的数据
ADD : 只有数据不存在时进行添加
REPLACE : 只有数据存在时,进行替换
APPEND : 往后追加
PREPEND : 往前追加
3.memcached特征
1.协议简单
memcached 和客户端通信并不使用复杂的xml等协议,而是使用简单的基于文件协议或者二进制协议。
2.基于 libevent 的事件处理
由于 epoll,kqueue,/dev/poll 每个接口都有自己的特点,程序移植比较困难,libevent这个程序库就应用而生。他将Linux的epoll,BSD类
操作系统kqueue等事件处理功能封装成统一的接口。memcached使用libevent库,因此能在Linux,BSD,Solaris等操作系统上发挥其性能。
3.内置内存存储方式
为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在内存中,因此重启memcached或者重启操作系统
会导致全部数据消失。另外,内存容量达到指定的值后,memcached会自动删除不使用的内存。缓存数据的回收采用LRU。
4.memcached 客户端分布式
memcached尽管是分布式缓存服务器,但服务器端并没有实现分布式功能。各个memcached实例是不会互相通信以共享信息。它的分布式主要是通过客户端
实现的。memcached客户端会通过一些路由算法选择具体定向到哪一台memcached缓存服务器。也就是说分布式能力是在客户端代码中实现的。
4.memcached的一些问题
1.无法备份,重启无法恢复 : 只能通过持久化解决,如兼容memcached协议的 MemcacheDB...
2.无法查询 : 比如不能按范围查询等
3.没有提供内置的安全机制
4.单点故障
memcached 不支持任何的 fail-over/high-availability 机制,因为它是作为cache使用的,不是原始数据源,这也是其特点。面对单点故障,可以采用
主从模式解决。
应用服务器A通过客户端hash,进行双写操作,同时更新MC1(master),MC11(slave)数据。而读数据的时候,先获取master数据,当master返回空,或者
无法获取到数据的时候,访问slave。
在这种模式下,涉及到master,slave的2份数据的一致性,则统一以master为准。即如果有更新数据操作,需要从master中获取数据,再对master进行CAS更新,
更新成功后,才更新slave。如果cas多次失败后,则对master,slave进行delete操作,后续让请求穿透,从数据库中获取数据再回写到缓存.
5.memcached 内部存储
memcached 默认情况下采用了 slab allocation 的机制分配,管理内存。
1.slab allocation
在 slab allocation 机制出现以前,内存的分配是通过对所有记录简单的进行malloc和free来进行的。但是,这种方式会导致内存碎片,加重操作系统内存管理
器的负担。其实 slab allocation 的原理十分简单,按照预先规定的大小,将分配的内存分隔成各种尺寸的块(chunk),并把尺寸相同的块分成组(chunk的集合),
注意,分配的块可以重复利用,不释放到内存中。
Page: 分配给 slab的内存空间,默认是1MB
Chunk: 用于缓存记录的内存空间
Slab Class: 特定大小的chunk的组
chunk 是如何从88bytes 增长到112bytes呢。这其实是由 growth factor 决定的,growth factor默认值是1.25。比如最小的chunk为88,88*1.15=112。
对于确定的slab,一个Page可以存储几个。把page(默认1MB)分给112bytes的slab,则可以得到9个slab(1024/112)。
slab allocator 机制简单来说,就是memcached根据收到的数据大小,选择最合适数据大小的slab。memcached 中保存着slab内空闲的chunk列表,根据该列表
选择chunk,然后将数据缓存于其中。slab allocator 解决了当初内存碎片的问题,但新的机制也给memcached带来了新的问题。这个问题就是,由于分配的特定长度
的内存,因此无法有效利用分配的内存。
memcached 在启动的时候指定growth factor 因子(通过 -f 选项),就可以在某种程度上控制slab之间的差异,默认值是1.25。
memcached -vv -u memcached -f 2
Item:
item 是我们要保持的数据。例如:set name 0 30 5 qiang
代表我们要把一个key为name,value为qiang的键值对保持在内存中30m,memcached会把这些数据打包成一个item,每一个完整的item都包含以下几部分:
1.key:键
2.nkey:键长
3.flags:用户定义的flag
4.nbytes:值长(包括换行符\r\n)
5.suffix:后缀buffer
6.nsuffix:后缀长
一个完整的item长度是 '键长+值长+后缀长+item结构大小(32字节)',item操作就是根据这个长度来计算slab的classid的。
6.典型问题分析
单实例memcached有以下问题:
1.容量问题
2.服务高可用
3.扩展问题
1.过期机制
memcached 有2种过期策略,一种是 Lazy Expiration,另外一种是LRU算法。
1.Lazy Expiration : memcached 内部不会监视记录是否过期,而是在get时查看记录的时间戳,检查记录是否过期。这种技术被称为lazy(惰性)expiration。
因此,memcached不会在过期监视上耗费cpu时间
2.LRU算法:最近最少使用算法。
memcached 启动时,通过 '-M' 参数可以禁止LRU。
memcached -M -m 1024
指定-M后,内存用尽时,memcached会返回错误。这里的LRU是在slab范围内,而不是全局的。
2.哈希算法
哈希(Hash)就是:任意长度的输入,通过散列算法,变换成固定长度的输出。为了解决单实例的瓶颈,需要memcached进行分布式部署。使用一致性hash可以很好的
解决动态变化环境下使用memcached扩容带来的大量数据失效问题。
3.热点问题
热点数据就是hot数据,可以进一步分为热点数据和巨热数据。
数据访问热点(读热点问题),提供的一个通用解决思路,就是在cache的client端做本地LocalCache,当发现热点数据时直接在cache在client里,而不要请求到
cache的server。
巨热数据的处理方式,在facebook里有一招,就是通过多个key_index(key:xxx#N),来获取热点key的数据,其实质也是把key分散。对于非高一致要求的高并发
读还是蛮有效的。
解决之道:把巨热数据的key分布到所有服务器上,每个服务器给对应的key一个别名,如: get key:xxx => get key:xxx#N
4.缓存与数据库更新问题
如果更新数据库成功,但是缓存更新失败,结果是缓存中依旧存在旧的数据。
5.别把缓存当存储
6.命名空间
memcached原生不支持命名空间,自己通过key前缀来区分。
7.CSA
cas主要解决原子操作问题。memcached的CAS(check-and-Set)机制便是解决该问题:
1.实现了Check-and-Set 原子操作功能
2.首先使用 gets 指令获取 key-value 及key对应value的版本号version;然后操作产生新的value值;最后使用cas指令重新提交key-value,并附带之前的
版本号version。当服务判断cas操作中的版本号不是最新的时候,则认为该key的值已经被修改,本次cas操作失败。
7.memcached 客户端分析
客户端主要功能包含:memcached协议的封装,连接池的实现,sharding机制,故障转移,序列化等机制。
路由机制:
1.平衡性
key尽可能均匀的分布到所有的节点上。
2.单调性
如果已经有一些内容通过哈希分配到后端缓存服务中,又有新的缓存服务加入到集群中。哈希的结果应该能够保证已分配的内容可以被映射到原有的或者新的
缓存服务中。取模路由策略显然不符合。