欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品

主頁 > 知識庫 > Redis中LFU算法的深入分析

Redis中LFU算法的深入分析

熱門標(biāo)簽:400電話辦理的口碑 臺灣電銷 b2b外呼系統(tǒng) 一個地圖標(biāo)注多少錢 廊坊外呼系統(tǒng)在哪買 南京手機(jī)外呼系統(tǒng)廠家 高碑店市地圖標(biāo)注app 四川穩(wěn)定外呼系統(tǒng)軟件 地圖標(biāo)注工廠入駐

前言

在Redis中的LRU算法文中說到,LRU有一個缺陷,在如下情況下:

~~~~~A~~~~~A~~~~~A~~~~A~~~~~A~~~~~A~~|
~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~|
~~~~~~~~~~C~~~~~~~~~C~~~~~~~~~C~~~~~~|
~~~~~D~~~~~~~~~~D~~~~~~~~~D~~~~~~~~~D|

會將數(shù)據(jù)D誤認(rèn)為將來最有可能被訪問到的數(shù)據(jù)。

Redis作者曾想改進(jìn)LRU算法,但發(fā)現(xiàn)Redis的LRU算法受制于隨機(jī)采樣數(shù)maxmemory_samples,在maxmemory_samples等于10的情況下已經(jīng)很接近于理想的LRU算法性能,也就是說,LRU算法本身已經(jīng)很難再進(jìn)一步了。

于是,將思路回到原點(diǎn),淘汰算法的本意是保留那些將來最有可能被再次訪問的數(shù)據(jù),而LRU算法只是預(yù)測最近被訪問的數(shù)據(jù)將來最有可能被訪問到。我們可以轉(zhuǎn)變思路,采用一種LFU(Least Frequently Used)算法,也就是最頻繁被訪問的數(shù)據(jù)將來最有可能被訪問到。在上面的情況中,根據(jù)訪問頻繁情況,可以確定保留優(yōu)先級:B>A>C=D。

Redis中的LFU思路

在LFU算法中,可以為每個key維護(hù)一個計數(shù)器。每次key被訪問的時候,計數(shù)器增大。計數(shù)器越大,可以約等于訪問越頻繁。

上述簡單算法存在兩個問題:

  • 在LRU算法中可以維護(hù)一個雙向鏈表,然后簡單的把被訪問的節(jié)點(diǎn)移至鏈表開頭,但在LFU中是不可行的,節(jié)點(diǎn)要嚴(yán)格按照計數(shù)器進(jìn)行排序,新增節(jié)點(diǎn)或者更新節(jié)點(diǎn)位置時,時間復(fù)雜度可能達(dá)到O(N)。
  • 只是簡單的增加計數(shù)器的方法并不完美。訪問模式是會頻繁變化的,一段時間內(nèi)頻繁訪問的key一段時間之后可能會很少被訪問到,只增加計數(shù)器并不能體現(xiàn)這種趨勢。

第一個問題很好解決,可以借鑒LRU實(shí)現(xiàn)的經(jīng)驗(yàn),維護(hù)一個待淘汰key的pool。第二個問題的解決辦法是,記錄key最后一個被訪問的時間,然后隨著時間推移,降低計數(shù)器。

Redis對象的結(jié)構(gòu)如下:

typedef struct redisObject {
  unsigned type:4;
  unsigned encoding:4;
  unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
              * LFU data (least significant 8 bits frequency
              * and most significant 16 bits access time). */
  int refcount;
  void *ptr;
} robj;

在LRU算法中,24 bits的lru是用來記錄LRU time的,在LFU中也可以使用這個字段,不過是分成16 bits與8 bits使用:

      16 bits   8 bits
   +----------------+--------+
   + Last decr time | LOG_C |
   +----------------+--------+

高16 bits用來記錄最近一次計數(shù)器降低的時間ldt,單位是分鐘,低8 bits記錄計數(shù)器數(shù)值counter。

LFU配置

Redis4.0之后為maxmemory_policy淘汰策略添加了兩個LFU模式:

  • volatile-lfu:對有過期時間的key采用LFU淘汰算法
  • allkeys-lfu:對全部key采用LFU淘汰算法

還有2個配置可以調(diào)整LFU算法:

lfu-log-factor 10
lfu-decay-time 1

lfu-log-factor可以調(diào)整計數(shù)器counter的增長速度,lfu-log-factor越大,counter增長的越慢。

lfu-decay-time是一個以分鐘為單位的數(shù)值,可以調(diào)整counter的減少速度

源碼實(shí)現(xiàn)

在lookupKey中:

