您的位置:首页 > 数据库 > Memcache

Memcached学习笔记

2016-05-02 22:35 369 查看

一、Memcached介绍



1.1、什么是Memcached?
—————–

free&opensource,high-performance,distributedmemory objectcachingsystem

自由&开放源码, 高性能 ,分布式的内存对象缓存系统
由 livejounal 旗下的 danga 公司开发的老牌 nosql 应用.

百度百科:

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。

Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。

![Memcached缓存图解](http://img.blog.csdn.net/20160502224115919)

1.2什么是 NoSQL?
————-

nosql,指的是非关系型的数据库。

相对于传统关系型数据库的”行与列”,NoSQL 的鲜明特点为 k-v 存储(memcached,redis), 或基于文档存储(mongodb).

> 注: nosql–notonlysql, 不仅仅是关系型数据库, 显著特点:key-value 键值对存储,如 memcached,redis, 或基于文档存储 如,mongodb

二、Memcached基本使用



2.1 linux 下编译 memcached
———————–

2.1.1:准备编译环境

libevent官网:http://libevent.org/

Memcached官网:http://memcached.org

• 安装 libevent(memcached依赖libevent)

tar zxvf libevent-2.0.1-stable.tar.gz <br>
cd libevent-2.0.21-stable <br>
./configure --prefix=/usr<br>
make # make install <br>


• 安装 Memcached

tar zxvf memcached-1.4.15.tar.gz<br>
cd memcached-1.4.15 <br>
./configure --prefix=/usr/local <br>
make<br>
make install


在 linux 编译,需要 gcc,make,cmake,autoconf,libtool 等工具.

在 linux 系统联网后,用如下命令安装

> yum install gcc make cmake autoconf libtool

2.2 memcached 的启动

memcached -h 查看帮助命令

-p <num>                        监听的TCP端口 (缺省: 11211)
-d                              以守护进程方式运行Memcached
-u <username>                   运行Memcached的账户,非root用户
-m <num>                        最大的内存使用, 单位是MB,缺省是 64 MB
-c <num>                        软连接数量, 缺省是 1024
-v                              输出警告和错误信息
-vv                             打印客户端的请求和返回信息
-h                              打印帮助信息
-i                              打印memcached和libevent的版权信息


2.3 memcached 的连接

memcached 客户端与服务器端的通信比较简单,使用的基于文本的协议,而不是二进制协议.

(http 协议也是这样), 因此我们通过 telnet 即可与 memcached 作交互.

另开一个终端,并运行 telnet 命令 (开启 memcached 的终端不要关闭)

> 格式 telnet host port

> telnet localhost 11211

> Trying ::1… Connected to localhost.

> Escape character is ‘^]’.

连接后 ctrl+] ,然后回车, 打开回显功能

输入stats 回车, 即可查看memcached运行状态.

2.4 memcached 的命令

增: add 往内存增加一行新记录

语法:addkeyflagexpire length 回车

key 给值起一个独特的名字

flag 标志,要求为一个正整数

expire 有效期 length 缓存的长度(字节为单位)

———-

flag 的意义:

memcached 基本文本协议,传输的东西,理解成字符串来存储.

想:让你存一个 php 对象,和一个 php 数组,怎么办?

答:序列化成字符串,往出取的时候,自然还要反序列化成 对象/数组/json 格式等等.

这时候,flag 的意义就体现出来了. 比如,1 就是字符串,2 反转成数组 3,反序列化对象…..

———-

expire 的意义: 设置缓存的有效期,有 3 种格式

1:设置秒数, 从设定开始数,第 n 秒后失效.

2:时间戳, 到指定的时间戳后失效. 比如在团购网站,缓存的某团到中午 12:00 失效.addkey013792099996

3: 设为 0. 不自动失效.

———-

delete 删除 deletekey [time seconds] 删除指定的 key.

如加可选参数 time,则指删除 key,并在删除 key 后的 time 秒内,不允许 get,add,replace 操作此 key.

———-

replace 替换 replacekeyflagexpire length 参数和 add 完全一样,不单独写.

———-

get 查询 get key 返回 key 的值

set 是设置和修改值 参数和 add,replace 一样,但功能不一样.

用 add 时,key 不存在,才能建立此键值.

但对于已经存在的键,可以用 replace 进行替换/更改

repalce,key 存在时,才能修改此键值,如上图,date 不存在,则没改成功.

———-

而 set 想当于有 addreplace 两者的功能. setkeyflagexpireleng 时 如果服务器无此键 —-> 增加的效果 如果服务器有此键 —-> 修改的效果.

