Memcached内部原理

内容都是从网上搜集的,不知道是不是完全正确的,自己也没有研究过memcached的服务端的代码。

Memcached机制 

•守护进程机制 

–UNIX daemon

•Socket事件处理机制 

–non-blocked:非阻塞 

–libevent:异步事件处理 

–epoll/kqueue 

•内存管理机制 

–slab:内存分配机制 

–LRU:对象清除机制 

–Hash机制:快速检索item 

•多线程处理机制:pthread(POSIX)线程模式 

–编译时开启:./configure –enable-threads 

–目前还比较粗糙,锁机制locking不够完善 

–负载过重时,可以开启(-t线程数为CPU核数) 

•SLAB内存处理机制 

–提前分配大内存slab 1MB(因为page默认为1MB),再进行小对象填充chunk 

–避免大量重复的初始化和清理à减轻内存管理器负担

 –避免频繁malloc/freeà系统碎片 

•懒惰检测机制 

–不检测item对象是否超时 

–get时检查item对象是否应该删除 

•懒惰删除机制 

–删除item对象时,不释放内存,作删除标记,指针放入slot回收插槽,下次分配的时候直接使用

内存模型

从http://yaowhat.com/2014/10/02/memcached-memory-manage.html 抄来的


Item

指实际存放到memcached中的数据对象结构,除key-value数据外,还包括memcached自身对数据对象的描述信息(Item=key+value+16byte后缀长+32byte结构体)

Page为内存分配的最小单位

Memcached的内存分配以page为单位,默认情况下一个page是1M,可以通过-I参数在启动时指定。如果需要申请内存时,memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配(page ressign已经从1.2.8版移除了)

Slabs划分数据空间

Memcached并不是将所有大小的数据都放在一起的,而是预先将数据空间划分为一系列slabs,每个slab只负责一定范围内的数据存储。如下图,每个slab只存储大于其上一个slab的size并小于或者等于自己最大size的数据。例如:slab 3只存储大小介于137 到 224 bytes的数据。如果一个数据大小为230byte将被分配到slab 4中。从下图可以看出,每个slab负责的空间其实是不等的,memcached默认情况下下一个slab的最大值为前一个的1.25倍,这个可以通过修改-f参数来修改增长比例。

Chunk才是存放缓存数据的单位

Chunk是一系列固定的内存空间,这个大小就是管理它的slab的最大存放大小。例如:slab 1的所有chunk都是104byte,而slab 4的所有chunk都是280byte。chunk是memcached实际存放缓存数据的地方,因为chunk的大小固定为slab能够存放的最大值,所以所有分配给当前slab的数据都可以被chunk存下。如果时间的数据大小小于chunk的大小,空余的空间将会被闲置,这个是为了防止内存碎片而设计的。例如下图,chunk size是224byte,而存储的数据只有200byte,剩下的24byte将被闲置。

Slab的内存分配

Memcached在启动时通过-m指定最大使用内存,但是这个不会一启动就占用,是随着需要逐步分配给各slab的。 如果一个新的缓存数据要被存放,memcached首先选择一个合适的slab,然后查看该slab是否还有空闲的chunk,如果有则直接存放进去;如果没有则要进行申请。slab申请内存时以page为单位,所以在放入第一个数据,无论大小为多少,都会有1M大小的page被分配给该slab。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,在从这个chunk数组中选择一个用于存储数据。如下图,slab 1和slab 2都分配了一个page,并按各自的大小切分成chunk数组。


Memcached内存分配策略

按slab需求分配page,各slab按需使用chunk存储。 

这里有几个特点要注意

Memcached分配出去的page不会被回收或者重新分配 

Memcached申请的内存不会被释放 slab

空闲的chunk不会借给任何其他slab使用[新版本中Page可以调配给其它的Slab,shell> memcached -o slab_reassign,slab_automove]


Memcached的LRU机制

为了规避内存碎片问题,Memcached采用了名为SlabAllocator的内存分配机制。内存以Page为单位来分配,每个Page分给一个特定长度的Slab来使用,每个Slab包含若干个特定长度的Chunk。实际保存数据时,会根据数据的大小选择一个最贴切的Slab,并把数据保存在对应的Chunk中。如果某个Slab没有剩余的Chunk了,系统便会给这个Slab分配一个新的Page以供使用,如果没有Page可用,系统就会触发LRU机制,通过删除冷数据来为新数据腾出空间,这里有一点需要注意的是:LRU不是全局的,而是针对Slab而言的。


为什么总内存没有被全部占用的情况下,memcached却出现了丢失缓存数据?

总内存没有被占用完,但是已经被分配完了,这样每个slab能够获得的内存其实已经确定了,当部分比较密集(或者开始不密集,后续增多,例如我当时遇到的,在已经基本定型的memcache上附加新的类型的应用数据,导致内存分配与数据密集程度不符)的slab出现内存不够时,会使用最久未使用算法进行移除,即使没有到达过期时间,这个时候就出现数据丢失了。但是从一些工具中,此时实际使用的内存其实没有达到最大。

内存模型几个概念

1. chunk:是用于存放数据的内存块

2. slab:是用于切分为chunk的内存块,一个slabclass可挂载多个slabs

3. slabclass:是用于管理相同chunk大小的内存的结构

4. item:用于管理在于的key/value数据的结构,一个item放于一个chunk中。 

5. LRU list: 用于管理各个slabclass的最近访问过的item, 以用于item踢出时的选择,list头部是最近访问过的item.

6. hashtable: 用于查找数据时item寻址,对key计算hash值,再在hashtable中找到对应的item, 取出数据。

7. slots list: 是slabclass用于管理当前class的空闲item的list, 由slabclass结构中的slots指针指向。


题外话:因为优秀,所以不足 

•Can’t dump –无法备份,重启无法恢复 

•Can’t iterate over keys –无法查询 

•Not persistent –没有持久化,重启全部丢失 

•Not redundant –单点故障failover 

•No Sessions –崩溃没法查找原因 

•No security –任何机器都可以telnet,需要放在防火墙后 

•内存问题 –LRU是slab局部,没有全局 –有空间浪费 

•日志问题 –没有合理的日志 

•集群问题 –集群增加机器成本高


参考:

http://yaowhat.com/2014/10/02/memcached-memory-manage.html

http://itindex.net/detail/51456-memcache-%E5%86%85%E5%AD%98-%E7%AD%96%E7%95%A5

http://lostphp.com/blog/564.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值