Memcached 是一款老牌的、经典的高性能内存kv缓存服务器。
Free & open source, high-performance, distributed memory object caching system.
http://www.memcached.org

安装

RHEL 发行光盘自带,如果是编译安装,依赖 libevent 的支持。

# yum install -y memcached

配置与启动

# systemctl restart memcached

这样会使用默认的配置文件 /etc/sysconfig/memcached 来启动。

# cat /etc/sysconfig/memcached 
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS=""

也可以直接使用其二进制文件来启动:

# /usr/bin/memcached -p 11211 -u memcached -c 1024 -m 64 -vv
slab class   1: chunk size        96 perslab   10922
slab class   2: chunk size       120 perslab    8738
slab class   3: chunk size       152 perslab    6898
slab class   4: chunk size       192 perslab    5461
slab class   5: chunk size       240 perslab    4369
slab class   6: chunk size       304 perslab    3449
slab class   7: chunk size       384 perslab    2730
slab class   8: chunk size       480 perslab    2184
slab class   9: chunk size       600 perslab    1747
slab class  10: chunk size       752 perslab    1394
slab class  11: chunk size       944 perslab    1110
slab class  12: chunk size      1184 perslab     885
slab class  13: chunk size      1480 perslab     708
slab class  14: chunk size      1856 perslab     564
slab class  15: chunk size      2320 perslab     451

连接 memcached

# telnet 192.168.111.128 11211

常用命令

add/set/replace

语法:

<command> <key> <flags> <expiration_time> <bytes>
value
  • flags 为标志,有点儿类似 key 的下标
  • expiration_time 为失效期,单位为秒,0表示不自动过期(并不是说永不过期,可能会被新数据挤掉),也可以指定一个绝对时间点到点过期(例如限时抢购),格式同 stats 命令 的 STAT time 时间格式。
  • bytes 为缓存的长度(单位为字节)

注意:

  • 仅当缓存中不存在键时,add 命令才会向缓存中添加一个键值对。如果缓存中已经存在键,则之前的值将仍然保持相同,并且您将获得响应 NOT_STORED。
  • 仅当键已经存在时,replace 命令才会替换缓存中的键。如果缓存中不存在键,那么您将从 memcached 服务器接受到一条 NOT_STORED 响应。
  • set 命令用于向缓存添加新的键值对。如果键已经存在,则之前的值将被替换。

示例:

set weather 0 0 6
cloudy    <-- 输入值,长度要跟上面定义的6对应
STORED

get weather
VALUE weather 0 6
cloudy
END

delete weather
DELETED

incr/decr

示例:

set age 0 0 2
25
STORED
incr age 1
26
incr age 1
27
decr age 1
26

注意:incr/decr 操作是把值理解为32位无符号整数来操作的。

应用场景:点赞关注等。

stats

查看统计信息,可以利用之来计算命中率。

flush_all

清空缓存(但不清零统计信息)。

内存分配原理

  • Memcached 的内存分配方法被称为 Slab Allocation。
  • 其原理相当简单,先将分配给 Memcached 的内存分割成大小大致相当的几个大块(slab classes)。
  • 然后将各个 slab classes 分成各种尺寸的块(即 chunk,每个 slab class 中的 chunk 的大小一致)。
  • 当分配内存时,去最合适的 slab class 中申请 chunk。

    • 比如,chunk size 有 96bytes, 120bytes, 152bytes 等,如果现在需要 140bytes,则去 152bytes 的 slab class 中分配一个 chunk(该 chunk 剩余的 12bytes 无法利用)。
    • Slab Allocation 可以缓解内存碎片问题,但是不能完全避免。
    • 如果需要的 chunk 已经用完,并不会寻找更大的,而是把旧数据踢掉。
  • 注意观察上面的 chunk size 的递增,下一级是上一级的 1.25 倍,这个被称为生长因子(比例因子)。

    • 比例因子默认值是 1.25,可以通过 -f 参数来指定,但是不宜设置得过大,过大容易造成浪费。
    • 一般而言,观察缓存数据大小的变化规律(例如使用 web 界面管理工具 memadmin 查看),设置合理的生长因子。

过期数据清理机制(lary expiration)

1.当某个值过期后,并没有从内存清除。因此,stats 命令统计时,curr_item 有其信息
2.当某个新值去占用他的位置时,当成空 chuk 来占用
3.当 get 值时,如果过期,返回空,并且清空,curr_item 就减少了

即:所谓的过期,只是让用户看不到这个数据而已,并没有真正清除。这个称为 lary expiration(惰性失效),好处是节省了CPU时间和检测成本。

删除机制(LRU)与永久数据被踢现象

如果 slab class 中的 chunk 用完了,又有新的值要加入,挤掉谁?
memcached 使用 LRU 淘汰机制。其原理是,每个 chunk 通过一个计数器来判断最近谁最少被使用,谁就被T出。

