memcached-items操作

        今天主要总结items相关的操作,items的操作分布比较多,定义和实现在memcachd.h/c、thread.h/c、items.h/c都有,感觉完全可以放在items.h/c中。这里就对所有的这些操作(除去stats部分)进行一个简单的总结。

        首先对数据结构、ITEM_*宏和一些变量进行一个简单的说明,这里先建立一个宏观的概念,理解了它们的用途对后续阅读程序有很大的帮助。

[cpp]  view plain copy
  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字节。

[cpp]  view plain copy
  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的状态:

[cpp]  view plain copy
  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的数量:

[cpp]  view plain copy
  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定义为:

[cpp]  view plain copy
  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队列的维护。


        下面贴出代码,并进行相应的注释。 

[cpp]  view plain copy
  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. }  
[cpp]  view plain copy
  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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值