memcached剖析(2)

叁叁肆2018-12-18 10:48

此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


4、slab内存模型

4.1、为什么使用slab内存模型?

  在最一开始的内存分配与回收是通过malloc和free来处理的,该方式会产生内存碎片,加重内存管理器的负担,严重缓存操作影响效率。

slab模型的出现就是为了:

  • 提高缓存操作效率
  • 完全的解决内存碎片问题。

注意:

  • 第一个目的:已经实现了(因为直接定位合适的chunk会很快)
  • 第二个目的:采用slab机制依旧会产生内存碎片,或者说成是内存浪费

4.2、slab模型原理


说明:该图摘自一篇博客(图中有标记,但是看不清),但是是很久以前摘的了,忘记了。以后找到了,我会标明出处的。

memcached的内存分配就是下面这一句话:采用分组管理预分配方式。

4.2.1、分组管理

  • 分组方式:Memcached将内存空间分为一组slab,每个slab的大小固定为1M,每个slab里又包含一组chunk,同一个slab里的每个chunk大小相同。根据这些slab中的chunk的大小,将这些slab编号slab class(也就是上图中的Classes i)。
  • 存储原理:当来一个要存储的key-value对时,我们查看这个数据的大小,选择最适合的slab class中的空闲chunk放置该对象。
    • 最合适的chunk:即该chunk的大小刚刚大于等于所存储数据的大小,而比该chunk小一级的大小刚刚比所要存储的数据小。

以上这种方式会造成内存大量浪费(我认为这也是内存碎片)。

  • 减少内存浪费的方式:预估自己的缓存数据的大小,然后在启动Memcached时合理的指定参数-f(增长因子)和-n(chunk最小尺寸)来划分内存大小,根据公式chunk size = 80*f*(n-1)将内存分配为若干个slab class。

疑问:上边这个若干到底是多少?

我们可以根据f,n,以及一个slab最大为1M来确定。(例子,我不举了,自己想想)

4.2.2、预分配

  在启动Memcached时通过-m参数为Memcached分配可用内存(假设-m 1024,即分配了1G内存),但是启动的时候不会把这些内存一次全部分配出去,而是默认先分配若干个slab class(数量取决于-f与-n参数),当其中的一个slab class被用完之后,Memcached就会再次申请1M空间,产生一个该slab class。这一块儿结合缓存删除机制中的LRU算法来看。(这一块如果有误,请大神帮忙指出来)

 

5、缓存删除机制

  • memcached不会释放已分配的内存,记录超时后,其存储空间即可重复使用
  • memcached内部不会监视缓存是否过期(即memcached不会在过期监视上耗费CPU时间),在get时查看缓存的时间戳,检查缓存是否过期
  • memcached会优先使用已超时的缓存的空间,但是当所有空间都没有超时,所有内存都已经分配完了,就删除最近最少使用(LRU)的缓存,将其空间分配给新缓存(注意,假设防止一个100k的数据,而最合适的chunk是112k,假设最合适的chunk全部用完了,这时候就取剩下的内存分配112k chunk的slab,若是剩下的内存页分配完了,不会使用刚刚大于112k的144k chunk,而是会采用LRU算法删除最近最少使用的元素,其实这样的话,就会有一个可能,就是原本112k中的数据还未过期,就有可能被踢出去了,这就是"老数据被踢现象")

注意:第三条与内存分配部分的预分配结合来看。

LRU算法原理:

当某个单元被请求时,维护一个计数器,通过计数器来判断最近最少被使用的元素被踢出去。 

 

6、两种序列化协议

  • 文本协议:
    • XML、JSON
    • key的长度为256字节
  • 二进制协议:相较于文本协议
    • jdk序列化机制、protobuf
    • 不需要文本协议的解析处理,速度更快
    • 具有更长的key,理论上最大可使用65536字节长度的key
    • 出现在1.4,推荐使用

注意:对于以上两种协议,自己选择吧。

  • 二进制协议+JDK的序列化机制,那么由于JDK自己的序列化机制低效,所以在速度上未必会比使用了fastjson的文本协议更快
  • 二进制协议+protobuf,速度很快,但是使用起来不太方便
  • 文本协议+fastjson

 

7、部分API

  • add:仅当存储空间中不存在相同key的数据时才保存
  • replace:替换。即仅当存储空间中存在相同的数据时才保存
  • set:add+replace。即无论何时都保存
  • delete(key, '阻塞时间(秒)')
  • 增1、减1操作,做计数器
  • get_multi(key1, key2):一次性非同步的同时(即并发的)获取多个键,比循环调用getKIA数十倍

 

注意点:

  • 对于memcached的监视:可以采用"nagios"


免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击