———-

incr,decr 命令:增加/减少值的大小 语法: incr/decrkeynum

- 应用场景——秒杀功能, 一个人下单,要牵涉数据库读取,写入订单,更改库存,及事务要求, 对于传统型数据库来说, 压力是巨大的. 可以利用 memcached 的 incr/decr 功能, 在内存存储 count 库存量, 秒杀 1000 台 每人抢单主要在内存操作,速度非常快, 抢到 count

3.1:内存的碎片化
———-

如果用 c 语言直接 malloc,free 来向操作系统申请和释放内存时, 在不断的申请和释放过程中,形成了一些很小的内存片断,无法再利用. 这种空闲,但无法利用内存的现象,—称为内存的碎片化.

3.2: slaballocator 缓解内存碎片化
————————–

memcached 用 slaballocator 机制来管理内存.

slaballocator 原理: 预告把内存划分成数个 slabclass 仓库.(每个 slabclass 大小 1M)

各仓库,切分成不同尺寸的小块(chunk). 需要存内容时,判断内容的大小,为其选取合理的仓库.

如下图:
![这里写图片描述](http://img.blog.csdn.net/20160502224543437)

3.3:Slab Allocator缓存原理

memcached根据收到的数据的大小,

选择最适合数据大小的slab。memcached中保存着slab内空闲chunk的列表,

根据该列表选择chunk,然后将数据缓存于其中。



警示: 如果有 100byte 的内容要存,但 122 大小的仓库中的 chunk 满了 并不会寻找更大的,如 144 的仓库来存储, 而是把 122 仓库的旧数据踢掉! 详见过期与删除机制.

3.4 固定大小 chunk 带来的内存浪费
———————-

由于 slaballocator 机制中, 分配的 chunk 的大小是”固定”的,

因此, 对于特定的 item,可能造 成内存空间的浪费. 比如, 将 100 字节的数据缓存到 122 字节的 chunk 中, 剩余的 22 字节就浪费了.

对于 chunk 空间的浪费问题,无法彻底解决,只能缓解该问题.

开发者可以对网站中缓存中的item的长度进行统计,并制定合理的slabclass中的chunk的大小. 可惜的是,我们目前还不能自定义 chunk 的大小,但可以通过参数来调整各 slabclass 中 chunk 大小的增长速度. 即增长因子,grow factor!
![这里写图片描述](http://img.blog.csdn.net/20160502224629292)

3.5 grow factor 调优
——————

memcached 在启动时可以通过­f 选项指定 Growth Factor 因子, 并在某种程度上控制 slab 之间的差异. 默认值为1.25. 但是,在该选项出现之前,这个因子曾经固定为2,称为”powersof2” 策略。 我们分别用 grow factor 为 2 和 1.25 来看一看效果:

>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

对比可知, 当 f=2 时, 各 slab 中的 chunksize 增长很快,有些情况下就相当浪费内存. 因此,我们应细心统计缓存的大小,制定合理的增长因子.

注意: 当f=1.25时,从输出结果来看,某些相邻的slabclass的大小比值并非为1.25,可能会觉得有些 计算误差,这些误差是为了保持字节数的对齐而故意设置的.

3.6 memcached 的过期数据惰性删除

1: 当某个值过期后,并没有从内存删除,因此,stats 统计时,curr_item 有其信息

2: 当某个新值去占用他的位置时,当成空 chunk 来占用.

3: 当 get 值时,判断是否过期,如果过期,返回空,并且清空,curr_item 就减少了.

即–这个过期,只是让用户看不到这个数据而已,并没有在过期的瞬间立即从内存删除.

这个称为 lazyexpiration, 惰性失效. 好处— 节省了 cpu 时间和检测的成本.

3.7: memcached 此处用的 lru 删除机制.
—————————–

如果以122byte大小的chunk举例,122的 chunk都满了, 又有新的值(长度为 120)要加入, 要挤掉谁?

memcached 此处用的 lru 删除机制. (操作系统的内存管理,常用 fifo,lru 删除)

lru:leastrecentlyused 最近最少使用

fifo:first in,first out

原理: 当某个单元被请求时,维护一个计数器,通过计数器来判断最近谁最少被使用. 就把谁 t 出.

> 注: 即使某个 key 是设置的永久有效期,也一样会被踢出来! 即–永久数据被踢现象

四、分布式集群算法

4.1 memcached 如何实现分布式?

在第 1 章中,我们介绍 memcached 是一个”分布式缓存”,然后 memcached 并不像 mongoDB 那样,允许配置多个节点,且节点之间”自动分配数据”.

就是说–memcached 节点之间,是不互相通信的. 因此,memcached 的分布式,要靠用户去设计算法,把数据分布在多个 memcached 节点中.

4.2 分布式之取模算法

最容易想到的算法是取模算法,即 N 个节点要,从 0->N-1 编号. key 对 N 取模,余 i,则 key 落在第 i 台服务器上.

余数分布式的缺陷:



4.3 取模算法对缓存命中率的影响

假设有 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) 所以: 服务器越多, 则 down 机的后果越严重!

4.4 一致性哈希算法原理



关于一致性hash算法的详细讲解,请转到:

一致性哈希算法详解

4.5 一致性哈希对其他节点的影响

4.6 一致性哈希+虚拟节点对缓存命中率的影响

理想状态下,

1) 节点在圆环上分配分配均匀,因此承担的任务也平均,但事实上, 一般的 Hash 函数对于节 点在圆环上的映射,并不均匀.

