memcached-items操作

        今天主要總結items相關的操作,items的操作分佈比較多,定義和實現在memcachd.h/c、thread.h/c、items.h/c都有,感覺完全可以放在items.h/c中。這裏就對所有的這些操作(除去stats部分)進行一個簡單的總結。

        首先對數據結構、ITEM_*宏和一些變量進行一個簡單的說明,這裏先建立一個宏觀的概念,理解了它們的用途對後續閱讀程序有很大的幫助。

  1. /** 
  2.  * Structure for storing items within memcached. 
  3.  */  
  4. typedef struct _stritem {  
  5.     struct _stritem *next;      // next, prev在LRU隊列中使用  
  6.     struct _stritem *prev;  
  7.     struct _stritem *h_next;    /* hash chain next */     // 用於hashtable中  
  8.     rel_time_t      time;       /* least recent access */ // 最近訪問時間  
  9.     rel_time_t      exptime;    /* expire time */         // 過期時間  
  10.     int             nbytes;     /* size of data */        // 數據的長度(數據格式等下面介紹)  
  11.     unsigned short  refcount;   // 引用計數  
  12.     uint8_t         nsuffix;    /* length of flags-and-length string */  
  13.     uint8_t         it_flags;   /* ITEM_* above */  
  14.     uint8_t         slabs_clsid;/* which slab class we're in */  
  15.     uint8_t         nkey;       /* key length, w/terminating null and padding */  
  16.     /* this odd type prevents type-punning issues when we do 
  17.      * the little shuffle to save space when not using CAS. */  
  18.     union {  
  19.         uint64_t cas;  
  20.         char end;  
  21.     } data[];  
  22.     /* if it_flags & ITEM_CAS we have 8 bytes CAS */  
  23.     /* then null-terminated key */  
  24.     /* then " flags length\r\n" (no terminating null) */  
  25.     /* then data with terminating \r\n (no terminating null; it's binary!) */  
  26. } item;  

        從上面的定義可以知道data字段的格式如下:

               if it_flags & ITEM_CAS, 8bytes      # 如果ITEM_CAS標誌設置時,這裏有8字節的數據

               key<null>                                       # 鍵值字符串,以null結尾,nkey長度不包括null字符

               flags-length\r\n                               # flags-and-length字符串,以\r\n結尾,nsuffix長度包括\r\n

               value\r\n                                         # 數據域,以\r\n結尾,nbytes長度包括\r\n

         知道了具體的data字段的格式,ITEM_*宏就很容易理解了,主要考慮ITEM_CAS標誌設置時,後面的key,flags-length及value字段的存取都需要後移8字節。

  1. // 獲取cas值,即當<span style="font-size:16px;">ITEM_CAS標誌設置時</span>,返回8字節的cas值,否則返回0  
  2. #define ITEM_get_cas(i) (((i)->it_flags & ITEM_CAS) ? \  
  3.         (i)->data->cas : (uint64_t)0)  
  4.   
  5. // 設置cas值,同上類似  
  6. #define ITEM_set_cas(i,v) { \  
  7.     if ((i)->it_flags & ITEM_CAS) { \  
  8.         (i)->data->cas = v; \  
  9.     } \  
  10. }  
  11. // 獲得key值,<span style="font-size:16px;">如果ITEM_CAS標誌設置時</span>,需要後移8字節  
  12. #define ITEM_key(item) (((char*)&((item)->data)) \  
  13.          + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))  
  14.   
  15. // 獲得suffix字段,同上類似  
  16. #define ITEM_suffix(item) ((char*) &((item)->data) + (item)->nkey + 1 \  
  17.          + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))  
  18. // 獲得數據域  
  19. #define ITEM_data(item) ((char*) &((item)->data) + (item)->nkey + 1 \  
  20.          + (item)->nsuffix \  
  21.          + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))  
  22.   
  23. // 獲得總結構的大小  
  24. #define ITEM_ntotal(item) (sizeof(struct _stritem) + (item)->nkey + 1 \  
  25.          + (item)->nsuffix + (item)->nbytes \  
  26.          + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))  

        下面3個宏,定義了item的狀態:

  1. #define ITEM_LINKED 1  
  2. #define ITEM_CAS 2  
  3. #define ITEM_SLABBED 4  

        ITEM_LINKED表示item處於hashtable和LRU隊列中,分別在do_item_link(將item放入hashtable和LRU隊列)和do_item_unlink(將item從hashtable和LRU隊列移除)中設置和置位,後續的對it_flags &ITEM_LINKED的判斷,根據是否在hashtable和LRU隊列中,從而能夠進行或禁止某些操作,如:如果item在hashtable和LRU隊列,就不能執行free_item操作(assert((it->it_flags & ITEM_LINKED) == 0)保證),必須首先do_item_unlink。

       Item_CAS是根據用戶命令行參數決定的,CAS表示check and set,只有cas字段一致時(check)才執行後續操作(set),否則操作失敗。

        ITEM_SLABBED,這個標誌只有在item_free函數中,slabs_free函數調用前設置,說明只有將其放入slabs對應的slabclass_t的freelist指針數組中時,才設置此標誌,即設置此標誌的item應該爲free狀態,it_flags & ITEM_SLABBED即可以以此來理解。

        下面說明LRU隊列,主要包括heads和tails,sizes數組只是爲了stats時使用,記錄了每個對應的heads中item的數量:

  1. #define LARGEST_ID POWER_LARGEST   
  2. static item *heads[LARGEST_ID];         // LRU head buckets, 每個與slabclass對應,雙向鏈表(每次插入放最前面)  
  3. static item *tails[LARGEST_ID];         // LRU tail buckets, 雙向鏈表,記錄了LRU隊列的最後一個元素(oldest)  

        slabclass定義爲:

  1. #define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1)  
  2. static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];   // slabclass數組  

        感覺 #define LARGEST_ID POWER_LARGEST  改爲    #define LARGEST_ID POWER_LARGEST+1 更合理一些,這樣heads就與slabclass一一對應起來了,雖然slabclass及heads並不是每個位置都使用了。因爲slabclass[0]並沒有使用,所以heads[0]和tails[0]也沒有使用(使用方法heads[slabs_clsid],slabs_clsid非0)。
        

        全局設置setting中存在一個oldest_live的字段,表示在oldest_live時間之前的數據失效,比如flush_all操作(memcached的文本協議),設置oldest_live爲當前時間,這樣之前的數據都失效了,LRU算法執行時,就可以複用這些item了,這裏稱它爲截止時間

        還有就是對memcached中引用計數的理解,只有do_item_get(或do_item_get_nocheck)時引用計數++,do_item_remove時引用計數--,其它地方不涉及具體的操作。

        只有item_link_q和item_unlink_q(名字後面有個_q)兩個函數才操作heads和tails數組,完成對LRU隊列的維護。


        下面貼出代碼,並進行相應的註釋。 

  1. /** 
  2.  * Generates the variable-sized part of the header for an object. 
  3.  * 
  4.  * key     - The key 
  5.  * nkey    - The length of the key(include the null terminator) 
  6.  * flags   - key flags 
  7.  * nbytes  - Number of bytes to hold value and addition CRLF terminator 
  8.  * suffix  - Buffer for the "VALUE" line suffix (flags, size). 
  9.  * nsuffix - The length of the suffix is stored here. 
  10.  * 
  11.  * Returns the total size of the header. 
  12.  */  
  13. static size_t item_make_header(const uint8_t nkey, const int flags, const int nbytes,  
  14.                      char *suffix, uint8_t *nsuffix) {  
  15.     /* suffix is defined at 40 chars elsewhere.. */  
  16.     *nsuffix = (uint8_t) snprintf(suffix, 40, " %d %d\r\n", flags, nbytes - 2);  
  17.     return sizeof(item) + nkey + *nsuffix + nbytes;  
  18. }  
  19.   
  20. /* 分配一個item結構 */  
  21. item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes) {  
  22.     uint8_t nsuffix;  
  23.     item *it = NULL;  
  24.     char suffix[40];  
  25.     size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);  
  26.     if (settings.use_cas) {  
  27.         ntotal += sizeof(uint64_t);         // if( (it_flags & ITEM_CAS)!=0), 8bytes CAS  
  28.     }  
  29.   
  30.     unsigned int id = slabs_clsid(ntotal);  // 返回對應的slab的clsid  
  31.     if (id == 0)                            // 查找失敗  
  32.         return 0;  
  33.   
  34.     /* do a quick check if we have any expired items in the tail.. */  
  35.     int tries = 50;  
  36.     item *search;  
  37.     rel_time_t oldest_live = settings.oldest_live;  
  38.   
  39.     for (search = tails[id];  
  40.          tries > 0 && search != NULL;  
  41.          tries--, search=search->prev) {  
  42.         if (search->refcount == 0 &&            // 引用計數爲0  
  43.             ((search->time < oldest_live) ||    // dead by flush,比oldest_live舊的已失效  
  44.              (search->exptime != 0 && search->exptime < current_time))) { // 過期  
  45.             it = search;  
  46.             /* I don't want to actually free the object, just steal 
  47.              * the item to avoid to grab the slab mutex twice ;-) 
  48.              */  
  49.             STATS_LOCK();  
  50.             stats.reclaimed++;  
  51.             STATS_UNLOCK();  
  52.             itemstats[id].reclaimed++;  
  53.             it->refcount = 1;           // 後面會重用此item  
  54.               
  55.             // 調整old item與新的item之間的差值,ITEM_ntotal(it)與ntotal計算方式相同  
  56.             slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);  
  57.               
  58.             do_item_unlink(it);                 // 從hashtable和LRU隊列摘除item  
  59.             /* Initialize the item block: */  
  60.             it->slabs_clsid = 0;  
  61.             it->refcount = 0;  
  62.             break;  
  63.         }  
  64.     }  
  65.    
  66.     // 上面查找過程沒有找到,則首先分配一個,如果分配失敗,則通過LRU算法嘗試摘除一些item  
  67.     // slabs_alloc:從slabclass[id]中分配一個size大小的trunk,錯誤時返回NULL(0)  
  68.     if (it == NULL && (it = slabs_alloc(ntotal, id)) == NULL) {  
  69.         /* 
  70.         ** Could not find an expired item at the tail, and memory allocation 
  71.         ** failed. Try to evict some items! 
  72.         */  
  73.   
  74.         /* If requested to not push old items out of cache when memory runs out, 
  75.          * we're out of luck at this point... 
  76.          */  
  77.   
  78.         // 通過M指定不使用LRU算法淘汰old item  
  79.         // -M: return error on memory exhausted (rather than removing items)  
  80.         if (settings.evict_to_free == 0) {  
  81.             itemstats[id].outofmemory++;  
  82.             return NULL;  
  83.         }  
  84.   
  85.         /* 
  86.          * try to get one off the right LRU 
  87.          * don't necessarily unlink the tail because it may be locked: refcount>0 
  88.          * search up from tail an item with refcount==0 and unlink it; give up after 50 
  89.          * tries 
  90.          */  
  91.   
  92.         if (tails[id] == 0) {  
  93.             itemstats[id].outofmemory++;  
  94.             return NULL;  
  95.         }  
  96.         tries = 50; // 最多嘗試50次  
  97.         for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev)   
  98.         {  
  99.             if (search->refcount == 0) // 引用計數爲0  
  100.             {  
  101.                 if (search->exptime == 0 || search->exptime > current_time)   
  102.                 {  
  103.                     itemstats[id].evicted++;  
  104.                     itemstats[id].evicted_time = current_time - search->time;  
  105.                     if (search->exptime != 0)  
  106.                         itemstats[id].evicted_nonzero++;  
  107.                     STATS_LOCK();  
  108.                     stats.evictions++;  
  109.                     STATS_UNLOCK();  
  110.                 }   
  111.                 else   
  112.                 {  
  113.                     itemstats[id].reclaimed++;  
  114.                     STATS_LOCK();  
  115.                     stats.reclaimed++;  
  116.                     STATS_UNLOCK();  
  117.                 }  
  118.                 do_item_unlink(search); // 從hashtable及LRU隊列摘除item項  
  119.                   
  120.                 break;                  // 找到第一個引用計數爲0的就結束此循環  
  121.             }  
  122.         }  
  123.         // 這裏重新分配,如果上面循環回收了item結構,這裏能夠分配成功  
  124.         // 否則,分配會失敗,然後強制回收時間鎖定(refcount>0)時間過長(超過3小時)的item  
  125.         it = slabs_alloc(ntotal, id);     
  126.         if (it == 0)   
  127.         {  
  128.             itemstats[id].outofmemory++;  
  129.             /* Last ditch effort. There is a very rare bug which causes 
  130.              * refcount leaks. We've fixed most of them, but it still happens, 
  131.              * and it may happen in the future. 
  132.              * We can reasonably assume no item can stay locked for more than 
  133.              * three hours, so if we find one in the tail which is that old, 
  134.              * free it anyway. 
  135.              */  
  136.             tries = 50;  
  137.             for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev)   
  138.             {  
  139.                 if (search->refcount != 0 && search->time + TAIL_REPAIR_TIME < current_time)   
  140.                 {  
  141.                     itemstats[id].tailrepairs++;  
  142.                     search->refcount = 0;  
  143.                     do_item_unlink(search);  
  144.                     break;  
  145.                 }  
  146.             }  
  147.             it = slabs_alloc(ntotal, id);  
  148.             if (it == 0) {  
  149.                 return NULL;  
  150.             }  
  151.         }  
  152.     }  
  153.   
  154.     assert(it->slabs_clsid == 0);  
  155.   
  156.     it->slabs_clsid = id;  
  157.   
  158.     assert(it != heads[it->slabs_clsid]);  
  159.   
  160.     it->next = it->prev = it->h_next = 0;  
  161.     it->refcount = 1;     /* the caller will have a reference */  
  162.     DEBUG_REFCNT(it, '*');  
  163.     it->it_flags = settings.use_cas ? ITEM_CAS : 0; //? 0  
  164.     it->nkey = nkey;                                // key由null結尾,nkey長度不包括null字符  
  165.     it->nbytes = nbytes;                            // value由\r\n結尾,nbytes長度包括\r\n  
  166.     memcpy(ITEM_key(it), key, nkey);  
  167.     it->exptime = exptime;  
  168.     memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);  
  169.     it->nsuffix = nsuffix;                          // suffix由\r\n結尾,nsuffix長度包括\r\n  
  170.     return it;  
  171. }  
  172.   
  173. void item_free(item *it) {  
  174.     size_t ntotal = ITEM_ntotal(it);  
  175.     unsigned int clsid;  
  176.     assert((it->it_flags & ITEM_LINKED) == 0);  
  177.     assert(it != heads[it->slabs_clsid]);  
  178.     assert(it != tails[it->slabs_clsid]);  
  179.     assert(it->refcount == 0);  
  180.   
  181.     /* so slab size changer can tell later if item is already free or not */  
  182.     clsid = it->slabs_clsid;  
  183.     it->slabs_clsid = 0;  
  184.     it->it_flags |= ITEM_SLABBED;  
  185.     DEBUG_REFCNT(it, 'F');  
  186.     // 將ptr指向的item結構放入slabclass[clsid]的slabclass_t的freelist數組中  
  187.     slabs_free(it, ntotal, clsid);  
  188. }  
  189.   
  190. /** 
  191.  * Returns true if an item will fit in the cache (its size does not exceed 
  192.  * the maximum for a cache entry.) 
  193.  */  
  194. bool item_size_ok(const size_t nkey, const int flags, const int nbytes) {  
  195.     char prefix[40];  
  196.     uint8_t nsuffix;  
  197.   
  198.     size_t ntotal = item_make_header(nkey + 1, flags, nbytes,  
  199.                                      prefix, &nsuffix);  
  200.     if (settings.use_cas) {  
  201.         ntotal += sizeof(uint64_t);  
  202.     }  
  203.   
  204.     return slabs_clsid(ntotal) != 0;  
  205. }  
  206.   
  207. // 把該item插入LRU隊列  
  208. static void item_link_q(item *it) { /* item is the new head */  
  209.     item **head, **tail;  
  210.     assert(it->slabs_clsid < LARGEST_ID);  
  211.     assert((it->it_flags & ITEM_SLABBED) == 0);  
  212.   
  213.     head = &heads[it->slabs_clsid];  
  214.     tail = &tails[it->slabs_clsid];  
  215.     assert(it != *head);  
  216.     assert((*head && *tail) || (*head == 0 && *tail == 0));  
  217.     it->prev = 0;  
  218.     it->next = *head;  
  219.     if (it->next) it->next->prev = it;  
  220.     *head = it;  
  221.     if (*tail == 0) *tail = it;  
  222.     sizes[it->slabs_clsid]++;  
  223.     return;  
  224. }  
  225.   
  226. // 從LRU隊列刪除此item  
  227. static void item_unlink_q(item *it) {  
  228.     item **head, **tail;  
  229.     assert(it->slabs_clsid < LARGEST_ID);  
  230.     head = &heads[it->slabs_clsid];  
  231.     tail = &tails[it->slabs_clsid];  
  232.     // 頭部  
  233.     if (*head == it) {  
  234.         assert(it->prev == 0);  
  235.         *head = it->next;  
  236.     }  
  237.     // 尾部  
  238.     if (*tail == it) {  
  239.         assert(it->next == 0);  
  240.         *tail = it->prev;  
  241.     }  
  242.     assert(it->next != it);  
  243.     assert(it->prev != it);  
  244.   
  245.     // 改變雙向鏈表指針  
  246.     if (it->next) it->next->prev = it->prev;  
  247.     if (it->prev) it->prev->next = it->next;  
  248.     sizes[it->slabs_clsid]--;  
  249.     return;  
  250. }  
  251.   
  252. // 將item放入hashtable和LRU隊列  
  253. int do_item_link(item *it) {  
  254.     MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);  
  255.     assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);  
  256.     it->it_flags |= ITEM_LINKED;    // 設置ITEM_LINKED標誌  
  257.     it->time = current_time;        // 最近訪問時間爲當前時間  
  258.     assoc_insert(it);               // 將item指針插入hash表中  
  259.   
  260.     STATS_LOCK();  
  261.     stats.curr_bytes += ITEM_ntotal(it);  
  262.     stats.curr_items += 1;  
  263.     stats.total_items += 1;  
  264.     STATS_UNLOCK();  
  265.   
  266.     /* Allocate a new CAS ID on link. */  
  267.     ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);// 調用get_cas_id()給item的cas_id賦值  
  268.   
  269.     // 把該item插入LRU隊列(heads, tails, sizes)  
  270.     item_link_q(it);  
  271.   
  272.     return 1;  
  273. }  
  274.   
  275. // 從hashtable及LRU隊列摘除item項  
  276. void do_item_unlink(item *it) {  
  277.     MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);  
  278.     if ((it->it_flags & ITEM_LINKED) != 0)   
  279.     {  
  280.         it->it_flags &= ~ITEM_LINKED;  
  281.         STATS_LOCK();  
  282.         stats.curr_bytes -= ITEM_ntotal(it);  
  283.         stats.curr_items -= 1;  
  284.         STATS_UNLOCK();  
  285.         assoc_delete(ITEM_key(it), it->nkey);// 從hashtable中刪除key對應的item  
  286.         item_unlink_q(it);                   // 從LRU隊列刪除此item  
  287.         if (it->refcount == 0)               // 引用計數爲1時,刪除此item  
  288.             item_free(it);  
  289.     }  
  290. }  
  291. // 減少引用計數refcount,引用計數爲0的時候,就將其釋放  
  292. void do_item_remove(item *it)   
  293. {  
  294.     MEMCACHED_ITEM_REMOVE(ITEM_key(it), it->nkey, it->nbytes);  
  295.     assert((it->it_flags & ITEM_SLABBED) == 0);  
  296.     if (it->refcount != 0)   
  297.     {  
  298.         it->refcount--;         // 減少引用計數  
  299.         DEBUG_REFCNT(it, '-');  
  300.     }  
  301.     if (it->refcount == 0 && (it->it_flags & ITEM_LINKED) == 0)   
  302.     {  
  303.         item_free(it);  
  304.     }  
  305. }  
  306. // 更新item時間戳  
  307. // 先調用item_unlink_q(),更新了時間以後,再調用item_link_q(),  
  308. // 將其重新連接到LRU隊列之中,即讓該item移到LRU隊列的最前  
  309. void do_item_update(item *it) {  
  310.     MEMCACHED_ITEM_UPDATE(ITEM_key(it), it->nkey, it->nbytes);  
  311.     if (it->time < current_time - ITEM_UPDATE_INTERVAL) {  
  312.         assert((it->it_flags & ITEM_SLABBED) == 0);  
  313.   
  314.         if ((it->it_flags & ITEM_LINKED) != 0) {  
  315.             item_unlink_q(it);  
  316.             it->time = current_time;  
  317.             item_link_q(it);  
  318.         }  
  319.     }  
  320. }  
  321. // 調用do_item_unlink()解除原有it的連接,再調用do_item_link()連接到新的new_it  
  322. int do_item_replace(item *it, item *new_it) {  
  323.     MEMCACHED_ITEM_REPLACE(ITEM_key(it), it->nkey, it->nbytes,  
  324.                            ITEM_key(new_it), new_it->nkey, new_it->nbytes);  
  325.     assert((it->it_flags & ITEM_SLABBED) == 0);  
  326.   
  327.     do_item_unlink(it);             // 從hashtable及LRU隊列摘除it  
  328.     return do_item_link(new_it);    // 將new_it插入hashtable和LRU隊列  
  329. }  
  1. /** wrapper around assoc_find which does the lazy expiration logic */  
  2. item *do_item_get(const char *key, const size_t nkey) {  
  3.     item *it = assoc_find(key, nkey);   // 從hashtable查找key  
  4.     int was_found = 0;  
  5.   
  6.     if (settings.verbose > 2) {  
  7.         if (it == NULL) {  
  8.             fprintf(stderr, "> NOT FOUND %s", key);  
  9.         } else {  
  10.             fprintf(stderr, "> FOUND KEY %s", ITEM_key(it));  
  11.             was_found++;  
  12.         }  
  13.     }  
  14.     // 命中,且item存取時間比截止時間早,已失效  
  15.     if (it != NULL && settings.oldest_live != 0 && settings.oldest_live <= current_time &&  
  16.         it->time <= settings.oldest_live) {  
  17.         do_item_unlink(it);           /* MTSAFE - cache_lock held */  
  18.         it = NULL;  
  19.     }  
  20.   
  21.     if (it == NULL && was_found) {  
  22.         fprintf(stderr, " -nuked by flush");  
  23.         was_found--;  
  24.     }  
  25.   
  26.     // 命中,且item已過期  
  27.     if (it != NULL && it->exptime != 0 && it->exptime <= current_time) {  
  28.         do_item_unlink(it);           /* MTSAFE - cache_lock held */  
  29.         it = NULL;  
  30.     }  
  31.   
  32.     if (it == NULL && was_found) {  
  33.         fprintf(stderr, " -nuked by expire");  
  34.         was_found--;  
  35.     }  
  36.   
  37.     if (it != NULL) {  
  38.         it->refcount++;  
  39.         DEBUG_REFCNT(it, '+');  
  40.     }  
  41.   
  42.     if (settings.verbose > 2)  
  43.         fprintf(stderr, "\n");  
  44.   
  45.     return it;  
  46. }  
  47.   
  48. /** returns an item whether or not it's expired. */  
  49. item *do_item_get_nocheck(const char *key, const size_t nkey) {  
  50.     item *it = assoc_find(key, nkey);  
  51.     if (it) {  
  52.         it->refcount++;  
  53.         DEBUG_REFCNT(it, '+');  
  54.     }  
  55.     return it;  
  56. }  
  57.   
  58. /* expires items that are more recent than the oldest_live setting. */// 執行flush操作,即將所有當前有效的item清空(flush)  
  59.  void do_item_flush_expired(void) {  
  60.     int i;  
  61.     item *iter, *next;  
  62.     if (settings.oldest_live == 0)  
  63.         return;  
  64.     for (i = 0; i < LARGEST_ID; i++) {  
  65.         /* The LRU is sorted in decreasing time order, and an item's timestamp 
  66.          * is never newer than its last access time, so we only need to walk 
  67.          * back until we hit an item older than the oldest_live time. 
  68.          * The oldest_live checking will auto-expire the remaining items. 
  69.          */  
  70.         for (iter = heads[i]; iter != NULL; iter = next) {  
  71.             if (iter->time >= settings.oldest_live) {     // 比截止時間新的數據  
  72.                 next = iter->next;  
  73.                 if ((iter->it_flags & ITEM_SLABBED) == 0) {  
  74.                     do_item_unlink(iter);  
  75.                 }  
  76.             } else {  <pre name="code" class="cpp">               // 比截止時間舊的數據已經在之前回收了,</pre> // 由於是按照時間降序排列的(越新越靠前),所以碰到第一個older的數據就停止 /* We've hit the first old item. Continue to the next queue. */ break; } } }}  
  77. <pre></pre>  
  78. <p><span style="font-size:16px">下面的函數都是上層調用的,主要與memcached的協議關係比較緊密,同時保證了臨界資源的互斥訪問(cache_lock),最好提前熟悉一下memcached的<a target="_blank" href="http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt">文本協議</a>。</span></p>  
  79. <p></p>  
  80. <pre name="code" class="html">/* 
  81.  * Allocates a new item. 
  82.  */  
  83. // 新分配一個item結構  
  84. item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes) {  
  85.     item *it;  
  86.     pthread_mutex_lock(&cache_lock);  
  87.   
  88.     // do_item_alloc:新分配一個item結構,首先快速掃描是否存在過期,存在則複用item結構,  
  89.     // 如果沒有,就新分配一個,如果分配失敗,則使用LRU算法進行回收  
  90.     it = do_item_alloc(key, nkey, flags, exptime, nbytes);  
  91.       
  92.     pthread_mutex_unlock(&cache_lock);  
  93.     return it;  
  94. }  
  95.   
  96. /* 
  97.  * Returns an item if it hasn't been marked as expired, 
  98.  * lazy-expiring as needed. 
  99.  */  
  100.  // 查詢key值的item  
  101. item *item_get(const char *key, const size_t nkey) {  
  102.     item *it;  
  103.     pthread_mutex_lock(&cache_lock);  
  104.     it = do_item_get(key, nkey);      // 查詢item,如果成功,這裏會增加引用計數  
  105.     pthread_mutex_unlock(&cache_lock);  
  106.     return it;  
  107. }  
  108. /* 
  109.  * Decrements the reference count on an item and adds it to the freelist if 
  110.  * needed. 
  111.  */  
  112. void item_remove(item *item) {  
  113.     pthread_mutex_lock(&cache_lock);  
  114.     do_item_remove(item);           // 減少item的引用計數,如果降爲0,則釋放  
  115.     pthread_mutex_unlock(&cache_lock);  
  116. }  
  117.   
  118. /* 
  119.  * Links an item into the LRU and hashtable. 
  120.  */  
  121. // 將item放入hashtable和LRU隊列  
  122. int item_link(item *item) {  
  123.     int ret;  
  124.   
  125.     pthread_mutex_lock(&cache_lock);  
  126.     ret = do_item_link(item);  
  127.     pthread_mutex_unlock(&cache_lock);  
  128.     return ret;  
  129. }  
  130. /* 
  131.  * Unlinks an item from the LRU and hashtable. 
  132.  */  
  133. // 從hashtable和LRU隊列去除item  
  134. void item_unlink(item *item) {  
  135.     pthread_mutex_lock(&cache_lock);  
  136.     do_item_unlink(item);  
  137.     pthread_mutex_unlock(&cache_lock);  
  138. }  
  139.   
  140. /* 
  141.  * Replaces one item with another in the hashtable. 
  142.  * Unprotected by a mutex lock since the core server does not require 
  143.  * it to be thread-safe. 
  144.  */  
  145. int item_replace(item *old_it, item *new_it) {  
  146.     // 替換,首先將old_it從hashtable和LRU刪除,再講new_it加入hashtable和LRU  
  147.     return do_item_replace(old_it, new_it);  
  148. }  
  149.   
  150. /* 
  151.  * Moves an item to the back of the LRU queue. 
  152.  */  
  153. void item_update(item *item) {  
  154.     pthread_mutex_lock(&cache_lock);  
  155.     do_item_update(item);  
  156.     pthread_mutex_unlock(&cache_lock);  
  157. }  
  158.   
  159. /* 
  160.  * Stores an item in the cache (high level, obeys set/add/replace semantics) 
  161.  */  
  162. enum store_item_type store_item(item *item, int comm, conn* c) {  
  163.     enum store_item_type ret;  
  164.   
  165.     pthread_mutex_lock(&cache_lock);  
  166.     ret = do_store_item(item, comm, c);  
  167.     pthread_mutex_unlock(&cache_lock);  
  168.     return ret;  
  169. }  
  170.   
  171. /* 
  172.  * Stores an item in the cache according to the semantics of one of the set 
  173.  * commands. In threaded mode, this is protected by the cache lock. 
  174.  * 
  175.  * Returns the state of storage. 
  176.  */  
  177. enum store_item_type do_store_item(item *it, int comm, conn *c) {  
  178.     char *key = ITEM_key(it);  
  179.     item *old_it = do_item_get(key, it->nkey);  // 查詢key值的item  
  180.     enum store_item_type stored = NOT_STORED;  
  181.   
  182.     item *new_it = NULL;  
  183.     int flags;  
  184.   
  185.     if (old_it != NULL && comm == NREAD_ADD) { // 已經存在  
  186.         /* add only adds a nonexistent item, but promote to head of LRU */  
  187.         do_item_update(old_it);  
  188.     } else if (!old_it && (comm == NREAD_REPLACE  
  189.         || comm == NREAD_APPEND || comm == NREAD_PREPEND))  
  190.     {  
  191.         /* replace only replaces an existing value; don't store */  
  192.     } else if (comm == NREAD_CAS) {  
  193.         /* validate cas operation */  
  194.         if(old_it == NULL) {  
  195.             // LRU expired  
  196.             stored = NOT_FOUND;  
  197.             pthread_mutex_lock(&c->thread->stats.mutex);  
  198.             c->thread->stats.cas_misses++;  
  199.             pthread_mutex_unlock(&c->thread->stats.mutex);  
  200.         }  
  201.         else if (ITEM_get_cas(it) == ITEM_get_cas(old_it)) {// cas(check and set)一致時,才操作  
  202.             // cas validates  
  203.             // it and old_it may belong to different classes.  
  204.             // I'm updating the stats for the one that's getting pushed out  
  205.             pthread_mutex_lock(&c->thread->stats.mutex);  
  206.             c->thread->stats.slab_stats[old_it->slabs_clsid].cas_hits++;  
  207.             pthread_mutex_unlock(&c->thread->stats.mutex);  
  208.   
  209.             item_replace(old_it, it);  
  210.             stored = STORED;  
  211.         } else {  
  212.             pthread_mutex_lock(&c->thread->stats.mutex);  
  213.             c->thread->stats.slab_stats[old_it->slabs_clsid].cas_badval++;  
  214.             pthread_mutex_unlock(&c->thread->stats.mutex);  
  215.   
  216.             if(settings.verbose > 1) {  
  217.                 fprintf(stderr, "CAS:  failure: expected %llu, got %llu\n",  
  218.                         (unsigned long long)ITEM_get_cas(old_it),  
  219.                         (unsigned long long)ITEM_get_cas(it));  
  220.             }  
  221.             stored = EXISTS;  
  222.         }  
  223.     } else {  
  224.         /* 
  225.          * Append - combine new and old record into single one. Here it's 
  226.          * atomic and thread-safe. 
  227.          */  
  228.         if (comm == NREAD_APPEND || comm == NREAD_PREPEND) {  
  229.             /* 
  230.              * Validate CAS 
  231.              */  
  232.             if (ITEM_get_cas(it) != 0) {  
  233.                 // CAS much be equal  
  234.                 if (ITEM_get_cas(it) != ITEM_get_cas(old_it)) {  
  235.                     stored = EXISTS;  
  236.                 }  
  237.             }  
  238.   
  239.             if (stored == NOT_STORED) {  
  240.                 /* we have it and old_it here - alloc memory to hold both */  
  241.                 /* flags was already lost - so recover them from ITEM_suffix(it) */  
  242.   
  243.                 flags = (int) strtol(ITEM_suffix(old_it), (char **) NULL, 10);  
  244.   
  245.                 // 新建一個item  
  246.                 new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */);  
  247.   
  248.                 if (new_it == NULL) {  
  249.                     /* SERVER_ERROR out of memory */  
  250.                     if (old_it != NULL)  
  251.                         do_item_remove(old_it);  
  252.   
  253.                     return NOT_STORED;  
  254.                 }  
  255.   
  256.                 /* copy data from it and old_it to new_it */  
  257.   
  258.                 if (comm == NREAD_APPEND) {  
  259.                     memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);  
  260.                     memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes);  
  261.                 } else {  
  262.                     /* NREAD_PREPEND */  
  263.                     memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes);  
  264.                     memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);  
  265.                 }  
  266.   
  267.                 it = new_it;  
  268.             }  
  269.         }  
  270.   
  271.         if (stored == NOT_STORED) {  
  272.             if (old_it != NULL)  
  273.                 item_replace(old_it, it);  
  274.             else  
  275.                 do_item_link(it);  
  276.   
  277.             c->cas = ITEM_get_cas(it);  
  278.   
  279.             stored = STORED;  
  280.         }  
  281.     }  
  282.   
  283.     if (old_it != NULL)  
  284.         do_item_remove(old_it);         /* release our reference */  
  285.     if (new_it != NULL)  
  286.         do_item_remove(new_it);  
  287.   
  288.     if (stored == STORED) {  
  289.         c->cas = ITEM_get_cas(it);  
  290.     }  
  291.   
  292.     return stored;  
  293. }  
  294.   
  295. /* 
  296.  * Flushes expired items after a flush_all call 
  297.  */  
  298. // 執行flush_all操作,清空所有items  
  299. void item_flush_expired() {  
  300.     pthread_mutex_lock(&cache_lock);  
  301.     do_item_flush_expired();  
  302.     pthread_mutex_unlock(&cache_lock);  
  303. }  
  304. </pre>  
  305. <pre></pre>  
總結:感覺items相關的操作並沒有很高的複用價值,這個主要是理解slab和hashtable的接口及使用方法,設計的算法和數據結構並不多,本身邏輯也並不是很複雜。

原載地址:http://blog.csdn.net/tankles/article/details/7048483
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章