设为首页 收藏本站
查看: 1568|回复: 0

[经验分享] memcached探索之item.h/item.c

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2014-3-31 10:14:45 | 显示全部楼层 |阅读模式
终于到了item的操作了,item是memcached数据管理的基本单位,也是hash, LRU等链表处理的基本单位。很核心的东西来。

一. 主要程序接口

item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes);
void item_free(item *it);
bool item_size_ok(const size_t nkey, const int flags, const int nbytes);

int do_item_link(item *it);
void do_item_unlink(item *it);
void do_item_remove(item *it);
void do_item_update(item *it);
int do_item_replace(item *it, item *new_it);

char *do_item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);

void do_item_flush_expired(void);

item *do_item_get(const char *key, const size_t nkey);
item *do_item_get_nocheck(const char *key, const size_t nkey);

二. 源代码注释和分析

1. 主要数据结构

#define ITEM_UPDATE_INTERVAL 60

#define LARGEST_ID POWER_LARGEST
typedef struct {
    unsigned int evicted;
    unsigned int evicted_nonzero;
    rel_time_t evicted_time;
    unsigned int reclaimed;
    unsigned int outofmemory;
    unsigned int tailrepairs;
} itemstats_t; //item的状态统计信息,这里就不分析了

static item *heads[LARGEST_ID]; //LRU链首指针,  每个classid一个LRU链
static item *tails[LARGEST_ID];    //LRU链尾指针,每个classid一个LRU链
static itemstats_t itemstats[LARGEST_ID];
static unsigned int sizes[LARGEST_ID];  //每个classid的LRU链的长度(item个数)

// item数据结构的定义(放在memcached.h中)
typedef struct _stritem {
    struct _stritem *next;  //next和prev是LRU链的双向链表指针
    struct _stritem *prev;
    struct _stritem *h_next;
    rel_time_t time;
    rel_time_t exptime;
    int nbytes;
    unsigned short refcount;
    uint8_t nsuffix;
    uint8_t it_flags;
    uint8_t slabs_clsid;
    uint8_t nkey;
    void * end[]; //方便item数据结构后面存储value_data




} item;
// item的三种flag: ITEM_SLABBED, ITEM_LINKED,ITEM_CAS


2. item_link_q 和 item_unlink_q (主要用于LRU链的管理)

//将item加入到对应classid的LRU链的head
oid item_link_q(item *it) {
    item **head, **tail;
    assert(it->slabs_clsid < LARGEST_ID);
    assert((it->it_flags & ITEM_SLABBED) == 0);

    head = &heads[it->slabs_clsid];
    tail = &tails[it->slabs_clsid];
    assert(it != *head);
    assert((*head && *tail) || (*head == 0 && *tail == 0));
    it->prev = 0; //双向链表的指针操作
    it->next = *head;
    if (it->next) it->next->prev = it;
    *head = it;
    if (*tail == 0) *tail = it;
    sizes[it->slabs_clsid]++; //LUR queue size++
    return;
}
//将item从对应classid的LRU链上移除
static void item_unlink_q(item *it) {
    item **head, **tail;
    assert(it->slabs_clsid < LARGEST_ID);
    head = &heads[it->slabs_clsid];
    tail = &tails[it->slabs_clsid];

    if (*head == it) {
        assert(it->prev == 0);
        *head = it->next;
    }
    if (*tail == it) {
        assert(it->next == 0);
        *tail = it->prev;
    }
    assert(it->next != it);
    assert(it->prev != it);

    if (it->next) it->next->prev = it->prev;
    if (it->prev) it->prev->next = it->next;
    sizes[it->slabs_clsid]--;
    return;
}

3. do_item_link 和 do_item_unlink,do_item_remove, do_item_update,do_item_replace等

//将item加入到hashtable并加入到对应classid的LRU链中。
int do_item_link(item *it) {
    MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
    assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    it->it_flags |= ITEM_LINKED; //进入linked状态
    it->time = current_time;
    assoc_insert(it); //加入到hashtable

    STATS_LOCK(); //stat信息统计
    stats.curr_bytes += ITEM_ntotal(it);
    stats.curr_items += 1;
    stats.total_items += 1;
    STATS_UNLOCK();


    ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
    item_link_q(it); //加入到LUR链中
    return 1;
}

