认识 Memcached

简介

Memcached是一个开源、免费、高性能的分布式对象缓存系统,通过减少对数据库的读取以提高Web应用的性能;Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用 C 写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。当某个服务器停止运行或崩溃了,所有存放在该服务器上的键/值对都将丢失。

Memcached的服务器端没有提供分布式功能,各个Memcached应用不会互相通信以共享信息。想要实现分布式通过,可以多搭建几个Memcached应用,通过算法实现此效果;

Memcached里有两个重要概念:

  • slab:为了防止内存碎片化,Memcached服务器端会预先将数据空间划分为一系列slab;举个例子,现在有一个100立方米的房间,为了合理规划这个房间放置东西,会在这个房间里放置 30 个 1 立方米的盒子、20 个 1.25 立方米的盒子、15 个 1.5 立方米的盒子…这些盒子就是slab
    • LRU:最近最少使用算法;当同一个slat的格子满了,这时需要新加一个值时,不会考虑将这个新数据放到比当前slat更大的空闲slat,而是使用LRU移除旧数据,放入这个新数据;

部署

Memcached能够在大多数 Linux类 BSD 系统上运行;官方没有给出Windows上安装Memcached的支持;

对于Debian / Ubuntu系统:

1
apt-get install memcached

对于Redhat / Fedora / CentOs系统:

1
yum install memcached

通过memcached -h查看帮助,同时也算是测试是否安装成功;
如果遇到错误,可参考官方上的FAQ

使用

服务器端

启动一个Memcached应用,常见的启动方式是这样的:
开启一个memcached应用作守护进程,TCP连接,端口号是 11211;-u参数是运行Memcached应用的用户(这个参数也只有 root用户才能使用);

1
memcached -u root -p 11211 -d -vvv

其他常见的参数也有

  1. -m <num>:分配给Memcached应用使用的内存大小,默认是 64M;
  2. -l <ip_addr>:设置能访问Memcached应用的IP(默认:所有都允许;无论内外网或者本机更换IP,有安全隐患;若设置为127.0.0.1就只能本机访问);
  3. -c <num>:设置最大运行的并发连接数,默认是 1024;
  4. -f <factor>:设置slat大小增长因子;默认是 1.25;比如说 10号slab大小是752,那么11号slab大小就是 752 * 1.25;

客户端

Memcached客户端与服务器端的通信比较简单,使用的基于文本的协议,而不是二进制协议;因此可以通过telnet进行交互;

1
telnet [host] [port]

按下Ctrl + ],并回车,即可回显;

Storage命令

set
存储数据。如果setkey已经存在,该命令可以更新该key所对应的原来的数据,也就是实现更新的作用。详细命令指南可参考菜鸟教程 - Memcached set 命令

add
只有在setkey不存在的情况下,才会存储数据;详细命令指南可参考菜鸟教程 - Memcached add 命令

replace
只有在setkey存在的情况下,才会替换数据;详细命令指南可参考菜鸟教程 - Memcached replace 命令

append
向已存在的元素值后追加数据;详细命令指南可参考菜鸟教程 - Memcached append 命令

prepend
向已存在的元素值的头部追加数据;详细命令指南可参考菜鸟教程 - Memcached prepend 命令

cas
命令用于执行一个”检查并设置”的操作。它仅在当前客户端最后一次取值后,该key 对应的值没有被其他客户端修改的情况下,才能够将值写入。检查是通过cas_token参数进行的, 这个参数是Memcach指定给已经存在的元素的一个唯一的 64 位值。详细命令指南可参考菜鸟教程 - Memcached cas 命令

Retrive命令

get
根据元素的键名获取值;详细命令指南可参考菜鸟教程 - Memcached get 命令

gets
获取带有CAS令牌的数据值;详细命令指南可参考菜鸟教程 - Memcached gets 命令

delete
删除已存在的元素;详细命令指南可参考菜鸟教程 - Memcached delete 命令

incr/decr
对于已存在的键值进行自增或自减操作;详细命令指南可参考菜鸟教程 - Memcached incr/decr 命令

Statistics命令

stats
查看memcached所有的统计信息;详细命令指南可参考菜鸟教程 - Memcached stats 命令

stats items
显示各个slabitem的数目和存储时长等其它信息;详细命令指南可参考菜鸟教程 - Memcached stats items 命令

stats slabs
显示各个slab的信息,包括chunk的大小、数目、使用情况等。详细命令指南可参考菜鸟教程 - Memcached stats slabs 命令

stats sizes
用于显示所有item的大小和个数。该信息返回两列,第一列是 item 的大小,第二列是 item 的个数。详细命令指南可参考菜鸟教程 - Memcached stats sizes 命令

