缓存数据以item为基本单元,以双链表形式存放在对应级别大小的slabclass结构的chunk中。同时该item还存放在链式hashtable中bucket中,用于提供快速查找的索引。

首先是理解缓存的基本数据单元item结构:

typedef struct _stritem {
    struct _stritem *next;    //在slab中的双链表后向指针
    struct _stritem *prev;    //在slab中的双向链表的前向指针
    struct _stritem *h_next;  //指向hash表该bucket中的该item的下一项  /* hash chain next */
    rel_time_t      time;      //最近访问时间戳 /* least recent access */
    rel_time_t      exptime;    //过期时间/* expire time */
    int            nbytes;    //数据大小/* size of data */
    unsigned short  refcount;    //引用计数
    uint8_t        nsuffix;    /* length of flags-and-length string */
    uint8_t        it_flags;  /* ITEM_* above */
    uint8_t        slabs_clsid; //所在的slab,该slab在slabclass数组中的下标/* which slab class we're in */
    uint8_t        nkey;      //key的长度/* key length, w/terminating null and padding */
    /* this odd type prevents type-punning issues when we do
    * the little shuffle to save space when not using CAS. */
    union {
        uint64_t cas;
        char end;
    } data[];//真实数据
    /* if it_flags & ITEM_CAS we have 8 bytes CAS */
    /* then null-terminated key */
    /* then " flags length\r\n" (no terminating null) */
    /* then data with terminating \r\n (no terminating null; it's binary!) */
} item;

item的结构图如下:

item存放的数据结构slabclass:

//slabclass结构
typedef struct {
    unsigned int size;    //该slab的每个chunk的大小 /* sizes of items */
    unsigned int perslab;  //能存放的size大小的chunk的数量/* how many items per slab */

void *slots;          /* 回收来的item链表,
        当分配出去的item回收时不时将空间还给slab,
        而是直接把该slab从chunk双向链表中删除,
        挂到slots链表的尾部,以供循环利用,
        且在下次使用时不需要再初始化该item结构,
        而是直接更改其各属性值即可list of item ptrs */
    unsigned int sl_curr;  /* 表示当前slots链表中
                            有多少个回收而来的空闲 item.
        total free items in list */

unsigned int slabs;    //已分配的当前种类slab的数量/* how many slabs were allocated for this class */

void **slab_list;      /* 初始时, memcached 为每个级别的slabclass 分配一个slab,
       当这个 slab 内存块使用完后,
       memcached 就分配一个新的 slab,
       所以 slabclass 可以拥有多个同一级别的slab,
       这些 slab 就是通过 slab_list 数组来管理的, 
       slab. array of slab pointers */
    unsigned int list_size; /* 表示当前 slabclass 有多少个slab
       size of prev array */

unsigned int killing;  /* index+1 of dying slab, or zero if none */
    size_t requested;  /* The number of requested bytes */
} slabclass_t;

static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];//slab数组(其中slab按其chunk从小到大排列)

(注意:同一级别的slabclas可能包括多个该级别的slab,维护在指针数组slab_list中)

item在slabclass中存放的结构示意图:

在 slabclass 内, 只有最后一个 slab 存在空闲的内存, 其它 slab 的 chunk 都分配出去了。

end_page_ptr:指向最后一个 slab 中的空闲内存块

end_page_free :表示最后一个 slab 中还剩下多少个空闲 chunk.  图中绿色部分的 chunk 表示空闲 chunk。

每个slabclass维护一个双向链表,所有分配出去的item按照最近访问时间依次放到该链表中,该链表也就相当于LRU队列。

所有slabclass的链表头 尾分表保存在*heads、 *tails两个数组中:

static item *heads[LARGEST_ID];//chunk链表头指针数组:slabclass数组中各级别slabclass的chunk链表头 组成的数组
static item *tails[LARGEST_ID];//chunk链表尾指针数组:slabclass数组中各级别slabclass的chunk链表尾 组成的数组

item空间分配策略:

** 每次需要为新的item分配空间时,首先根据该item的大小,计算出对应级别的slabclass的id,然后在slabclass数组中找到该slabclass。

**  定位到对应slabclass后,首先检查LRU队列的最后一个chunk是否过期,过期则分给用户使用;否则到item回收链表slots中查空闲的chunk;没有回收空闲的chunk则从slab空闲(未分配过得)的chunk中分配;如果没有,则LRU算法在已分配chunk的双向链表中从    尾部向前查找能够释放(最久未访问)的item,依次为新item取得空间。

** 当删除某item时,并不将该chunk空间归还给对应的slab,而是从该slab的已分配chunk链表中删除该chunk ,然后将该chunk挂到回收链表slots的头部,以供循环利用,    并且该chunk中的item也不会释放,直到该chunk被重新利用时直接更新该item的各项属性值。(不用每次都初始化item结构,提高效率!)