//将item从hashtable和LRU链中移除。是do_item_link的逆操作
void do_item_unlink(item *it) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey);
        item_unlink_q(it);
        if (it->refcount == 0) item_free(it);
    }
}
//移除item
void do_item_remove(item *it) {
    MEMCACHED_ITEM_REMOVE(ITEM_key(it), it->nkey, it->nbytes);
    assert((it->it_flags & ITEM_SLABBED) == 0);
    if (it->refcount != 0) { //有别人在引用
        it->refcount--;
        DEBUG_REFCNT(it, '-');
    }
    if (it->refcount == 0 && (it->it_flags & ITEM_LINKED) == 0) {
        item_free(it); //释放item,接下来分析
    }
}
//更新一个item的最后访问时间,不过需要在一段时间之后
void do_item_update(item *it) {
    MEMCACHED_ITEM_UPDATE(ITEM_key(it), it->nkey, it->nbytes);
    if (it->time < current_time - ITEM_UPDATE_INTERVAL) {
        assert((it->it_flags & ITEM_SLABBED) == 0);
        if ((it->it_flags & ITEM_LINKED) != 0) {
            item_unlink_q(it);
            it->time = current_time;
            item_link_q(it);
        }
    }
}
//将item用new_item代替
int do_item_replace(item *it, item *new_it) { //
    MEMCACHED_ITEM_REPLACE(ITEM_key(it), it->nkey, it->nbytes,
                           ITEM_key(new_it), new_it->nkey, new_it->nbytes);
    assert((it->it_flags & ITEM_SLABBED) == 0);

    do_item_unlink(it);
    return do_item_link(new_it);
}

4. do_item_alloc (分配item) 和 item_free (释放item)


//由这里可以推出item的存储格式:item struct + key+1 + suffix +value + '\r\n'
static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes,
                     char *suffix, uint8_t *nsuffix) {
     //nbytes-2=value length
    *nsuffix = (uint8_t) snprintf(suffix, 40, " %d %d\r\n", flags, nbytes - 2);
    return sizeof(item) + nkey + *nsuffix + nbytes;
  }

//分配一个item空间
item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {
    uint8_t nsuffix;
    item *it = NULL;
    char suffix[40];
    size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);
    if (settings.use_cas) {
        ntotal += sizeof(uint64_t);
    }
    unsigned int id = slabs_clsid(ntotal); //寻找合适的slabclass
    if (id == 0)
        return 0;

    int tries = 50;
    item *search; //首先从LRU链尾开始,寻找一个过期的item, 最多尝试50次
    for (search = tails[id];tries > 0 && search != NULL; tries--, search=search->prev) {
        if (search->refcount == 0 &&
            (search->exptime != 0 && search->exptime < current_time)) {
            it = search;

            STATS_LOCK();
            stats.reclaimed++;
            STATS_UNLOCK();
            itemstats[id].reclaimed++;
            it->refcount = 1;
            do_item_unlink(it); //将寻找到的item移除

            it->slabs_clsid = 0;
            it->refcount = 0;
            break;
        }
    }

    if (it == NULL && (it = slabs_alloc(ntotal, id)) == NULL) {

        tries = 50; //没有找到过期item并分配chunk失败,则进行LRU淘汰一个item

        if (settings.evict_to_free == 0) {
            itemstats[id].outofmemory++;
            return NULL;
        }

        if (tails[id] == 0) {
            itemstats[id].outofmemory++;
            return NULL;
        }

        for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
            if (search->refcount == 0) {
                if (search->exptime == 0 || search->exptime > current_time) {
                    itemstats[id].evicted++;
                    itemstats[id].evicted_time = current_time - search->time;
                    if (search->exptime != 0)
                        itemstats[id].evicted_nonzero++;
                    STATS_LOCK();
                    stats.evictions++;
                    STATS_UNLOCK();
                } else {
                    itemstats[id].reclaimed++;
                    STATS_LOCK();
                    stats.reclaimed++;
                    STATS_UNLOCK();
                }
                do_item_unlink(search);
                break;
            }
        }
        it = slabs_alloc(ntotal, id); //LRU淘汰没有找到,再尝试一次内存分配。。。
        if (it == 0) {
            itemstats[id].outofmemory++;

            tries = 50; //分配失败,再尝试一次LRU淘汰
            for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
                if (search->refcount != 0 && search->time + TAIL_REPAIR_TIME < current_time) {
                    itemstats[id].tailrepairs++;
                    search->refcount = 0;
                    do_item_unlink(search);
                    break;
                }
            }
            it = slabs_alloc(ntotal, id); //最后一次chunk分配
            if (it == 0) {
                return NULL;
            }
        }
    }

    assert(it->slabs_clsid == 0); //标志已经找到item时 slabs_clsid=0
    it->slabs_clsid = id;

    assert(it != heads[it->slabs_clsid]);

    it->next = it->prev = it->h_next = 0;
    it->refcount = 1;
    DEBUG_REFCNT(it, '*');
    it->it_flags = settings.use_cas ? ITEM_CAS : 0;
    it->nkey = nkey;
    it->nbytes = nbytes;
    memcpy(ITEM_key(it), key, nkey); //存放key string
    it->exptime = exptime;
    memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix); //存放suffix
    it->nsuffix = nsuffix;
    return it;  //注意:这里并没有存放value string, 要等到调用函数来memcpy
}

