<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官問:Redis 內(nèi)存滿了怎么辦?我想不到!

          共 4939字,需瀏覽 10分鐘

           ·

          2021-08-19 12:19

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          Redis是基于內(nèi)存的key-value數(shù)據(jù)庫,因?yàn)橄到y(tǒng)的內(nèi)存大小有限,所以我們?cè)谑褂肦edis的時(shí)候可以配置Redis能使用的最大的內(nèi)存大小。


          1、通過配置文件配置


          通過在Redis安裝目錄下面的redis.conf配置文件中添加以下配置設(shè)置內(nèi)存大小

          1. //設(shè)置Redis最大占用內(nèi)存大小為100M

          2. maxmemory 100mb

          3. 復(fù)制代碼

          redis的配置文件不一定使用的是安裝目錄下面的redis.conf文件,啟動(dòng)redis服務(wù)的時(shí)候是可以傳一個(gè)參數(shù)指定redis的配置文件的

          2、通過命令修改


          Redis支持運(yùn)行時(shí)通過命令動(dòng)態(tài)修改內(nèi)存大小

          1. //設(shè)置Redis最大占用內(nèi)存大小為100M

          2. 127.0.0.1:6379> config set maxmemory 100mb

          3. //獲取設(shè)置的Redis能使用的最大內(nèi)存大小

          4. 127.0.0.1:6379> config get maxmemory

          5. 復(fù)制代碼

          如果不設(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)存就有用完的時(shí)候。那在內(nèi)存用完的時(shí)候,還繼續(xù)往Redis里面添加數(shù)據(jù)不就沒內(nèi)存可用了嗎?


          實(shí)際上Redis定義了幾種策略用來處理這種情況:


          noeviction(默認(rèn)策略):對(duì)于寫請(qǐng)求不再提供服務(wù),直接返回錯(cuò)誤(DEL請(qǐng)求和部分特殊請(qǐng)求除外)


          allkeys-lru:從所有key中使用LRU算法進(jìn)行淘汰


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


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


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


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

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

          如何獲取及設(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算法進(jìn)行內(nèi)存淘汰的,那么什么是LRU算法呢?

          LRU(Least Recently Used),即最近最少使用,是一種緩存置換算法。在使用內(nèi)存作為緩存的時(shí)候,緩存的大小一般是固定的。當(dāng)緩存被占滿,這個(gè)時(shí)候繼續(xù)往緩存里面添加數(shù)據(jù),就需要淘汰一部分老的數(shù)據(jù),釋放內(nèi)存空間用來存儲(chǔ)新的數(shù)據(jù)。這個(gè)時(shí)候就可以使用LRU算法了。其核心思想是:如果一個(gè)數(shù)據(jù)在最近一段時(shí)間沒有被用到,那么將來被使用到的可能性也很小,所以就可以被淘汰掉。

          使用java實(shí)現(xiàn)一個(gè)簡單的LRU算法
          public class LRUCache<k, v> {//容量private int capacity;//當(dāng)前有多少節(jié)點(diǎn)的統(tǒng)計(jì)private int count;//緩存節(jié)點(diǎn)private Map<k, Node<k, v>> nodeMap;private Node<k, v> head;private Node<k, v> tail;

          public LRUCache(int capacity) {if (capacity < 1) {throw new IllegalArgumentException(String.valueOf(capacity));}this.capacity = capacity;this.nodeMap = new HashMap<>();//初始化頭節(jié)點(diǎn)和尾節(jié)點(diǎn),利用哨兵模式減少判斷頭結(jié)點(diǎn)和尾節(jié)點(diǎn)為空的代碼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<k, v> node = nodeMap.get(key);if (node == null) {if (count >= capacity) {//先移除一個(gè)節(jié)點(diǎn) removeNode();} node = new Node<>(key, value);//添加節(jié)點(diǎn) addNode(node);} else {//移動(dòng)節(jié)點(diǎn)到頭節(jié)點(diǎn) moveNodeToHead(node);}}

          public Node<k, v> get(k key) {Node<k, v> 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<k, v> 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<k, v> node) {//添加節(jié)點(diǎn)到頭部 addToHead(node); nodeMap.put(node.key, node); count++;}

          private void addToHead(Node<k, v> node) {Node next = head.next;next.pre = node; node.next = next; node.pre = head; head.next = node;}

          public void moveNodeToHead(Node<k, v> node) {//從鏈表里面移除 removeFromList(node);//添加節(jié)點(diǎn)到頭部 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;}}}

          上面這段代碼實(shí)現(xiàn)了一個(gè)簡單的LUR算法,代碼很簡單,也加了注釋,仔細(xì)看一下很容易就看懂。

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


          近似LRU算法


          Redis使用的是近似LRU算法,它跟常規(guī)的LRU算法還不太一樣。近似LRU算法通過隨機(jī)采樣法淘汰數(shù)據(jù),每次隨機(jī)出5(默認(rèn))個(gè)key,從里面淘汰掉最近最少使用的key。

          可以通過maxmemory-samples參數(shù)修改采樣數(shù)量:例:maxmemory-samples 10 maxmenory-samples配置的越大,淘汰的結(jié)果越接近于嚴(yán)格的LRU算法

          Redis為了實(shí)現(xiàn)近似LRU算法,給每個(gè)key增加了一個(gè)額外增加了一個(gè)24bit的字段,用來存儲(chǔ)該key最后一次被訪問的時(shí)間。


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


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


          當(dāng)需要淘汰的時(shí)候,則直接從池中選取最近訪問時(shí)間最小(最久沒被訪問)的key淘汰掉就行。


          LRU算法的對(duì)比


          我們可以通過一個(gè)實(shí)驗(yàn)對(duì)比各LRU算法的準(zhǔn)確率,先往Redis里面添加一定數(shù)量的數(shù)據(jù)n,使Redis可用內(nèi)存用完,再往Redis里面添加n/2的新數(shù)據(jù),這個(gè)時(shí)候就需要淘汰掉一部分的數(shù)據(jù),如果按照嚴(yán)格的LRU算法,應(yīng)該淘汰掉的是最先加入的n/2的數(shù)據(jù)。生成如下各LRU算法的對(duì)比圖



          你可以看到圖中有三種不同顏色的點(diǎn):


          • 淺灰色是被淘汰的數(shù)據(jù)

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

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


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


          LFU算法


          LFU算法是Redis4.0里面新加的一種淘汰策略。它的全稱是Least Frequently Used,它的核心思想是根據(jù)key的最近被訪問的頻率進(jìn)行淘汰,很少被訪問的優(yōu)先被淘汰,被訪問的多的則被留下來。


          LFU算法能更好的表示一個(gè)key被訪問的熱度。假如你使用的是LRU算法,一個(gè)key很久沒有被訪問到,只剛剛是偶爾被訪問了一次,那么它就被認(rèn)為是熱點(diǎn)數(shù)據(jù),不會(huì)被淘汰,而有些key將來是很有可能被訪問到的則被淘汰了。如果使用LFU算法則不會(huì)出現(xiàn)這種情況,因?yàn)槭褂靡淮尾⒉粫?huì)使一個(gè)key成為熱點(diǎn)數(shù)據(jù)。


          LFU一共有兩種策略:


          • volatile-lfu:在設(shè)置了過期時(shí)間的key中使用LFU算法淘汰key

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

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


          問題


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

          來源:https://tinyurl.com/y6qctca6

          1、 19 張圖讓你秒懂 Spring Cloud 全家桶!

          2、 10 款牛哄哄的 Chrome 插件,你用了幾個(gè)?

          3、 在 IntelliJ IDEA 中這樣使用 Git,賊方便了!

          4、計(jì)算機(jī)時(shí)間到底是怎么來的?程序員必看的時(shí)間知識(shí)!

          5、這些IDEA的優(yōu)化設(shè)置趕緊安排起來,效率提升杠杠的!

          6、和100位00后聊完,我明白了為什么還有6億中國人仍不放棄QQ

          7、真香!用 IDEA 神器看源碼,效率真高!

          點(diǎn)分享 

          點(diǎn)收藏 

          點(diǎn)點(diǎn)贊 

          點(diǎn)在看

          瀏覽 16
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91久久香蕉国产熟女线看麻豆 | 91中文字幕 | 狠狠躁夜夜躁 | 日日色综合| 插久久|