注意,即使某个key是设置的永久有效也一样会被T出来!即老数据被踢现象(永久数据被踢现象)。网上有人反馈为“memcached数据丢失”:明明设为永久有效,却莫名其妙的丢失了。

memcached 中的一些参数限制

Key的长度:250字节(二进制协议支持65536个字节)
Value的限制:1M,一般都是存储一些文本,如新闻列表等等,这个值足够了
内存的限制:32位下最大设置到2G。
如果有大量数据要缓存,一般建议开启多个实例(技术上可以在不同的机器,或同台机器上的不同端口)。

键与服务器的关系

当有多台缓存服务器时,键放到哪台服务器呢?我们可以在应用代码中通过不同的算法来决定。

分布式算法之取模算法

将key转换成数字后,对N取模(N是服务器台数)。

弊端:
假设有8台服务器,运行良好,根据取模算法,各服务器中保存有不同的key,例如,某key转换为数字后为9,保存在第2台服务器中。
现有一台服务器当机,则求余的底数变成7。当去取该key时,会到第3台服务器中去取,未命中,导致缓存命中率下降。但实际情况是该key是缓存在服务器中的。

后果:
Key0%8=0,key0%7=0 hits
Key9%8=1,key9%7=2 miss

DOWN 的服务器越多,情况越糟糕。

一致性 Hash 算法

consistent_hashing.jpg
Consistent Hashing 原理如上图所示:首先求出memcached服务器(节点)的哈希值,并将其配置到0~2^32的圈(continuum)上。
然后用同样的方法求出存储数据的键的哈希值,并映射到圈上。
然后从key映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。
如果超过2^32仍然找不到服务器,就会保存到第一台memcached服务器上。

这样一来,Down 掉某个节点,只会影响该节点与前上一个节点之间的数据。这些数据会迁移至下一个节点(如此会导致下一节点压力爆增且数据分布不均匀)。

为了解决节点故障后导致的下一节点压力变大和数据不均匀问题,引入了虚拟节点的概念。

比如,有3台服务器 a,b,c 对就应的虚拟节点:

a: a1 a2 ... a64
b: b1 b2 ... b64
c: c1 c2 ... c64

hashing_virtual_node.jpg
如上图所示,红、绿、蓝分别代表三个节点,各条线就代表多个虚拟节点(即算法上的很多节点其实只有3个真实节点)。

当蓝色节点 Down 掉后,橙色广块代表的那部分数据迁移到绿色节点,紫红广块代表的那部分数据迁移到红色节点……,如此,就达到了故障节点数据均匀分布到其它节点的目的。

缓存雪消现象

一般是由某个节点失效导致其他节点的缓存命中率下降,缓存中缺失的数据去数据库查询,短时间内造成数据库服务器崩溃。
重启DB,缓存服务器中的数据也多了一些,但 DB 短期又被压跨,反复多次启动后缓存重建完毕,D8才稳定运行。

或者是由于缓存周期性的失效,比如每6小时失效一次,那么每6小时,将有一个请求峰值,严重者甚至会令DB崩溃。

解决办法:
1)把缓存时间调长使其夜里低峰时段一起生效,后面随着客户的访问会再逐渐把数据读入到缓存
2)把缓存设置为随机3到9小时的生命周期这样不同时失效,把工作分担到各个时间点上去

缓存的无底现象

facebook 的工作人员反应的,facebook 在2010年左右,memcached 节点就已经达3000个。存储数千G的缓存。
他们发现了一个问题:memcached 连接频繁,效率下降了,于是增加 memcached 节点。添加了后,发现因为连接频率导致的问题,仍然存在并没有好转,称之为“缓存无底洞现象”。

问题分析:
以用户为例;user-133-age,user-133-name,user-133-height…N个key,当服务器增多,133号用户的信息,也被散落在更多的节点。
所人同样是访问个人主页,得到相同的个人信息,节点越多,要连接的节点也越多。对于 memcached 的连接数,并没有随着节点的增多而降低,问题出现。

问题解决方案:
把某一组key按其共同前缀来分布。
比如 user-133-age,uer-133-nmme,user-133-heigt 这3个key,在用分布式算法求其节点时应以“user-133”来计算,而不是以user-133-age/nmme/height来计算。这样,3个关于个人信息的key都落在同1个节点上,访问个人主页时,只需要连接1个节点, 问题解决。

事实上,NosQL 和传统的 RDBAMS,并不是水火不容,两者在某些设计上,是可以相互参考的。对于 memcached 这种存储 key 的设计,可以参考 MySQL 中表列的设计,比如;user 表下有 age列,name 列,height 列,对应的 key 可以用 user:133:age=23,user:133:name='lisi',user:133:height=168。

-- By 许望(RHCA、OCM、VCP)
最后修改:2020 年 07 月 17 日 05 : 43 PM
如果觉得我的文章对你有用,请随意赞赏