总体介绍

相比于redis来说memcached并没有什么难度,而且就目前的行业要求来说memcached的绝大使用场景都可以使用redis代替,对memcached有个大概的了解就行。
基础知识
你需要了解什么是memcached,什么情况下会用到他,和redis进行对比,他有什么特点,接下来就是来安装memcached,首先讲单机,然后就是集群。
客户端使用
对memcached对一定了解,也安装好了后,接下来就是客户端的使用,你需要了解这玩意,这也是整个课程的重点
在这部分会讲3个客户端memcached-java-client是现在用得比较多得,但另外两个客户端spymemcach,XMemcached也可能会用到,所以还是需要具备一定的了解
在讲客户端的时候,重点还是结合spring cache来讲解,这恰恰也是工作中用得比较多的。
原理
原理这块不需要重点理解,工作中用memcached这些的机会并不多,但并不保证一定不会问道,所以对一些原理还是要了解一些。
我们会讲解一致性hash,内存管理策略,缓存过期策略,重点是内存管理策略,这玩意如果是问memcached还真是可能被问道,因为针对memcached的调优重点就是调这个,调一个叫增长因子的玩意。

基础知识

1.什么是Memcached


Memcached:是一个免费开源的、高性能的、具有分布式内存对象的缓存系统,它通过减轻数据库负载加速动态Web应用;

Memcached特性:
l 本质上就是一个内存key-value缓存;
l 协议简单,使用的是基于文本行的协议;
l 不支持数据的持久化,服务器关闭之后数据全部丢失;
l Memcached简洁而强大,便于快速开发,上手较为容易;
l 互不通信的Memcached之间具有分布特征;(每个机器彼此间是不进行通信的,意思是假如集群有三台机器,三台机器彼此间数据是不一样的,如果A机器挂了,B机器是无法获取A机器原来存放的数据的,不支持高可用,不支持读写分离)
l 没有安全机制


这些特性其实就把memcached的有点缺全部说到了
本质上是一个内存的key-value缓存这个没什么好说的,这是nosql数据库的标准特点

适合和不适合的场景

合适
memcached适合变化频繁,查询频繁,重点是不是要入库的场景,为啥?它没法持久化
还又就是变化不频繁,但查询频繁的数据,最后就是读多谢少的场景,尤其是电商场景中用于页面数据的缓存,这是memcached适合的场景
不合适
pv值不高,不考虑使用,什么叫pv? 就是page view页面展示次数,如果一些页面都没什么访问量就不要考虑memcached了。

变化频繁且需要入库,这个就直接不要考虑memcached了,为啥?它没法持久化呗。

再就是过大的数据不适合放在memcached中,因为memcached是基于内存的,一旦断电数据就丢失了.

2.Redis和Memcached对比


Memcached[笔记] - 图1
首先来看下线程模型,Redis是单进程单线程的模式,而memcached是单进程多线程,这肯定memcached性能比redis要好那么一点,这样的差距反应在QPS/TPS的比较中,不过这个差别也不是特别的大。

结论:
memcached除了性能比Redis好之外其它的基本redis超过memcached一大截.

集群比较

当然现在还讲到安装,更没讲到memcached集群,不过同学们可以先来看下,首先回顾下redis集群,不管是redis中的哨兵还是cluster集群,服务与服务直接是可以有数据的同步的,master的节点数据会通过slaveof的配置参数进行同步,如下图所示。


Memcached[笔记] - 图2

再来看下memcached,memcached肯定也是可以支持集群的,但他的集群仅仅体现再数据的分库中,memcached内存有限制是吧?我用多个memcached来存不就可以了。memcached节点之间是不会进行任何通信的,更别说什么master与slave机制了,他本身也不支持持久化,服务重启数据就丢了,所以高可用什么的想都不要想,他不支持,如下图所示。
Memcached[笔记] - 图3

数据库流行度排行

Memcached[笔记] - 图4
这个图是2018年的最新数据,关系型数据库就不说了,重点就来看下redis和memcached这两个nosql所在得位置,redis还不错。比较汗颜得就是memcached了。我记得2017年我就看过这数据库他排24名,现在2019年了,依然不温不火,排在24名,有点小尴尬!

3.Memcached安装

准备工作

在linux根目录创建soft文件夹(根据自己习惯)
mkdir /soft

在soft目录里面上传课件里面的两文件
libevent-2.1.8-stable.tar.gz
memcached-1.5.12.tar.gz

安装libevent

安装Memcached应该先安装好libevent依赖,在soft目录里面执行命令

tar -xzvf libevent-2.1.8-stable.tar.gz

进入libevent的目录,开始安装libevent
./configure -prefix=/soft/libevent
make
make install

通过上述命令,libevent已经安装到/soft/libevent目录了
Memcached[笔记] - 图5