//释放item
void item_free(item *it) {
    size_t ntotal = ITEM_ntotal(it);
    unsigned int clsid;
    assert((it->it_flags & ITEM_LINKED) == 0); //非LINKED状态才能free
    assert(it != heads[it->slabs_clsid]);
    assert(it != tails[it->slabs_clsid]);
    assert(it->refcount == 0);


    clsid = it->slabs_clsid;
    it->slabs_clsid = 0;
    it->it_flags |= ITEM_SLABBED; //标志归还内存到slab,唯一 一次使用 ITEM_SLABBED
    DEBUG_REFCNT(it, 'F');
    slabs_free(it, ntotal, clsid); //是否slab memory
}

bool item_size_ok(const size_t nkey, const int flags, const int nbytes) {
    char prefix[40];
    uint8_t nsuffix;

    return slabs_clsid(item_make_header(nkey + 1, flags, nbytes,
                                        prefix, &nsuffix)) != 0;
}


5. do_item_get


//这个函数写的比较乱,这里精简了一下,现在看起来简单多来吧

item *do_item_get(const char *key, const size_t nkey) {
    item *it = assoc_find(key, nkey);
    int was_found = 0;

    if (it != NULL && settings.oldest_live != 0 && settings.oldest_live <= current_time &&
        it->time <= settings.oldest_live) {
        do_item_unlink(it);
        it = NULL;
    }

    if (it != NULL && it->exptime != 0 && it->exptime <= current_time) {
        do_item_unlink(it);
        it = NULL;
    }

    if (it != NULL) {
        it->refcount++;
        DEBUG_REFCNT(it, '+');
    }

    return it;
}


item *do_item_get_nocheck(const char *key, const size_t nkey) {
    item *it = assoc_find(key, nkey);
    if (it) {
        it->refcount++;
        DEBUG_REFCNT(it, '+');
    }
    return it;
}

//注意下oldest_live定义:

void do_item_flush_expired(void) {
    int i;
    item *iter, *next;
    if (settings.oldest_live == 0)
        return;
    for (i = 0; i < LARGEST_ID; i++) { //两次循环

        for (iter = heads[i]; iter != NULL; iter = next) {
            if (iter->time >= settings.oldest_live) {
                next = iter->next;
                if ((iter->it_flags & ITEM_SLABBED) == 0) { //没被释放才需要unlink
                    do_item_unlink(iter);
                }
            } else {

                break;
            }
        }
    }
}


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-16512-1-1.html 上篇帖子: Memcached源码分析(线程模型) 下篇帖子: memcache 的总体轮廓一览
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表