memcached is a high-performance,
distributed memory object caching system,
generic in nature, but originally intended for use in
speeding up dynamic web applications by alleviating database load.
You can think of it as a short-term memory for your applications.
memcached是一个高性能,分布式内存对象缓存系统,具备通用性,但本来的目的是用于为动态web程序加速,并减轻数据库的的负担.
在 win 下启动
memcached -m 64 -p 11211 -vvv
memcached已经打开,并监听11211端口,
因此,任何能满足端口通信的工具,
都可以连接memcahced
我们利用telnet来连接
telnet localhost 11211
连接后 ctrl+] ,然后回车, 打开回显功能
输入stats 回车, 即可查看memcached运行状态.
memcached 客户端与服务器端的通信比较简单,使用的基于文本的协议,而不是二进制协议. (http 协议也是这样), 因此我们通过 telnet 即可与 memcached 作交互. 另开一个终端,并运行 telnet 命令 (开启 memcached 的终端不要关闭)
# 格式 telnet host port# telnet localhost 11211Trying ::1...Connected to localhost.Escape character is '^
语法: add key flag expire length 回车
key 给值起一个独特的名
flag 标志,要求为一个正整
expire 有效期
length 缓存的长度(字节为单
flag 的意义:
memcached 基本文本协议,传输的东西,理解成字符串来存储. 想:让你存一个 php 对象,和一个 php 数组,怎么办?
答:序列化成字符串,往出取的时候,自然还要反序列化成 对象/数组/json 格式等等. 这时候, flag 的意义就体现出来了. 比如, 1 就是字符串, 2 反转成数组 3,反序列化对象…..
expire 的意义:
设置缓存的有效期,有 3 种格式
注: 有种误会,设为 0,永久有效.错误的
- 1:编译 memcached 时,指定一个最长常量,默认是 30 天. 所以,即使设为 0,30 天后也会失效.
- 2:可能等不到 30 天,就会被新数据挤出去
delete key [time seconds]
删除指定的 key. 如加可选参数 time,则指删除 key,并在删除 key 后的 time 秒内,不允许get,add,replace 操作此 key.
replace key flag expire length
get key
返回 key 的值
参数和 add ,replace 一样,但功能不一样. 如下比较:
用 add 时, key 不存在,才能建立此键值.
但对于已经存在的键,可以用replace 进行替换/更改
repalce,key 存在时,才能修改此键值,如上图,date 不存在,则没改成功.
而 set 想当于有 add replace 两者的功能.
set key flag expire leng 时
如下图的演示该图中name 是已经存在而 date 原本不存在. set 都可以成功设置
语法: incr/decr key num
set age 0 0 2
28
stored
get age
value age 0 2
28
end
incr age 1
29
incr age 2
31
decr age 1
30
decr age 2
28
把 memcached 当前的运行信息统计出来
stats
stat pid 2296 进程号
stat uptime 4237 持续运行时间
stat time 1370054990
stat version 1.2.6
stat pointer_size 32
stat curr_items 4 当前存储的键个数
stat total_items 13
stat bytes 236
stat curr_connections 3
stat total_connections 4
stat connection_structures 4
stat cmd_get 20
stat cmd_set 16
stat get_hits 13
stat get_misses 7 // 这 2 个参数 可以算出命中率
stat evictions 0
stat bytes_read 764
stat bytes_written 618
stat limit_maxbytes 67108864
stat threads 1
end
缓存有一个重要的概念: 命中率.
命中率是指:
(查询到数据的次数/查询总数)*100%
如上, 13/(13+7) = 60+% , 的命中率.
memcached 用 slab allocator 机制来管理内存. slab allocator 原理: 预告把内存划分成数个 slab class 仓库.(每个 slab class 大小 1M)
各仓库,切分成不同尺寸的小块(chunk).
需要存内容时,判断内容的大小,为其选取合理的仓库.
由于 slab allocator 机制中, 分配的 chunk 的大小是”固定”的, 因此, 对于特定的 item,可能造
成内存空间的浪费. 比如, 将 100 字节的数据缓存到 122 字节的 chunk 中, 剩余的 22 字节就浪费了
对于 chunk 空间的浪费问题,无法彻底解决,只能缓解该问题.
开发者可以对网站中缓存中的 item 的长度进行统计,并制定合理的 slab class 中的 chunk 的大小.
可惜的是,我们目前还不能自定义 chunk 的大小,但可以通过参数来调整各 slab class 中 chunk 大小的增长速度. 即增长因子, grow factor!默认1.25
memcached 在启动时可以通过f 选项指定 Growth Factor 因子, 并在某种程度上控制 slab 之间的差异. 默认值为 1.25. 但是,在该选项出现之前,这个因子曾经固定为 2,称为”powers of 2” 策略
>memcached f 2 vvv
slab class 1: chunk size 128 perslab 8192
slab class 2: chunk size 256 perslab 4096
slab class 3: chunk size 512 perslab 2048
slab class 4: chunk size 1024 perslab 1024
....
.....
slab class 10: chunk size 65536 perslab 16
slab class 11: chunk size 131072 perslab 8
slab class 12: chunk size 262144 perslab 4
slab class 13: chunk size 524288 perslab 2
可见,从 128 字节的组开始,组的大小依次增大为原来的 2 倍. 来看看 f=1.25 时的输出:
>memcached -f 1.25 -vvv
slab class 1: chunk size 88 perslab 11915
slab class 2: chunk size 112 perslab 9362
slab class 3: chunk size 144 perslab 7281
slab class 4: chunk size 184 perslab 5698
....
....
slab class 36: chunk size 250376 perslab 4
slab class 37: chunk size 312976 perslab 3
slab class 38: chunk size 391224 perslab 2
slab class 39: chunk size 489032 perslab 2
即——这个过期,只是让用户看不到这个数据而已,并没有在过期的瞬间立即从内存删除. 这个称为 lazy expiration, 惰性失效.
好处—— 节省了 cpu 时间和检测的成本
如果以 122byte 大小的 chunk 举例, 122 的 chunk 都满了, 又有新的值(长度为 120)要加入, 要
挤掉谁?
memcached 此处用的 lru 删除机制.
(操作系统的内存管理,常用 fifo,lru 删除)
原理: 当某个单元被请求时,维护一个计数器,通过计数器来判断最近谁最少被使用. 就把谁 t 出.
memcached 是一个”分布式缓存”,然后 memcached 并不像mongoDB 那样,允许配置多个节点,且节点之间”自动分配数据”.
就是说——memcached 节点之间,是不互相通信的.
因此,memcached 的分布式,要靠用户去设计算法,把数据分布在多个memcached 节点中.
假设有 8 台服务器, 运行中,突然 down 一台, 则求余的底数变成 7
key0%8==0, key0%7 ==0 hits
....
key6%8==6, key6%7== 6 hits
key7%8==7, key7%7==0 miss
key9%8==1, key9%7 == 2 miss
...
key55%8 ==7 key55%7 == 6 miss
一般地,我们从数学上归纳之:
有 N 台服务器, 变为 N-1 台,
每 N*(N-1)个数中, 只有(n-1)个单元,%N, %(N-1)得到相同的结果
所以 命中率在服务器 down 的短期内, 急剧下降至 (N-1)/(N*(N-1)) = 1/(N-1)
Consistent Hashing 原理:
首先求出memcached服务器(节点)的哈希值,并将其配置到0~2^32的圆(continuum)上。
然后用同样的方法求出存储数据的键的哈希值,并映射到圆上。
然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。
如果超过2^32仍然找不到服务器,就会保存到第一台memcached服务器上。
通俗理解一致性哈希:
把各服务器节点映射放在钟表的各个时刻上, 把 key 也映射到钟表的某个时刻上.
该 key 沿钟表顺时针走,碰到的第 1 个节点即为该 key 的存储节点。
当某个节点 down 后,只影响该节点顺时针之后的 1 个节点,而其他节点不受影响.因此,Consistent Hashing 最大限度地抑制了键的重新分布
由图 5.5 中可以看到,理想状态下,
完全可以——引入虚拟节点来达到目标
虚拟节点即——N 个真实节点,把每个真实节点映射成 M 个虚拟节点, 再把 M*N 个虚拟节点, 散列在圆环上. 各真实节点对应的虚拟节点相互交错分布
这样,某真实节点 down 后,则把其影响平均分担到其他所有节点上.
缓存雪崩一般是由某个缓存节点失效,导致其他节点的缓存命中率下降, 缓存中缺失的数据去数据库查询.短时间内,造成数据库服务器崩溃.
重启 DB,短期又被压跨,但缓存数据也多一些.
DB 反复多次启动多次,缓存重建完毕,DB 才稳定运行.
或者,是由于缓存周期性的失效,比如每 6 小时失效一次,那么每 6 小时,将有一个请求”峰值”, 严重者甚至会令 DB 崩溃.
官方回应:http://dormando.livejournal.com/521163.html
请求多台服务器并不是问题的症结,真正的原因在于客户端在请求多台服务器时是并行的还是串行的!
问题是很多客户端,包括Libmemcached在内,在处理Multiget多服务器请求时,使用的是串行的方式!
也就是说,先请求一台服务器,然后等待响应结果,接着请求另一台,结果导致客户端操作时间累加,请求堆积,性能下降。
如何解决这个棘手的问题呢?
只要保证Multiget中的键只出现在一台服务器上即可!
比如说用户名字(user:foo:name),用户年龄(user:foo:age)等数据在散列到多台服务器上时,不应按照完整的键名(user:foo:name和user:foo:age)来散列的,而应按照特殊的键(foo)来散列的,这样就保证了相关的键只出现在一台服务器上。
以PHP的 Memcached客户端为例,有getMultiByKey和setMultiByKey可供使用。
网上有人反馈为”memcached数据丢失”,明明设为永久有效,却莫名其妙的丢失了.
其实,这要从2个方面来找原因:
即前面介绍的 懒删除,与 LRU 最近最少使用记录删除.
提示: