1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【93期】經(jīng)典面試題:Redis 內(nèi)存滿了怎么辦?

        共 4688字,需瀏覽 10分鐘

         ·

        2020-11-21 04:24

        程序員的成長之路
        互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
        關(guān)注


        閱讀本文大概需要 6 分鐘。

        來自:juejin.im/post/5d674ac2e51d4557ca7fdd70

        Redis占用內(nèi)存大小

        我們知道Redis是基于內(nèi)存的key-value數(shù)據(jù)庫,因為系統(tǒng)的內(nèi)存大小有限,所以我們在使用Redis的時候可以配置Redis能使用的最大的內(nèi)存大小。

        1、通過配置文件配置

        通過在Redis安裝目錄下面的redis.conf配置文件中添加以下配置設(shè)置內(nèi)存大小
        //設(shè)置Redis最大占用內(nèi)存大小為100M
        maxmemory?100mb
        redis的配置文件不一定使用的是安裝目錄下面的redis.conf文件,啟動redis服務(wù)的時候是可以傳一個參數(shù)指定redis的配置文件的

        2、通過命令修改

        Redis支持運行時通過命令動態(tài)修改內(nèi)存大小
        //設(shè)置Redis最大占用內(nèi)存大小為100M
        127.0.0.1:6379>?config?set?maxmemory?100mb
        //獲取設(shè)置的Redis能使用的最大內(nèi)存大小
        127.0.0.1:6379>?config?get?maxmemory
        如果不設(shè)置最大內(nèi)存大小或者設(shè)置最大內(nèi)存大小為0,在64位操作系統(tǒng)下不限制內(nèi)存大小,在32位操作系統(tǒng)下最多使用3GB內(nèi)存

        Redis的內(nèi)存淘汰

        既然可以設(shè)置Redis最大占用內(nèi)存大小,那么配置的內(nèi)存就有用完的時候。那在內(nèi)存用完的時候,還繼續(xù)往Redis里面添加數(shù)據(jù)不就沒內(nèi)存可用了嗎?
        實際上Redis定義了幾種策略用來處理這種情況:
        • noeviction(默認策略):對于寫請求不再提供服務(wù),直接返回錯誤(DEL請求和部分特殊請求除外)

        • allkeys-lru:從所有key中使用LRU算法進行淘汰

        • volatile-lru:從設(shè)置了過期時間的key中使用LRU算法進行淘汰

        • allkeys-random:從所有key中隨機淘汰數(shù)據(jù)

        • volatile-random:從設(shè)置了過期時間的key中隨機淘汰

        • volatile-ttl:在設(shè)置了過期時間的key中,根據(jù)key的過期時間進行淘汰,越早過期的越優(yōu)先被淘汰

        當(dāng)使用volatile-lru、volatile-random、volatile-ttl這三種策略時,如果沒有key可以被淘汰,則和noeviction一樣返回錯誤

        如何獲取及設(shè)置內(nèi)存淘汰策略

        獲取當(dāng)前內(nèi)存淘汰策略:
        127.0.0.1:6379>?config?get?maxmemory-policy
        通過配置文件設(shè)置淘汰策略(修改redis.conf文件):
        maxmemory-policy?allkeys-lru
        通過命令修改淘汰策略:
        127.0.0.1:6379>?config?set?maxmemory-policy?allkeys-lru

        LRU算法

        什么是LRU?

        上面說到了Redis可使用最大內(nèi)存使用完了,是可以使用LRU算法進行內(nèi)存淘汰的,那么什么是LRU算法呢?
        LRU(Least Recently Used),即最近最少使用,是一種緩存置換算法。在使用內(nèi)存作為緩存的時候,緩存的大小一般是固定的。當(dāng)緩存被占滿,這個時候繼續(xù)往緩存里面添加數(shù)據(jù),就需要淘汰一部分老的數(shù)據(jù),釋放內(nèi)存空間用來存儲新的數(shù)據(jù)。
        這個時候就可以使用LRU算法了。其核心思想是:如果一個數(shù)據(jù)在最近一段時間沒有被用到,那么將來被使用到的可能性也很小,所以就可以被淘汰掉。
        使用java實現(xiàn)一個簡單的LRU算法
        public?class?LRUCache<k,?v>?{
        ????//容量
        ????private?int?capacity;
        ????//當(dāng)前有多少節(jié)點的統(tǒng)計
        ????private?int?count;
        ????//緩存節(jié)點
        ????private?Map>?nodeMap;
        ????private?Node?head;
        ????private?Node?tail;

        ????public?LRUCache(int?capacity)?{
        ????????if?(capacity?1)?{
        ????????????throw?new?IllegalArgumentException(String.valueOf(capacity));
        ????????}
        ????????this.capacity?=?capacity;
        ????????this.nodeMap?=?new?HashMap<>();
        ????????//初始化頭節(jié)點和尾節(jié)點,利用哨兵模式減少判斷頭結(jié)點和尾節(jié)點為空的代碼
        ????????Node?headNode?=?new?Node(null,?null);
        ????????Node?tailNode?=?new?Node(null,?null);
        ????????headNode.next?=?tailNode;
        ????????tailNode.pre?=?headNode;
        ????????this.head?=?headNode;
        ????????this.tail?=?tailNode;
        ????}

        ????public?void?put(k?key,?v?value)?{
        ????????Node?node?=?nodeMap.get(key);
        ????????if?(node?==?null)?{
        ????????????if?(count?>=?capacity)?{
        ????????????????//先移除一個節(jié)點
        ????????????????removeNode();
        ????????????}
        ????????????node?=?new?Node<>(key,?value);
        ????????????//添加節(jié)點
        ????????????addNode(node);
        ????????}?else?{
        ????????????//移動節(jié)點到頭節(jié)點
        ????????????moveNodeToHead(node);
        ????????}
        ????}

        ????public?Node?get(k?key)?{
        ????????Node?node?=?nodeMap.get(key);
        ????????if?(node?!=?null)?{
        ????????????moveNodeToHead(node);
        ????????}
        ????????return?node;
        ????}

        ????private?void?removeNode()?{
        ????????Node?node?=?tail.pre;
        ????????//從鏈表里面移除
        ????????removeFromList(node);
        ????????nodeMap.remove(node.key);
        ????????count--;
        ????}

        ????private?void?removeFromList(Node?node)?{
        ????????Node?pre?=?node.pre;
        ????????Node?next?=?node.next;

        ????????pre.next?=?next;
        ????????next.pre?=?pre;

        ????????node.next?=?null;
        ????????node.pre?=?null;
        ????}

        ????private?void?addNode(Node?node)?{
        ????????//添加節(jié)點到頭部
        ????????addToHead(node);
        ????????nodeMap.put(node.key,?node);
        ????????count++;
        ????}

        ????private?void?addToHead(Node?node)?{
        ????????Node?next?=?head.next;
        ????????next.pre?=?node;
        ????????node.next?=?next;
        ????????node.pre?=?head;
        ????????head.next?=?node;
        ????}

        ????public?void?moveNodeToHead(Node?node)?{
        ????????//從鏈表里面移除
        ????????removeFromList(node);
        ????????//添加節(jié)點到頭部
        ????????addToHead(node);
        ????}

        ????class?Node<k,?v>?{
        ????????k?key;
        ????????v?value;
        ????????Node?pre;
        ????????Node?next;

        ????????public?Node(k?key,?v?value)?{
        ????????????this.key?=?key;
        ????????????this.value?=?value;
        ????????}
        ????}
        }
        上面這段代碼實現(xiàn)了一個簡單的LUR算法,代碼很簡單,也加了注釋,仔細看一下很容易就看懂。

        LRU在Redis中的實現(xiàn)

        近似LRU算法

        Redis使用的是近似LRU算法,它跟常規(guī)的LRU算法還不太一樣。近似LRU算法通過隨機采樣法淘汰數(shù)據(jù),每次隨機出5(默認)個key,從里面淘汰掉最近最少使用的key。
        可以通過maxmemory-samples參數(shù)修改采樣數(shù)量:
        例:maxmemory-samples 10
        maxmenory-samples配置的越大,淘汰的結(jié)果越接近于嚴(yán)格的LRU算法
        Redis為了實現(xiàn)近似LRU算法,給每個key增加了一個額外增加了一個24bit的字段,用來存儲該key最后一次被訪問的時間。

        Redis3.0對近似LRU的優(yōu)化

        Redis3.0對近似LRU算法進行了一些優(yōu)化。新算法會維護一個候選池(大小為16),池中的數(shù)據(jù)根據(jù)訪問時間進行排序,第一次隨機選取的key都會放入池中,隨后每次隨機選取的key只有在訪問時間小于池中最小的時間才會放入池中,直到候選池被放滿。當(dāng)放滿后,如果有新的key需要放入,則將池中最后訪問時間最大(最近被訪問)的移除。
        當(dāng)需要淘汰的時候,則直接從池中選取最近訪問時間最小(最久沒被訪問)的key淘汰掉就行。

        LRU算法的對比

        我們可以通過一個實驗對比各LRU算法的準(zhǔn)確率,先往Redis里面添加一定數(shù)量的數(shù)據(jù)n,使Redis可用內(nèi)存用完,再往Redis里面添加n/2的新數(shù)據(jù),這個時候就需要淘汰掉一部分的數(shù)據(jù),如果按照嚴(yán)格的LRU算法,應(yīng)該淘汰掉的是最先加入的n/2的數(shù)據(jù)。
        生成如下各LRU算法的對比圖
        圖片來源:segmentfault.com/a/1190000017555834
        你可以看到圖中有三種不同顏色的點:
        • 淺灰色是被淘汰的數(shù)據(jù)

        • 灰色是沒有被淘汰掉的老數(shù)據(jù)

        • 綠色是新加入的數(shù)據(jù)

        我們能看到Redis3.0采樣數(shù)是10生成的圖最接近于嚴(yán)格的LRU。而同樣使用5個采樣數(shù),Redis3.0也要優(yōu)于Redis2.8。

        LFU算法

        LFU算法是Redis4.0里面新加的一種淘汰策略。它的全稱是Least Frequently Used,它的核心思想是根據(jù)key的最近被訪問的頻率進行淘汰,很少被訪問的優(yōu)先被淘汰,被訪問的多的則被留下來。
        LFU算法能更好的表示一個key被訪問的熱度。假如你使用的是LRU算法,一個key很久沒有被訪問到,只剛剛是偶爾被訪問了一次,那么它就被認為是熱點數(shù)據(jù),不會被淘汰,而有些key將來是很有可能被訪問到的則被淘汰了。如果使用LFU算法則不會出現(xiàn)這種情況,因為使用一次并不會使一個key成為熱點數(shù)據(jù)。
        LFU一共有兩種策略:
        • volatile-lfu:在設(shè)置了過期時間的key中使用LFU算法淘汰key

        • allkeys-lfu:在所有的key中使用LFU算法淘汰數(shù)據(jù)

        設(shè)置使用這兩種淘汰策略跟前面講的一樣,不過要注意的一點是這兩周策略只能在Redis4.0及以上設(shè)置,如果在Redis4.0以下設(shè)置會報錯

        問題

        最后留一個小問題,可能有的人注意到了,我在文中并沒有解釋為什么Redis使用近似LRU算法而不使用準(zhǔn)確的LRU算法,可以在評論區(qū)給出你的答案,大家一起討論學(xué)習(xí)。

        推薦閱讀:

        【92期】面試官:你說你精通Java并發(fā),那給我講講J.U.C吧

        【91期】面試官:Spring 用了哪些設(shè)計模式?說三種即可

        【90期】面試官:說一下使用 Redis 實現(xiàn)大規(guī)模的帖子瀏覽計數(shù)的思路

        5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復(fù)「2048」,即可免費獲?。?!

        微信掃描二維碼,關(guān)注我的公眾號

        朕已閱?

        瀏覽 37
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产人成无码视频在线观看 | 人人搞人人操 | 大尺度做爰啪啪高潮床戏电视剧 | 性欧美孕交 | 曰比视频 | 18片毛片60分钟免费 | 在线观看成人毛片 | www.九色 | 一道本无码在线视频 | 美女调教网站 |