安装Memcached

在soft目录解压上传的memcached
tar -zxvf memcached-1.5.12.tar.gz


注意:编译的时候需要指定动态链接库,需要linux把libevent/lib目录加载进来,下面的步骤特别重要

vi /etc/ld.so.conf
在ld.so.conf目录中增加libevent/lib所在目录,wq保存退出

增加(请根据libevent实际安装目录设置)
/soft/libevent/lib
Memcached[笔记] - 图6

执行下面命令让上面的修改生效
ldconfig

下面可以正式安装memcached,需要指定libevent的安装位置
./configure -prefix=/soft/memcached —with-libevent=/soft/libevent

make
make install

测试

进入memcached的安装目录下的bin目录
执行:
./memcached -h
Memcached[笔记] - 图7
发现有如上界面说明memcached 已经安装成功

4.memcached启动

memcached -m 16 -p 11211 -d -c 1024 -u root

-d 选项是启动一个守护进程,
-m 是分配给Memcache使用的内存数量,单位是MB,这里是1024MB,默认是64MB
-u 是运行Memcache的用户,这里是root
-l 是监听的服务器IP地址,默认应该是本机
-p 是设置Memcache监听的端口,默认是11211,最好是1024以上的端口
-c 选项是最大运行的并发连接数,默认是1024,这里设置了1024,按照你服务器的负载量来设定
-P 是设置保存Memcache的pid文件位置
-h 打印帮助信息
-v 输出警告和错误信息
-vv 打印客户端的请求和返回信息

ps -ef | grep memcached 查看
Memcached[笔记] - 图8

5.集群启动

memcached 可以安装到多台机器上,安装方式与上面一样。这里就不分别安装了,就在当前的节点上启动多个memcached 实例。


memcached -m 16 -p 11212 -d -c 1024 -u root

memcached -m 16 -p 11213 -d -c 1024 -u root

通过上面两条命令,实际又启动了2个memcached实例,加上上面启动的,总共启动了3个memcached实例

ps -ef | grep memcached 查看

Memcached[笔记] - 图9

6.停止memcached

Kill -9 端口号



(二)客户端使用

1.telnet 命令行

连接上memcached

telnet 127.0.0.1 11211

添加数据

语法: add key flags exptime bytes [noreply]
Value

key:键值 key-value 结构中的 key,用于查找缓存值。
flags:可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。
exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
bytes:在缓存中存储的字节数
noreply(可选): 该参数告知服务器不需要返回数据
value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value)

范例:
add k1 0 0 3
abc

注意:如果有同KEY数据,添加会失败

获取数据

语法: get key

范例:
get k1

设置更新

语法: set key flags exptime bytes [noreply]
Value

范例:
set k1 0 0 3
abc

注意:和add有所区别,如果没有这key,会添加数据,如果有同KEY数据,会修改数据

替换

语法: replace key flags exptime bytes [noreply]
Value

范例:
replace k1 0 0 3
abc

注意:replace 才是正儿八经的修改,如果k1存在修改成功,如果k1不存在,修改失败

前追加

语法: prepend flags exptime bytes [noreply]
value

范例:
prepend k1 0 0 3
abc

后追加

语法: append key flags exptime bytes [noreply]
value

范例:
append k1 0 0 3
abc

递增

语法: incr key value

范例:
add k2 0 0 1
1

incr k2 2

注意:递增的KEY必须为整数类型

递减

语法: decr key value

范例:
decr k2 2
注意:最多减到0,最小值为0,不会产生负数,在memcached没有负数的概念

删除

语法:delete key
范例:delete k2

清空数据

语法:flush_all

CAS相关命令

轻量级的锁机制CAS机制
解释:Check and set ,即保存之前进行版本检查,memcache 1.2.4新增的特性。
gets: 获取item,并获取版本号
cas:更新item,并上传获取item时的版本号,版本号与服务器一致才能更新成功
CAS 命令的基本语法格式如下:
cas key flags exptime bytes unique_cas_token [noreply]
Value

参数说明:
key:键值 key-value 结构中的 key,用于查找缓存值。
flags:可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。
exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
bytes:在缓存中存储的字节数
unique_cas_token通过 gets 命令获取的一个唯一的64位值。
noreply(可选): 该参数告知服务器不需要返回数据
value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value)

输出信息:
STORED:保存成功后输出。
ERROR:保存出错或语法错误。
EXISTS:在最后一次取值后另外一个用户也在更新该数据。
NOT_FOUND:Memcached 服务上不存在该键值


2.JAVA客户端

Memcached[笔记] - 图10

具体内容:见代码

(三)原理

1.hash算法

前面讲过memcached是单节点是有存储局限的,所以用到了多节点,多集群的memcached.这里就有一个分布式存取与分布式读取的问题。