链式HsahTable:

同时,slab的chunk链表中的item也被存放到hashTable中。 当需要查找给定key的item时,首先在哈希表中hash到该key对应的item,然后利用hashtable中的item信息得到该item在slabclass中索引位置。

使用了两张hashtable,一个主表,一个“原表”。正常情况下,操作都是在主表中进行的;当正在扩容时,首先在原表中进行操作。

当表中item数量大于表bucket节点数的1.5倍时开始扩容为原来的2倍,采用逐步扩容方式,每次迁移的数量可以设置。  主表与原表是动态切换的,当扩容开始的时候,把主表的类容复制到原表中,让原表替换主表暂时接受操作,而主表容量扩大为原来的两倍,然后逐步从原表中将数据hash到扩容后的主表中,当数据全部迁移完成,所有的操作又回到主表中进行了。

这与Redis中的两张hashtable的操作是一致的。

分布式缓存系统 Memcached 数据存储slab与hashtable的更多相关文章

  1. 分布式缓存系统 Memcached 整体架构

    分布式缓存系统 Memcached整体架构 Memcached经验分享[架构方向] Memcached 及 Redis 架构分析和比较

  2. 分布式缓存系统Memcached简介与以及在.net下的实践(转)

    缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...

  3. 分布式缓存系统 Memcached 快速入门

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

  4. 分布式缓存系统Memcached简介与实践

    缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...

  5. 分布式缓存系统Memcached简介与实践(.NET memcached client library)

    缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够灵 ...

  6. [Memcached]分布式缓存系统Memcached在Asp.net下的应用

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

  7. (转)C# 中使用分布式缓存系统Memcached

    转自:http://blog.csdn.net/devgis/article/details/8212917 缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了 ...

  8. 分布式缓存系统Memcached在Asp.net下的应用

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

  9. php分布式缓存系统 Memcached 入门

    Memcached 是一个分布式的缓存系统, 但是 Memcachd 到底是什么意思,有什么作用呢?缓存一般用来保存一些经常被存取的数据和资源(例如:浏览器会将访问过的网页会话缓存起来),因为通过缓存 ...

随机推荐

  1. scala学习手记33 - 使用trait进行装饰

    在上一节看到了scala的在实例一级的选择性混入就不得不感叹scala在语法上的扩展性.就通过这样一个特性scala简化了很多在java中的编程概念和设计模式. 比如说在java中常用的组合,以及装饰 ...

  2. Dagger2 中的 Scope

    Dagger2 中虽然概念挺多的,但是大部分花时间都能理清.包括看人家的分析,Debug 代码下去也能懂.但是对于 scope 的用法以及实现原理还是有点难理解的.主要的问题也像简书上的文章所说: 自 ...

  3. npm install 时总是报phantomjs-prebuilt@2.1.14安装失败

    在npm install时总是报如下错误, 尝试单独安装:npm install phantomjs-prebuilt@2.1.14 还是报错 Please report this full log ...

  4. socket编程 TCP 粘包和半包 的问题及解决办法

    一般在socket处理大数据量传输的时候会产生粘包和半包问题,有的时候tcp为了提高效率会缓冲N个包后再一起发出去,这个与缓存和网络有关系. 粘包 为x.5个包 半包 为0.5个包 由于网络原因 一次 ...

  5. app与后台的token、sessionId、RSA加密登录认证与安全解决方案

    一.登录机制 粗略地分析, 登录机制主要分为登录验证.登录保持.登出三个部分.登录验证是指客户端提供用户名和密码,向服务器提出登录请求,服务器判断客户端是否可以登录并向客户端确认. 登录认保持是指客户 ...

  6. Django进阶Form篇

    一.django表单系统中,所有的表单类都作为django.forms.Form的之类创建,包括ModelForm 关于django的表单系统,主要分两种: 1.基于django.forms.Form ...

  7. java多线程补:充原子性和可见性

    参考:http://www.cnblogs.com/mengyan/archive/2012/08/22/2651575.html 原子性:所谓原子性就是不可分割的,比如:在我们编程中直接给变量赋值, ...

  8. 剑指offer--24.树的子结构

    时间限制:1秒 空间限制:32768K 热度指数:407165 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构)   class Solution ...

  9. Lua基础---lua字符串库函数详解,实例及正则表达式

    参考这个网友写的,比较简单容易理解. http://blog.csdn.net/wlgy123/article/details/49206183

  10. 1 秒杀系统模拟基础实现,使用DB实现

    本文根据动脑学院的一节类似的课程,改编实现.分别使用DB和redis来完成. 隔离的解释 业务隔离:将秒杀业务独立出来,尽量不与其他业务关联,以减少对其他业务的依赖性.譬如秒杀业务只保留用户id,商品 ...