2) 当某个节点 down 后,直接冲击下 1 个节点,对下 1 个节点冲击过大,能否把 down 节点上的 压力平均的分担到所有节点上?

完全可以—引入虚拟节点来达到目标虚拟节点即—-N 个真实节点,把每个真实节点映射成 M 个虚拟节点, 再把 M*N 个虚拟节点, 散列在圆环上. 各真实节点对应的虚拟节点相互交错分布 这样,某真实节点 down 后,则把其影响平均分担到其他所有节点上.

五、memcached 经典问题或现象

5.1 缓存雪崩现象及真实案例

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

重启 DB,短期又被压跨,但缓存数据也多一些.

DB 反复多次启动多次,缓存重建完毕,DB 才稳定运行.

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

5.2 缓存的无底洞现象 multiget-hole

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

原文见:

http://highscalability.com/blog/2009/10/26/facebooks-memcached-multiget-hole-more-machinesmore-capacit.html

具体表现为:出于效率的考虑,很多Memcached应用都已Multiget操作为主,随着访问量的增加,

系统负载捉襟见肘,

遇到此类问题,直觉通常都是通过增加服务器来提升系统性能,

但是在实际操作中却发现问题并不简单,新加的服务器好像被扔到了无底洞里一样毫无效果。

为什么会这样?让我们来模拟一下案发经过,看看到底发生了什么:

我们使用Multiget一次性获取100个键对应的数据,

系统最初只有一台Memcached服务器,随着访问量的增加,系统负载捉襟见肘,

于是我们又增加了一台Memcached服务器,数据散列到两台服务器上,

开始那100个键在两台服务器上各有50个,

问题就在这里:原本只要访问一台服务器就能获取的数据,

现在要访问两台服务器才能获取,服务器加的越多,

需要访问的服务器就越多,所以问题不会改善,甚至还会恶化。

5.2.1multiget-hole 问题分析

以用户为例:user-133-age, user-133-name,user-133-height …..

N 个 key, 当服务器增多,133 号用户的信息,也被散落在更多的节点,

所以,同样是访问个人主页,得到相同的个人信息, 节点越多,要连接的节点也越多.

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

5.2.2multiget-hole 解决方案:

把某一组 key,按其共同前缀,来分布.

比如 user-133-age,user-133-name,user-133-height 这 3 个 key,

在用分布式算法求其节点时,应该以‘user-133’来计算,而不是以 user-133-age/name/height 来计算.

这样,3 个关于个人信息的 key,都落在同 1 个节点上,访问个人主页时,只需要连接 1 个节点. 问题解决. 官方回应:http://dormando.livejournal.com/521163.html

事实上: NoSQL 和传统的 RDBMS,并不是水火不容,两者在某些设计上,是可以相互参考的.

对于 memcached,redis 这种 kv 存储,key 的设计,可以参考 MySQL 中表/列的设计. 比如:user 表下,有 age 列,name 列,身高列, 对应的 key,可以用 user:133:age =23,user:133:name=‘lisi,user:133:height =168;

5.3 永久数据被踢现象

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

其实,这要从 2 个方面来找原因: 即前面介绍的 惰性删除,与 LRU 最近最少使用记录删除. 分析:

1:如果 slab 里的很多 chunk,已经过期,但过期后没有被get过, 系统不知他们已经过期.

2:永久数据很久没get了,不活跃,如果新增item,则永久数据被踢了.

3: 当然,如果那些非永久数据被 get,也会被标识为 expire,从而不会再踢掉永久数据.

解决方案: 永久数据和非永久数据分开放
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: