memcached(九)--LRU
memcached通過LRU算法(least recently usage)把過期的對象淘汰掉。
簡單點說,每個slab自己就是一個雙向鏈表。熱數(shù)據(jù)在鏈頭,冷數(shù)據(jù)在鏈尾。
- 創(chuàng)建對象的時候,把這個對象放到鏈頭。
- 當創(chuàng)建對象時分配內(nèi)存不足,則把鏈尾的冷數(shù)據(jù)淘汰掉。
- 同一個對象更新會把對象的時間屬性更新。而查詢對象是不會更新時間屬性的!
?
2015-4-19發(fā)布的mc 1.4.23中。對mc的lru進行了優(yōu)化。見:https://code.google.com/p/memcached/wiki/ReleaseNotes1423。官方說明如下:
This release is a reworking of memcached's core LRU algorithm.global cache_lock is gone, LRU's are now independently locked. LRU's are now split between HOT, WARM, and COLD LRU's. New items enter the HOT LRU. LRU updates only happen as items reach the bottom of an LRU. If active in HOT, stay in HOT, if active in WARM, stay in WARM. If active in COLD, move to WARM. HOT/WARM each capped at 32% of memory available for that slab class. COLD is uncapped. Items flow from HOT/WARM into COLD.A background thread exists which shuffles items between/within the LRU's as capacities are reached. The primary goal is to better protect active items from "scanning". items which are never hit again will flow from HOT, through COLD, and out the bottom. Items occasionally active (reaching COLD, but being hit before eviction), move to WARM. There they can stay relatively protected.A secondary goal is to improve latency. The LRU locks are no longer used on item reads, only during sets and from the background thread. Also the background thread is likely to find expired items and release them back to the slab class asynchronously, which speeds up new allocations. Further work on the thread should improve this.
? 通過這樣的策略,我覺得最大的一個區(qū)別是:冷區(qū)數(shù)據(jù)只會跑到溫區(qū)數(shù)據(jù),而不是之前我理解的直接跑到鏈頭熱區(qū)。當這個雙向鏈表數(shù)據(jù)量很大,需要經(jīng)常觸發(fā)lru的時候,可以很好的優(yōu)化lru的性能。
?
代碼的角度說,主要觸發(fā)LRU是在do_item_alloc方法分配創(chuàng)建對象的時候;或者啟動mc的時候增加參數(shù)(lru_maintainer),mc運行時候就會啟動后臺線程定時執(zhí)行l(wèi)ru。不過,最終都是調用lru_pull_tail這個方法實現(xiàn)lru的。增加注釋說明:
/* Returns number of items remove, expired, or evicted.* Callable from worker threads or the LRU maintainer thread* LRU 具體的算法。* 根據(jù)cur_lru參數(shù) 只遍歷某一個區(qū)域(熱、溫、冷區(qū))* LRU策略如果是hot的話不作為;warm的話移動到cold區(qū),cold才是真的淘汰** orig_id slab數(shù)值的id* cur_lru cold warm hot lru* total_chunks 0?* do_evict 是否剔除。cold才是有可能剔除,其他區(qū)域策略都是是false。cold剔除就是淘汰,不剔除就是移動到溫區(qū)* cur_hv hash值* */ static int lru_pull_tail(const int slab_idx, const int cur_lru,const unsigned int total_chunks, const bool do_evict, const uint32_t cur_hv) {item *it = NULL; //臨時變量,記錄有沒有找到要淘汰或者移動到另外一個區(qū)域的對象int slabIdx = slab_idx;int removed = 0;if (slabIdx == 0)return 0;int tries = 5; //下面的外層循環(huán)只跑5次item *search;item *next_it;void *hold_lock = NULL;unsigned int move_to_lru = 0; //移動到哪一個區(qū)域 uint64_t limit;slabIdx |= cur_lru; //第幾個slab+某一種LRU | 運算后,作為一個標記作為鎖的標記。pthread_mutex_lock(&lru_locks[slabIdx]);search = tails[slabIdx]; //某一個區(qū)域中最尾部的元素/* We walk up *only* for locked items, and if bottom is expired. *///------------------------------------------------------------------------------------------------循環(huán)開始for (; tries > 0 && search != NULL; tries--, search=next_it) {/* we might relink search mid-loop, so search->prev isn't reliable */next_it = search->prev;if (search->nbytes == 0 && search->nkey == 0 && search->it_flags == 1) {//這個對象是個爬蟲?/* We are a crawler, ignore it. */tries++;continue;}uint32_t hv = hash(ITEM_key(search), search->nkey);/* Attempt to hash item lock the "search" item. If locked, no* other callers can incr the refcount. Also skip ourselves. */if (hv == cur_hv || (hold_lock = item_trylock(hv)) == NULL) //同一個對象?continue;/* Now see if the item is refcount locked */if (refcount_incr(&search->refcount) != 2) {/* Note pathological case with ref'ed items in tail.* Can still unlink the item, but it won't be reusable yet */itemstats[slabIdx].lrutail_reflocked++;/* In case of refcount leaks, enable for quick workaround. *//* WARNING: This can cause terrible corruption */if (settings.tail_repair_time &&search->time + settings.tail_repair_time < current_time) {itemstats[slabIdx].tailrepairs++;search->refcount = 1;/* This will call item_remove -> item_free since refcnt is 1 */do_item_unlink_nolock(search, hv);item_trylock_unlock(hold_lock);continue;}}/* Expired or flushed 過期或者清空過數(shù)據(jù),刪 */if ((search->exptime != 0 && search->exptime < current_time)|| is_flushed(search)) {itemstats[slabIdx].reclaimed++;if ((search->it_flags & ITEM_FETCHED) == 0) {itemstats[slabIdx].expired_unfetched++;}/* refcnt 2 -> 1 */do_item_unlink_nolock(search, hv);/* refcnt 1 -> 0 -> item_free */do_item_remove(search);item_trylock_unlock(hold_lock);removed++;/* If all we're finding are expired, can keep going */continue;}/* If we're HOT_LRU or WARM_LRU and over size limit, send to COLD_LRU.* If we're COLD_LRU, send to WARM_LRU unless we need to evict* 某個對象已經(jīng)確認, 現(xiàn)在判定在哪一個LRU策略*/switch (cur_lru) {case HOT_LRU:limit = total_chunks * settings.hot_lru_pct / 100; //不淘汰//break;case WARM_LRU:limit = total_chunks * settings.warm_lru_pct / 100; //limit是warm區(qū)可以容納的數(shù)量 超過這個limit就把對象丟到cold區(qū)if (sizes[slabIdx] > limit) {itemstats[slabIdx].moves_to_cold++;move_to_lru = COLD_LRU;do_item_unlink_q(search);it = search;removed++;break;} else if ((search->it_flags & ITEM_ACTIVE) != 0) { /* 更新對象的時間 Only allow ACTIVE relinking if we're not too large. */itemstats[slabIdx].moves_within_lru++;search->it_flags &= ~ITEM_ACTIVE;do_item_update_nolock(search);do_item_remove(search);item_trylock_unlock(hold_lock);} else {/* Don't want to move to COLD, not active, bail out */it = search;}break;case COLD_LRU:it = search; /* 如果是這種策略,一定會在這個策略內(nèi)處理完并且退出的 No matter what, we're stopping */if (do_evict) { /* 淘汰對象 */if (settings.evict_to_free == 0) {/* Don't think we need a counter for this. It'll OOM. */break;}itemstats[slabIdx].evicted++;itemstats[slabIdx].evicted_time = current_time - search->time;if (search->exptime != 0)itemstats[slabIdx].evicted_nonzero++;if ((search->it_flags & ITEM_FETCHED) == 0) {itemstats[slabIdx].evicted_unfetched++;}do_item_unlink_nolock(search, hv);removed++;} else if ((search->it_flags & ITEM_ACTIVE) != 0&& settings.lru_maintainer_thread) {itemstats[slabIdx].moves_to_warm++;search->it_flags &= ~ITEM_ACTIVE;move_to_lru = WARM_LRU;do_item_unlink_q(search);removed++;}break;}if (it != NULL)break;}//------------------------------------------------------------------------------------------------循環(huán)結束 pthread_mutex_unlock(&lru_locks[slabIdx]);if (it != NULL) { //search到一個對象,移動到某個區(qū)域(溫or冷)if (move_to_lru) {it->slabs_clsid = ITEM_clsid(it);it->slabs_clsid |= move_to_lru;item_link_q(it);}do_item_remove(it);item_trylock_unlock(hold_lock);}return removed; }
?
itemstats是統(tǒng)計相關的數(shù)據(jù),與這個邏輯沒太大關系的。
?
?
轉載于:https://www.cnblogs.com/ELMND/p/4631185.html
總結
以上是生活随笔為你收集整理的memcached(九)--LRU的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jQuery EasyUI combob
- 下一篇: html5 css3中的一些笔记