1. 程式人生 > >Memcached原始碼閱讀之get過程

Memcached原始碼閱讀之get過程

我們在前面分析過,Memcached從網路讀取完資料,解析資料,如果是get操作,則執行get操作,下面我們分析下get操作的流程。

//根據key資訊和key的長度資訊讀取資料
item *item_get(const char *key, const size_t nkey) {
    item *it;
    uint32_t hv;
    hv = hash(key, nkey, 0);//獲得分段鎖資訊,如果未進行擴容,則item的hash表是多個hash桶共用同一個鎖,即是分段的鎖
    item_lock(hv);//執行分段加鎖
    it = do_item_get(key, nkey, hv);//執行get操作
    item_unlock(hv);//釋放鎖
    return it;
}
//執行分段加鎖
void item_lock(uint32_t hv) {
    uint8_t *lock_type = pthread_getspecific(item_lock_type_key);
    if (likely(*lock_type == ITEM_LOCK_GRANULAR)) {
        mutex_lock(&item_locks[(hv & hashmask(hashpower)) % item_lock_count]);//執行分段加鎖
    } else {//如果在擴容過程中
        mutex_lock(&item_global_lock);
    }
}
//執行分段解鎖
void item_unlock(uint32_t hv) {
    uint8_t *lock_type = pthread_getspecific(item_lock_type_key);
    if (likely(*lock_type == ITEM_LOCK_GRANULAR)) {
        mutex_unlock(&item_locks[(hv & hashmask(hashpower)) % item_lock_count]);//釋放分段鎖
    } else {//如果在擴容過程中
        mutex_unlock(&item_global_lock);
    }
}
//執行讀取操作
item *do_item_get(const char *key, const size_t nkey, const uint32_t hv) {
    item *it = assoc_find(key, nkey, hv);//從Hash表中獲取相應的結構
    if (it != NULL) {
        refcount_incr(&it->refcount);//item的引用次數+1
        if (slab_rebalance_signal && //如果正在進行slab調整,且該item是調整的物件
            ((void *)it >= slab_rebal.slab_start && (void *)it < slab_rebal.slab_end)) {
            do_item_unlink_nolock(it, hv);//將item從hashtable和LRU鏈中移除
            do_item_remove(it);//刪除item
            it = NULL;//置為空
        }
    }
    int was_found = 0;
    //列印除錯資訊
    if (settings.verbose > 2) {
        if (it == NULL) {
            fprintf(stderr, "> NOT FOUND %s", key);
        } else {
            fprintf(stderr, "> FOUND KEY %s", ITEM_key(it));
            was_found++;
        }
    }

    if (it != NULL) {
        //判斷Memcached初始化是否開啟過期刪除機制,如果開啟,則執行刪除相關操作
        if (settings.oldest_live != 0 && settings.oldest_live <= current_time &&
            it->time <= settings.oldest_live) {
            do_item_unlink(it, hv);//將item從hashtable和LRU鏈中移除           
            do_item_remove(it);//刪除item
            it = NULL;
            if (was_found) {
                fprintf(stderr, " -nuked by flush");
            }
        //判斷item是否過期
        } else if (it->exptime != 0 && it->exptime <= current_time) {
            do_item_unlink(it, hv);//將item從hashtable和LRU鏈中移除
            do_item_remove(it);//刪除item
            it = NULL;
            if (was_found) {
                fprintf(stderr, " -nuked by expire");
            }
        } else {
            it->it_flags |= ITEM_FETCHED;//item的標識修改為已經讀取
            DEBUG_REFCNT(it, '+');
        }
    }

    if (settings.verbose > 2)
        fprintf(stderr, "\n");

    return 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);//判斷item的狀態是否正確

    if (refcount_decr(&it->refcount) == 0) {//修改item的引用次數
        item_free(it);//釋放item
    }
}
//釋放item
void item_free(item *it) {
    size_t ntotal = ITEM_ntotal(it);//獲得item的大小
    unsigned int clsid;
    assert((it->it_flags & ITEM_LINKED) == 0);//判斷item的狀態是否正確
    assert(it != heads[it->slabs_clsid]);//item不能為LRU的頭指標
    assert(it != tails[it->slabs_clsid]);//item不能為LRU的尾指標
    assert(it->refcount == 0);//釋放時,需保證引用次數為0

    /* so slab size changer can tell later if item is already free or not */
    clsid = it->slabs_clsid;
    it->slabs_clsid = 0;//斷開slabclass的連結
    DEBUG_REFCNT(it, 'F');
    slabs_free(it, ntotal, clsid);//slabclass結構執行釋放
} 
//slabclass結構釋放
void slabs_free(void *ptr, size_t size, unsigned int id) {
    pthread_mutex_lock(&slabs_lock);//保持同步
    do_slabs_free(ptr, size, id);//執行釋放
    pthread_mutex_unlock(&slabs_lock);
}
//slabclass結構釋放
static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
    slabclass_t *p;
    item *it;

    assert(((item *)ptr)->slabs_clsid == 0);//判斷資料是否正確
    assert(id >= POWER_SMALLEST && id <= power_largest);//判斷id合法性
    if (id < POWER_SMALLEST || id > power_largest)//判斷id合法性
        return;

    MEMCACHED_SLABS_FREE(size, id, ptr);
    p = &slabclass[id];

    it = (item *)ptr;
    it->it_flags |= ITEM_SLABBED;//修改item的狀態標識,修改為空閒
    it->prev = 0;//斷開資料鏈表
    it->next = p->slots;
    if (it->next) it->next->prev = it;
    p->slots = it;

    p->sl_curr++;//空閒item個數+1
    p->requested -= size;//空間增加size
    return;
}
//將item從hashtable和LRU鏈中移除。是do_item_link的逆操作
void do_item_unlink(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    mutex_lock(&cache_lock);//執行同步
    if ((it->it_flags & ITEM_LINKED) != 0) {//判斷狀態值,保證item還在LRU佇列中
        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, hv);//從Hash表中刪除
        item_unlink_q(it);//將item從slabclass對應的LRU佇列摘除
        do_item_remove(it);//移除item
    }
    mutex_unlock(&cache_lock);
}

Memcached的get操作在讀取資料時,會判斷資料的有效性,使得不用額外去處理過期資料,get操作牽涉到Slab結構,Hash表,LRU佇列的更新,我們後面專門分析這些的變更,這裡暫不分析。