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

[经验分享] Memcached源码分析之do_item_alloc操作

[复制链接]

尚未签到

发表于 2015-11-19 01:45:34 | 显示全部楼层 |阅读模式
  前面我们分析了Memcached的set操作,其set操作在经过所有的数据有效性检查之后,如果需要存储item,则会执行item的实际存储操作,我们下面分析下其过程。
  

//执行item的存储操作,该操作会将item挂载到LRU表和slabcalss中
item *do_item_alloc(char *key, const size_t nkey, const int flags,
const rel_time_t exptime, const int nbytes,
const uint32_t cur_hv) {
uint8_t nsuffix;
item *it = NULL;
char suffix[40];
size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);//计算item的总大小(空间)
if (settings.use_cas) {//如果使用了cas
ntotal += sizeof(uint64_t);//增加cas的空间
}
unsigned int id = slabs_clsid(ntotal);//那大小选择合适的slab
if (id == 0)
return 0;
mutex_lock(&cache_lock);//执行LRU锁
//存储时,会尝试从LRU中选择合适的空间的空间
int tries = 5;//如果LRU中尝试5次还没合适的空间,则执行申请空间的操作
int tried_alloc = 0;
item *search;
void *hold_lock = NULL;
rel_time_t oldest_live = settings.oldest_live;//初始化时选择的过期时间
search = tails[id];//第id个LRU表的尾部
for (; tries > 0 && search != NULL; tries--, search=search->prev) {
uint32_t hv = hash(ITEM_key(search), search->nkey, 0);//获取分段锁
if (hv != cur_hv && (hold_lock = item_trylock(hv)) == NULL)//尝试执行锁操作,这里执行的乐观锁
continue;
if (refcount_incr(&search->refcount) != 2) {//判断item是否被锁住,item的引用次数其实充当的也是一种锁
refcount_decr(&search->refcount);//更新it的引用次数
            //如果it的添加时间比当前时间小于3*3600
            if (search->time + TAIL_REPAIR_TIME < current_time) {
itemstats[id].tailrepairs++;//更新统计信息
search->refcount = 1;
do_item_unlink_nolock(search, hv);//执行分段解锁操作
}
if (hold_lock)
item_trylock_unlock(hold_lock);//执行分段解锁操作
continue;
}
//过期时间判断
if ((search->exptime != 0 && search->exptime < current_time)
|| (search->time <= oldest_live && oldest_live <= current_time)) { //过期时间的判断
itemstats[id].reclaimed++;
if ((search->it_flags & ITEM_FETCHED) == 0) {
itemstats[id].expired_unfetched++;//更新统计信息
}
it = search;
slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);//slabclass申请合适的空间
do_item_unlink_nolock(it, hv);//执行的Hash表的分段解锁操作
it->slabs_clsid = 0;
} else if ((it = slabs_alloc(ntotal, id)) == NULL) {//申请合适的slabclass
tried_alloc = 1;//申请失败一次
if (settings.evict_to_free == 0) {//关闭了LRU的
itemstats[id].outofmemory++;//统计信息更新
} else {//打开了LRU的操作
itemstats[id].evicted++;//更新统计信息
itemstats[id].evicted_time = current_time - search->time;
if (search->exptime != 0)
itemstats[id].evicted_nonzero++;
if ((search->it_flags & ITEM_FETCHED) == 0) {
itemstats[id].evicted_unfetched++;
}
it = search;
slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);//选择合适的slabclass空间
do_item_unlink_nolock(it, hv);//执行it的分段解锁操作
it->slabs_clsid = 0;
if (settings.slab_automove == 2)//如果打开了slab调整
slabs_reassign(-1, id);//唤醒调整线程
}
}
refcount_decr(&search->refcount);//更新引用次数
if (hold_lock)
item_trylock_unlock(hold_lock);//解分段锁
break;
}
if (!tried_alloc && (tries == 0 || search == NULL))//5次循环查找,未找到合适的空间
it = slabs_alloc(ntotal, id);//则从内存池申请新的空间
if (it == NULL) {//内存池申请失败
itemstats[id].outofmemory++;//更新统计信息
mutex_unlock(&cache_lock);//释放LRU锁
return NULL;
}
assert(it->slabs_clsid == 0);
assert(it != heads[id]);
    it->refcount = 1;     //更新it的引用次数
mutex_unlock(&cache_lock);
it->next = it->prev = it->h_next = 0;//执行初始化操作
it->slabs_clsid = id;//it所属的slabclass为第id个
DEBUG_REFCNT(it, '*');
it->it_flags = settings.use_cas ? ITEM_CAS : 0;
it->nkey = nkey;//it的key
it->nbytes = nbytes;//it的缓冲区的数据
memcpy(ITEM_key(it), key, nkey);//it的数据信息
it->exptime = exptime;//it的过期时间
memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);//it的前缀信息
it->nsuffix = nsuffix;//it的一些前缀信息
return it;
}
//计算item的大小
static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes,
                     char *suffix, uint8_t *nsuffix) {
    //suffix限定了40个字节
    *nsuffix = (uint8_t) snprintf(suffix, 40, &quot; %d %d\r\n&quot;, flags, nbytes - 2);
    return sizeof(item) + nkey + *nsuffix + nbytes;//返回item的长度
}
//选择合适的slabclass
unsigned int slabs_clsid(const size_t size) {
    int res = POWER_SMALLEST;
    if (size == 0)
        return 0;
    while (size > slabclass[res].size)//按slabclass的size的选择
        if (res++ == power_largest)//如果大于最大的slab的,则直接返回错误,按默认的,大于1M的申请空间失败   
            return 0;
    return res;
}
//从内存池申请合适的空间
void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal)
{
    pthread_mutex_lock(&slabs_lock);//slabclass加锁,保持同步
    slabclass_t *p;
    if (id < POWER_SMALLEST || id > power_largest) {//判断数据合法性
        fprintf(stderr, &quot;Internal error! Invalid slab class\n&quot;);
        abort();
    }
    p = &slabclass[id];
    p->requested = p->requested - old + ntotal;//调整request信息,request表示的是old所在的slab申请空间大小
    pthread_mutex_unlock(&slabs_lock);
}



  

版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 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-140842-1-1.html 上篇帖子: MEMCACHED在集群环境下对并发更新是否保持数据一致 下篇帖子: Memcached(三):数据存储测试
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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