flush_all
清除所有缓存数据;详细命令指南可参考菜鸟教程 - Memcached flush_all 命令

分布式算法

取余算法

根据服务器节点数的余数来进行分散,就是通过hash函数求得的Key的整数哈希值再除以服务器节点数并取余数来选择服务器。这种算法取余计算简单,分散效果好,但是缺点是如果某一台机器宕机,那么应该落在该机器的请求就无法得到正确的处理,这时需要将当掉的服务器从算法从去除,此时候会有 (N-1) / N 的服务器的缓存数据需要重新进行计算;如果新增一台机器,会有N / (N+1)的服务器的缓存数据需要进行重新计算。对于系统而言,这通常是不可接受的颠簸(因为这意味着大量缓存的失效或者数据需要转移)。

【本段内容摘自大脸猫的博客

一致性哈希

表现为一个封闭的圆环,圆环上的点分别代表0 ~ 2^32。各个memcached节点根据hash算法,分别占据圆环上的一个点,当某key进行存储操作,会针对key进行hash操作,hash后也是圆环上的一个点,那么这个key将被存储在顺时针方向的第一个节点上。

图片描述

如上图:分配不均的节点,此时key将会被存储到节点C上。

此时,我们新增节点D,如下图。受影响的部分只有节点A~节点D中间的部分,这边分数据不会再映射到节点B上,而是映射到新增节点D上。减掉一个节点同理,只影响顺时针后面一个节点。

图片描述

优点:动态的增删节点,服务器down机,影响的只是顺时针的下一个节点
缺点:当服务器进行hash后值较为接近会导致在圆环上分布不均匀,进而导致key的分布、服务器的压力不均匀。若中间某一权重较大的serverdown机,命中率下降明显;

在一致性哈希算法的基础上引入虚拟节点

图片描述

引入虚拟节点的思想,解决一致性hash算法分布不均导致负载不均的问题。一个真实节点对应若干个虚拟节点,当key被映射到虚拟节点上时,则被认为映射到虚拟节点所对应的真实节点上。

优点:引入虚拟节点的思想,每个物理节点对应圆环上若干个虚拟节点(比如200~300个),当keyhash到虚拟节点,就会存储到实际的物理节点上,有效的实现了负载均衡;

【本段内容摘自鱼我所欲也的“memcached学习 - 分布式算法”文章

工作中常见的问题

缓存雪崩现象

缓存雪崩一般是由某个缓存节点失效,导致其他节点的缓存命中率下降,缓存中缺失的数据去数据库查询,短时间内,造成数据库服务器崩溃;

重启DB,短期又被压垮,但缓存数据也多一些;DB反复多次启动多次,缓存重建完毕,DB才稳定运行;或者,是由于缓存周期性的失效,比如每 6 小时失效一次,那么每 6 小时,将有一个请求“峰值”,严重者甚至会令DB崩溃;

缓存的无底洞现象(multiget-hole)

该问题由 facebook 的工作人员提出的, facebook 在 2010 年左右,memcached节点就已经达3000 个.缓存数千 G 内容。

他们发现了一个问题,memcached 连接频率,效率下降了,于是加 memcached 节点,添加了后,发现因为连接频率导致的问题,仍然存在,并没有好转,称之为“无底洞现象”。

问题分析

以用户为例: user-133-ageuser-133-nameuser-133-height …..N 个 key,当服务器增多,133 号用户的信息,也被散落在更多的节点,所以,同样是访问个人主页,得到相同的个人信息, 节点越多,要连接的节点也越多。

对于 memcached 的连接数,并没有随着节点的增多,而降低。 于是问题出现。

multiget-hole 解决方案

把某一组key,按其共同前缀,来分布。比如 user-133-ageuser-133-nameuser-133-height 这 3 个 key,在用分布式算法求其节点时,应该以 ‘user-133’来计算,而不是以 user-133-age/name/height 来计算。

这样,3 个关于个人信息的 key,都落在同 1 个节点上,访问个人主页时,只需要连接 1 个节点。

永久数据被踢现象

网上有人反馈为”memcached 数据丢失”,明明设为永久有效,却莫名其妙的丢失了。

分析原因:

  1. 如果 slab 里的很多 chunk,已经过期,但过期后没有被 get 过, 系统不知他们已经过期。
  2. 永久数据很久没 get 了, 不活跃, 如果新增 item,则永久数据被踢了。
  3. 当然,如果那些非永久数据被 get,也会被标识为 expire,从而不会再踢掉永久数据;

解决方案:永久数据和非永久数据分开放;