robj *lookupKey(redisDb *db, robj *key, int flags) {
  dictEntry *de = dictFind(db->dict,key->ptr);
  if (de) {
    robj *val = dictGetVal(de);

    /* Update the access time for the ageing algorithm.
     * Don't do it if we have a saving child, as this will trigger
     * a copy on write madness. */
    if (server.rdb_child_pid == -1 
      server.aof_child_pid == -1 
      !(flags  LOOKUP_NOTOUCH))
    {
      if (server.maxmemory_policy  MAXMEMORY_FLAG_LFU) {
        updateLFU(val);
      } else {
        val->lru = LRU_CLOCK();
      }
    }
    return val;
  } else {
    return NULL;
  }
}

當(dāng)采用LFU策略時,updateLFU更新lru:

/* Update LFU when an object is accessed.
 * Firstly, decrement the counter if the decrement time is reached.
 * Then logarithmically increment the counter, and update the access time. */
void updateLFU(robj *val) {
  unsigned long counter = LFUDecrAndReturn(val);
  counter = LFULogIncr(counter);
  val->lru = (LFUGetTimeInMinutes()8) | counter;
}

降低LFUDecrAndReturn

首先,LFUDecrAndReturn對counter進(jìn)行減少操作:

/* If the object decrement time is reached decrement the LFU counter but
 * do not update LFU fields of the object, we update the access time
 * and counter in an explicit way when the object is really accessed.
 * And we will times halve the counter according to the times of
 * elapsed time than server.lfu_decay_time.
 * Return the object frequency counter.
 *
 * This function is used in order to scan the dataset for the best object
 * to fit: as we check for the candidate, we incrementally decrement the
 * counter of the scanned objects if needed. */
unsigned long LFUDecrAndReturn(robj *o) {
  unsigned long ldt = o->lru >> 8;
  unsigned long counter = o->lru  255;
  unsigned long num_periods = server.lfu_decay_time ? LFUTimeElapsed(ldt) / server.lfu_decay_time : 0;
  if (num_periods)
    counter = (num_periods > counter) ? 0 : counter - num_periods;
  return counter;
}

函數(shù)首先取得高16 bits的最近降低時間ldt與低8 bits的計數(shù)器counter,然后根據(jù)配置的lfu_decay_time計算應(yīng)該降低多少。

LFUTimeElapsed用來計算當(dāng)前時間與ldt的差值:

/* Return the current time in minutes, just taking the least significant
 * 16 bits. The returned time is suitable to be stored as LDT (last decrement
 * time) for the LFU implementation. */
unsigned long LFUGetTimeInMinutes(void) {
  return (server.unixtime/60)  65535;
}

/* Given an object last access time, compute the minimum number of minutes
 * that elapsed since the last access. Handle overflow (ldt greater than
 * the current 16 bits minutes time) considering the time as wrapping
 * exactly once. */
unsigned long LFUTimeElapsed(unsigned long ldt) {
  unsigned long now = LFUGetTimeInMinutes();
  if (now >= ldt) return now-ldt;
  return 65535-ldt+now;
}

具體是當(dāng)前時間轉(zhuǎn)化成分鐘數(shù)后取低16 bits,然后計算與ldt的差值now-ldt。當(dāng)ldt > now時,默認(rèn)為過了一個周期(16 bits,最大65535),取值65535-ldt+now。

然后用差值與配置lfu_decay_time相除,LFUTimeElapsed(ldt) / server.lfu_decay_time,已過去n個lfu_decay_time,則將counter減少n,counter - num_periods。

增長LFULogIncr

增長函數(shù)LFULogIncr如下:

/* Logarithmically increment a counter. The greater is the current counter value
 * the less likely is that it gets really implemented. Saturate it at 255. */
uint8_t LFULogIncr(uint8_t counter) {
  if (counter == 255) return 255;
  double r = (double)rand()/RAND_MAX;
  double baseval = counter - LFU_INIT_VAL;
  if (baseval  0) baseval = 0;
  double p = 1.0/(baseval*server.lfu_log_factor+1);
  if (r  p) counter++;
  return counter;
}

counter并不是簡單的訪問一次就+1,而是采用了一個0-1之間的p因子控制增長。counter最大值為255。取一個0-1之間的隨機(jī)數(shù)r與p比較,當(dāng)rp時,才增加counter,這和比特幣中控制產(chǎn)出的策略類似。p取決于當(dāng)前counter值與lfu_log_factor因子,counter值與lfu_log_factor因子越大,p越小,rp的概率也越小,counter增長的概率也就越小。增長情況如下:

+--------+------------+------------+------------+------------+------------+
| factor | 100 hits   | 1000 hits  | 100K hits  | 1M hits    | 10M hits   |
+--------+------------+------------+------------+------------+------------+
| 0      | 104        | 255        | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 1      | 18         | 49         | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 10     | 10         | 18         | 142        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 100    | 8          | 11         | 49         | 143        | 255        |
+--------+------------+------------+------------+------------+------------+

