Chinaunix首页 | 论坛 | 博客
  • 博客访问: 147159
  • 博文数量: 27
  • 博客积分: 2011
  • 博客等级: 大尉
  • 技术积分: 332
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-02 16:13
文章分类

全部博文(27)

文章存档

2009年(18)

2008年(9)

我的朋友

分类: C/C++

2009-02-09 09:33:05

memcached缓存,只能保存【关键字/数据对】
    存 :自己通过保存命令(协议中的set add等命令)保存的,命令是字符串
         要保存数据的最大长度是有限制的(1M),超过的保存【失败】
         初始化时指定了一个能分配给memcached的用于缓存的最大内存(如1024M),
         当且仅当已经用完了,就用【LRU算法】删除一个项用来保存新项
          如命令"set foo 0 600 3\r\nbar\r\n"
           set  命令,通知memcached保存该项
           foo  关键字
           0    flags,memcached只是帮我们保存,对memcached没意义
           600  超时秒数,这里超过10分钟后,该缓存数据就失效了
           3    数据的字节数
           \r\n 分隔符
           bar  关键字对应的数据
           \r\n 分隔符
    取 :通过协议的get命令获取之前保存到memcached中的数据,返回的是字符串
         如果
          之前已经保存了,
          并且没有超时,
          并且没有因为分配给memcached的内存不足该项被用LRU算法挪用,就返回该缓存项
            如命令"get foo\r\n"
             get 命令,在memcached缓存中查找
             foo 关键字
            返回"VALUE foo 0 3\r\nbar\r\n"
             VALUE 前缀
             foo   关键字
             0     flags
             \r\n  分隔符
             bar   该关键字对应的数据
             \r\n  分隔符
每个缓存项用数据结构item表示
    关键字/flags/数据三项内容都是保存在end字符串中,格式为"关键字\0 flags 数据长度\r\n数据\r\n"
    其他的项是信息项和用于数据结构组织(hash桶链表指针,正在使用item项双向链表指针等)的项
需要保存项时,不是直接malloc分配内存的,而是采用了slabs分配算法,放到合适的chunk中的
  每次malloc分配的内存是一个特定大小(比如1M)的slab
  然后把这个slab划分成很多大小(比如88字节)相同的chunk
  每个chunk中能容纳一个不超过其容量的item(slab=1M, chunk=88B时能放11915个chunk)
如果需要保存很多接近且不超过88B的item,一个slab不足时,就会分配另外一个同样组织结构的slab
这样有很多chunk大小为88B的slab,所有这些slab的集合成为slabs class,用数据结构slabclass_t表示
  slab只是一个概念,没有专门的数据结构表示,其chunk的大小在slabclass_t中指出了
  slabclass_t中包含有所有slab的指针
slabs class中需要记录
  每个chunk的大小,每个slab中能容纳的chunk的个数
  它有哪些slab(指针)
  chunk状态
    1:哪些chunk还从来没有使用过 : slabclass_t.end_page_ptr 第一个未被使用的空间的指针
                                   slabclass_t.end_page_free 未使用的chunk的个数
    2:哪些chunk使用过被回收了   : slabclass_t.slots二维指针维护它们的指针
    3:哪些chunk正在使用         : slabclass_t本身没有记录这些,
                                   通过另外的全局的双向链表(heads或tails)记录
                                   struct item中的next prev字段就是双向链表指针
slab只管内存分配、回收和维护,所有项是通过hash表和单向链表组织起来的
hash表
    hash表存的是是item的指针
    初始化时是有2^16个桶,当item个数超过同的个数的1.5倍时,会扩大一倍
            #define hashsize(n) ((ub4)1<<(n))
    初始化  primary_hashtable = calloc(hashsize(hashpower), sizeof(void *));
    扩大    if (! expanding && hash_items > (hashsize(hashpower) * 3) / 2)
                primary_hashtable = calloc(hashsize(hashpower + 1), sizeof(void *));
hash函数
    没懂,
    其中涉及位操作,在configure时还要判断是大端字节序还是小端字节序
具有相同hash值的item,通过单向链表连接(struct item中的h_next字段)
操作
    查找
    插入 插入桶的最前面
    删除 只是把需要删除前面的指针跳过需要删除的,指向后面的,不会释放内存
由于加入了slabs内存分配管理。所以在增删项时涉及到多个方面
    1:组织item项指针的hash表
    2:slabs class结构中
    3:对应slabs class正在使用item的双向链表heads tails
do_item_alloc
    依次查看
        使用后被回收【1】
        分配后从未被使用【2】
    中还有没空闲的chunk,有就返回这个
    如果两种情况都没有,那么尝试分配新的slab【3】(如果系统(所有slab class)已分配内存超过了限制,则失败)
    如果不能分配新的slab,就是用LRU算法(heads tails说明)删除(do_item_unlink)一个item,返回这个【4】
do_item_link
    1:把该item指针插入hash表桶
    2:把该item插入双向链表的最前面