必然,不管是写入还是读取,肯定要通过某种算法,这种算法负责定位到某个固定的节点,
Memcached[笔记] - 图11

同学们学redis或者nginx的时候都应该或多或少的接触过各种负载均衡的算法。

余数分散(普通hash)

Memcached[笔记] - 图12

整个过程可以看出公式
公式:m = hash(o) mod n
Memcached[笔记] - 图13

一致性hash算法

Memcached[笔记] - 图14

一致性hash是为了解决普通hash的问题,使用一个环状结构,具体可以分层4步骤。


①步骤
1)构建环形hash空间
Memcached[笔记] - 图15
说明:
一致性hash算法通过一个叫作一致性hash环的数据结构实现

这个环的起点是0,终点是2^32 - 1,并且起点与终点连接,环的中间的整数按逆时针分布

这个环的整数分布范围是[0, 2^32-1]
2)把对象映射到hash空间

假设现在我们有4个对象,分别为o1,o2,o3,o4,
使用hash函数计算这4个对象的hash值(范围为0 ~ 2^32-1)


Memcached[笔记] - 图16

3)把cache节点映射到hash空间
使用同样的hash函数,我们将机器也放置到hash环上。
假设我们有三台缓存机器,分别为 c1,c2,c3
使用hash函数计算这3台机器的hash值
Memcached[笔记] - 图17
4)对象映射到cache节点
将对象和机器都放置到同一个hash环后
在hash环上顺时针查找距离这个对象的hash值最近的机器,即是这个对象所属的机器。
Memcached[笔记] - 图18

②增加一个节点的情况
Memcached[笔记] - 图19
增加机器c4的部署并将机器c4加入到hash环的机器c3与c2之间。
只有对象o4被重新分配到了c4
其他对象仍在原有机器上


③删除一个节点的情况
Memcached[笔记] - 图20
将机器c1下线(当然,也有可能是机器c1宕机)
只有对象o2被重新分配到机器c3
其他对象仍在原有机器上

④虚拟节点
仅仅使用真实的节点来形成环的结构可能会导致数据的倾斜,为了解决这种数据不对称的现象,可以在原理缓存的机器的基础上,增加相应的虚拟节点,这样数据就会均匀的打散到其他节点中。

Memcached[笔记] - 图21
这个时候客户端进行数据存取的时候找的就是虚拟节点,而不是真实的物理节点服务器。

Memcached[笔记] - 图22

这样增加节点会删除节点虽然命中率会降低,却很好的解决了数据倾斜问题。

2.内存管理策略

MC采用Slab Allocator (Slab分配器)进行内存管理

Memcached[笔记] - 图23
l 内存被拆分为多个SlabClass
l 每个Slab Class中有多个Slab 每个Slab=1M
l 每个Slab下有多个大小相等的Chunk
l 不同Slab中的Chunk大小不等
l 数据存储在Chunk中

演示:

①服务端启动
memcached -p 11211 -d -u root -vv
其中-vv显示memcached的内存分配情况,如下图所示
Memcached[笔记] - 图24

这里会显示memcached内存会分配的情况。
每一行的显示如下
slab class 1: chunk size 96 perslab 10922
其中chunk size 为96,总共有10922个块

96*10922=1048512/1024/1024 = 0.9999 M

②客户端测试
使用telnet 连接上服务端
telnet 127.0.0.1 11211

查看内存分布情况
stats slabs

Memcached[笔记] - 图25

可以看到激活的slabs的数目为0,可以分配的内存也是0

新增一个数据
add k1 0 0 10
1234567890

在使用stats查看内存分布情况
Memcached[笔记] - 图26

再新增一个数据
add k2 0 0 10
1234567890

Memcached[笔记] - 图27
这个时候发现已经用了2个块。
新增一个数据
add k3 0 0 100
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890

Memcached[笔记] - 图28

增长因子

启动时指定增长(growth factor)因子,就可以在某种程度上控制slab之间的差异,默认值为1.25 命令
memcached -p 11211 -f 2 -d -u root -vv
Memcached[笔记] - 图29

这个时候每一个slab的chunk size 都比上一个slab大了一倍。

注意:真实项目中,要均衡数据的大小,如果数据之间大小差别不大建议增长因子设置比较小,而数据之间大小悬殊,数据的增长因子可以考虑增大。

3.缓存过期策略

MC不会主动删除过期数据,而是采用lazy策略
n Memcached不会释放已分配的内存,其存储空间可以重复使用
n Memcached内部不会监视数据是否过期,而是在get时查看数据的时间戳,查看数据是否过期。被称为lazy expiration(惰性过期)
n Memcached内存空间不足,即无法从slab class中获取到新的空间时,就从最近未被使用的数据中搜索,将其空间分配给新的数据。(如果要禁用LRU,使用-M参数,超出会报错)