可見counter增長與訪問次數(shù)呈現(xiàn)對數(shù)增長的趨勢,隨著訪問次數(shù)越來越大,counter增長的越來越慢。

新生key策略

另外一個問題是,當(dāng)創(chuàng)建新對象的時候,對象的counter如果為0,很容易就會被淘汰掉,還需要為新生key設(shè)置一個初始counter,createObject:

robj *createObject(int type, void *ptr) {
  robj *o = zmalloc(sizeof(*o));
  o->type = type;
  o->encoding = OBJ_ENCODING_RAW;
  o->ptr = ptr;
  o->refcount = 1;

  /* Set the LRU to the current lruclock (minutes resolution), or
   * alternatively the LFU counter. */
  if (server.maxmemory_policy  MAXMEMORY_FLAG_LFU) {
    o->lru = (LFUGetTimeInMinutes()8) | LFU_INIT_VAL;
  } else {
    o->lru = LRU_CLOCK();
  }
  return o;
}

counter會被初始化為LFU_INIT_VAL,默認(rèn)5。

pool

pool算法就與LRU算法一致了:

    if (server.maxmemory_policy  (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) ||
      server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL)

計算idle時有所不同:

    } else if (server.maxmemory_policy  MAXMEMORY_FLAG_LFU) {
      /* When we use an LRU policy, we sort the keys by idle time
       * so that we expire keys starting from greater idle time.
       * However when the policy is an LFU one, we have a frequency
       * estimation, and we want to evict keys with lower frequency
       * first. So inside the pool we put objects using the inverted
       * frequency subtracting the actual frequency to the maximum
       * frequency of 255. */
      idle = 255-LFUDecrAndReturn(o);

使用了255-LFUDecrAndReturn(o)當(dāng)做排序的依據(jù)。

參考鏈接

  • Random notes on improving the Redis LRU algorithm
  • Using Redis as an LRU cache

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。

您可能感興趣的文章:
  • C++ 實(shí)現(xiàn)LRU 與 LFU 的緩存算法
  • C++泛型編程基本概念詳解
  • C++算法與泛型算法(algorithm、numeric)
  • C++ 泛型編程詳解
  • C++實(shí)現(xiàn)支持泛型的LFU詳解

標(biāo)簽:伊春 南寧 定州 河源 泰州 拉薩 甘南 畢節(jié)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Redis中LFU算法的深入分析》,本文關(guān)鍵詞  Redis,中,LFU,算法,的,深入分析,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Redis中LFU算法的深入分析》相關(guān)的同類信息!
  • 本頁收集關(guān)于Redis中LFU算法的深入分析的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品
  • <rt id="w000q"><acronym id="w000q"></acronym></rt>
  • <abbr id="w000q"></abbr>
    <rt id="w000q"></rt>
    亚洲国产精品久久久男人的天堂| 97精品国产97久久久久久久久久久久| 人妻激情偷乱频一区二区三区| 91成人国产精品| 国产精品福利一区| 成人性色生活片| 人妻人人澡人人添人人爽| 中文字幕电影一区| 国产精品69久久久久水密桃| 日本高清黄色片| 欧美国产激情二区三区| 国产白丝精品91爽爽久久| 顶级黑人搡bbw搡bbbb搡| 国产精品视频一二三区| 成人激情视频网站| 色八戒一区二区三区| 一区二区三区在线视频免费| 久久久久亚洲av无码麻豆| 欧美无乱码久久久免费午夜一区| 一区二区三区美女| yjizz视频| 日韩欧美国产精品| 激情综合色丁香一区二区| 日本在线观看网址| 综合激情成人伊人| 国产精品99精品无码视亚| 欧美日韩国产综合草草| 视频一区二区欧美| 无码人妻aⅴ一区二区三区69岛| 国产色91在线| av一本久道久久综合久久鬼色| 欧美视频在线一区二区三区 | 国产人妻精品午夜福利免费| 欧美日韩夫妻久久| 人妖欧美一区二区| 337人体粉嫩噜噜噜| 亚洲婷婷国产精品电影人久久| 欧美色图校园春色| 日韩女优制服丝袜电影| 国产精品18久久久久久久久 | 久久久久久久久久97| 中文字幕一区二区5566日韩| 日本少妇一级片| 精品国产免费人成电影在线观看四季 | 日精品一区二区三区| 特级西西www444人体聚色| 国产精品卡一卡二卡三| 亚洲一区和二区| 欧美精品一区二区三区久久久| 国产成人在线影院 | 欧美午夜影院一区| 免费视频一区二区| 一区二区在线观看免费视频| 亚洲国产aⅴ成人精品无吗| 欧洲av一区二区三区| 中文字幕永久在线不卡| 欧美在线一级片| 中文字幕精品一区| 国产激情视频网站| 国产精品欧美久久久久无广告 | 91麻豆蜜桃一区二区三区| 日韩欧美国产系列| 成人动漫一区二区三区| 欧美一级理论性理论a| 国产91高潮流白浆在线麻豆 | 日本一区二区免费在线观看视频| 成年人性生活视频| 精品国产露脸精彩对白| 99久久精品免费观看| 亚洲精品在线免费播放| 91看片淫黄大片一级| 久久久精品免费网站| 宇都宫紫苑在线播放| 国产亚洲人成网站| 好吊色视频一区二区三区| 国产精品免费av| 国产精品成人一区二区三区电影毛片| 亚洲色图.com| 亚洲一二三四五六区| 日韩电影免费在线| 色屁屁一区二区| 国产乱一区二区| 日韩欧美中文一区二区| 少妇性l交大片7724com| 国产欧美一区二区在线观看| 成人手机在线免费视频| 一区二区三区不卡视频在线观看| 少妇愉情理伦三级| 日韩国产成人精品| 欧美日韩一区小说| 成人性生交大片免费看在线播放| 精品国产乱码久久久久久蜜臀| www.欧美com| 中文字幕一区二区5566日韩| 中文字幕黄色网址| 奇米影视7777精品一区二区| 欧美日韩一区视频| 99久久免费视频.com| 国产精品污网站| 亚洲最大成人综合网| 日韩高清国产一区在线| 欧美另类z0zxhd电影| 99久久er热在这里只有精品66| 国产女同互慰高潮91漫画| 蜜桃av噜噜一区二区三区小说| 欧美无人高清视频在线观看| 亚洲最大成人综合| 欧美综合欧美视频| eeuss影院一区二区三区| 国产精品久久综合| 天天色天天综合| 国产精品白丝jk黑袜喷水| 2020国产精品自拍| 91成人在线免费视频| 免费久久99精品国产| 日韩一区二区电影网| www.日本高清| 丝袜美腿亚洲一区| 欧美一区二区三区视频在线| 欧美夫妇交换xxx| 五月婷婷综合网| 欧美一区二区三区色| 中文字幕第3页| 日韩精品一级二级 | 欧美日韩精品高清| 亚洲国产精品狼友在线观看| 一卡二卡三卡日韩欧美| 欧美三日本三级三级在线播放| 老女人性生活视频| 亚洲一区二区精品3399| 欧美久久久久免费| 亚洲av无码一区二区三区观看| 天天做天天摸天天爽国产一区| 这里只有精品免费| 99久久免费看精品国产一区| 日本午夜一本久久久综合| 精品免费国产二区三区| 国产高潮呻吟久久| 国产精品77777竹菊影视小说| 国产精品天美传媒| 色综合久久久久网| 国内自拍偷拍视频| 蜜臀av性久久久久蜜臀av麻豆| 欧美不卡一区二区三区| 一级黄色片网址| 成人性生交大片免费看中文| 亚洲伦在线观看| 7777精品伊人久久久大香线蕉最新版 | 国产精品一线二线三线精华| 国产精品婷婷午夜在线观看| 色婷婷久久久久swag精品| 亚洲精品无码久久久久久久| 婷婷开心久久网| 久久午夜色播影院免费高清| 亚洲综合网在线| 美女露出粉嫩尿囗让男人桶| 日韩成人午夜精品| 国产无人区一区二区三区| 破处女黄色一级片| 中文字幕人妻一区| 久久精品国产第一区二区三区| 国产欧美一区二区三区鸳鸯浴| 一本一道久久a久久精品| 国产精品扒开腿做爽爽爽a片唱戏 亚洲av成人精品一区二区三区 | 亚洲免费视频中文字幕| 91精品国产全国免费观看| 蜜桃久久精品成人无码av| 成人av资源站| 午夜激情一区二区三区| 久久精品综合网| 欧美在线影院一区二区| 人人妻人人藻人人爽欧美一区| 高清国产一区二区| 香蕉av福利精品导航| 久久久噜噜噜久久人人看| 欧美在线免费观看亚洲| 欧美激情aaa| 91视频免费观看| 久久国产三级精品| 亚洲美女电影在线| 精品国产乱码久久久久久闺蜜| 动漫性做爰视频| 巨胸大乳www视频免费观看| 成人免费观看视频| 日韩高清在线一区| 亚洲欧洲三级电影| 日韩免费高清av| 91国模大尺度私拍在线视频| xxxxx在线观看| 91视视频在线直接观看在线看网页在线看| 免费观看一级特黄欧美大片| 国产精品久久免费看| 欧美日韩精品欧美日韩精品一| 在线免费看视频| 亚洲天堂2024| av一二三不卡影片| 国内精品国产三级国产a久久| 一区二区久久久久| 国产日韩精品一区| 欧美一级在线观看|