do_item_unlink
    1:从hash表桶中删除该item的指针,就找不到了
    2:从该slab class双向链表heads(链接了该slab class所有正在使用的items的指针)中去掉这个item指针
    3:把这个item空间放到其slab class的空闲区列表slots中
   
do_item_remove
    1.1:或者减少引用计数refcount
    1.2:或者把该item放入到其slab class的被释放空闲item堆中(slots)
   
do_item_update
    把该item放到双线链表的最前而已,这样根据LRU算法被删除的可能会推迟
   
do_item_replace
    do_item_unlink老的item
    do_item_link新的item
item_get
    根据关键字查找item
        如果没找到,返回NULL
        如果已经超时就do_item_unlink该item,返回NULL(lazy expiration/deletion logic等到了查找时才看是不是超时)
    如果找到并且没有超时,就返回该item的指针
----------------------------------------------------------------------------
memcached中的struct conn包含很多字段,还有指针,指向malloc的内存
如果每次要用的时候malloc一个。用完free,耗时
memcached中malloc的struct conn,用完不会free,而是放到一个可用链表中
如果下回需要struct conn时,先看这个链表中还有没有,如果有就从链表的尾部取一个来用,没有在malloc一个
如果不需要了就放到链表的尾部,而不是free释放空间。
/*
 * Free list management for connections.
 */
static conn **freeconns;  // 可用链表
static int freetotal;     // 总共的指针个数
static int freecurr;      // 可用链表尾数组下标

slabcalss_t代表一个存放指定大小chunk的集合,系统中有很多个slabclass_t
比如存放的88 112字节长度的两个subclass_t
size    -- slab class中每个chunk的大小
perslab -- 每个slab中能存放上面size大小chunk的个数
这两个参数在初始化时确定,并且始终不变,下面是一个实际运行例子
    /userhome/zhongyj/sfw/memcached/bin>memcached -vv
    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   5: chunk size    232 perslab  4519
    slab class   6: chunk size    296 perslab  3542
    ......
    每个slab的大小由宏参数POWER_BLOCK决定这里是1M,每个slab能存放的个数perslab = 1M / size,

初始化时确定每个slab class的chunk大小
全局变量
static void *mem_base = NULL;
static void *mem_current = NULL;
static size_t mem_avail = 0;
只在启动就与分配所有内存的情况下才有用
mem_base记录的是启动就分配所有内存的指针,如果不是启动就与分配所有内存的情况,这个值恒定为NULL
mem_current记录的是与分配内存空闲区的首指针
mem_avail记录的是预分配的空间还有多少可用的空间

current_time
    全局当前时间:是自启动以来的秒数,作为事件(clock_handler函数),周期的被更新,其他需要取当前时间时,就去这个全局
   
每个slab class都有一个双向链表
    static item *heads[255];
    static item *tails[255];
heads指向双向链表的头 : 插入的时候从这边插入
tails指向双线链表的尾 : 删除的时候从这边删除(LRU算法)
    int tries = 50;
    for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev)
    {
        if (可以删除)
            do_item_unlink(search);  // 从hash表的桶中删除指针,然后把这个item放到slab class的空闲item列表中slots
    }
 
 

typedef struct {
    unsigned int size; /* sizes of items */ // 每个chunk字节数大小

    unsigned int perslab; /* how many items per slab */ // 每个slab中chunk个数


    void **slots; /* list of item ptrs */ // 被释放的空闲的item,分配时首先分配这些,不够时在看末尾,还不够就要malloc分配

    unsigned int sl_total; /* size of previous array */ // slots的总容量,每次分配时是16的2次方个,这个就是这个数

    unsigned int sl_curr; /* first free slot */ // 第一个还没用的的slots


    void *end_page_ptr; /* pointer to next free item at end of page, or 0 */ // 最后一个slab空闲内存起始地址

    unsigned int end_page_free; /* number of items remaining at end of last alloced page */ // 最后一个slab空闲区能存放的item个数


    unsigned int slabs; /* how many slabs were allocated for this class */ // 每次malloc实际分配一个slab(1M)是会加1


    void **slab_list; /* array of slab pointers */
    unsigned int list_size; /* size of prev array */ // 每次分配slab_list时,都是分配16的2次方倍,这个是这个数,而slabs是实际malloc分配了内存的个数


    unsigned int killing; /* index+1 of dying slab, or zero if none */
} slabclass_t;

typedef struct _stritem {
    struct _stritem *next; // 可用item的双向链表(heads tails)的前面item指针

    struct _stritem *prev; // 可用item的双向链表(heads tails)的后面item指针

    struct _stritem *h_next; /* hash chain next */ // 指向hash表该桶的下一项, hash表中的项就是item指针

    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;/* which slab class we're in */
    uint8_t nkey; /* key length, w/terminating null and padding */
    uint64_t cas_id; /* the CAS identifier */
    void * end[]; // 关键字 flags 数据长度 数据内容都存放在一个字符串数组中

    /* 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;

阅读(4051) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~