欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Redis內(nèi)存回收策略

 更新時(shí)間:2023年06月06日 09:08:09   作者:小盒子的技術(shù)分享  
本文主要介紹了淺談Redis內(nèi)存回收策略,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

Redis的內(nèi)存回收機(jī)制主要體現(xiàn)在以下兩個(gè)方面:

  • 刪除到達(dá)過(guò)期時(shí)間的鍵對(duì)象。
  • 內(nèi)存使用達(dá)到maxmemory上限時(shí)觸發(fā)內(nèi)存溢出控制策略。

過(guò)期刪除策略

刪除策略的目標(biāo):在內(nèi)存占用與CPU占用之間尋找一種平衡,顧此失彼都會(huì)造成整體redis性能的下降,甚至引發(fā)服務(wù)器宕機(jī)或
內(nèi)存泄露。

設(shè)置Redis鍵過(guò)期時(shí)間

先回顧一下Redis 提供的設(shè)置過(guò)期時(shí)間的命令:

  • EXPIRE :表示將鍵 key 的生存時(shí)間設(shè)置為 ttl 秒。
  • PEXPIRE :表示將鍵 key 的生存時(shí)間設(shè)置為 ttl 毫秒。
  • EXPIREAT :表示將鍵 key 的生存時(shí)間設(shè)置為 timestamp 所指定的秒數(shù)時(shí)間戳。
  • PEXPIREAT :表示將鍵 key 的生存時(shí)間設(shè)置為 timestamp 所指定的毫秒數(shù)時(shí)間戳。

在Redis內(nèi)部實(shí)現(xiàn)中,前面三個(gè)設(shè)置過(guò)期時(shí)間的命令最后都會(huì)轉(zhuǎn)換成最后一個(gè)PEXPIREAT 命令來(lái)完成。

其他相關(guān)命令還有:

  • 移除鍵的過(guò)期時(shí)間 PERSIST :表示將key的過(guò)期時(shí)間移除。
  • 返回鍵的剩余生存時(shí)間
    • TTL :以秒的單位返回鍵 key 的剩余生存時(shí)間。
    • PTTL :以毫秒的單位返回鍵 key 的剩余生存時(shí)間。

在Redis內(nèi)部,每當(dāng)我們?cè)O(shè)置一個(gè)鍵的過(guò)期時(shí)間時(shí),Redis就會(huì)將該鍵帶上過(guò)期時(shí)間存放到一個(gè)過(guò)期字典中。當(dāng)我們查詢一個(gè)鍵時(shí),Redis便首先檢查該鍵是否存在過(guò)期字典中,如果存在,那就獲取其過(guò)期時(shí)間。然后將過(guò)期時(shí)間和當(dāng)前系統(tǒng)時(shí)間進(jìn)行比對(duì),比系統(tǒng)時(shí)間大,那就沒(méi)有過(guò)期;反之判定該鍵過(guò)期。

此外:

對(duì)于字符串類型鍵,執(zhí)行set命令會(huì)去掉過(guò)期時(shí)間,這個(gè)問(wèn)題很容易在開發(fā)中被忽視

如下是Redis源碼中,set命令的函數(shù)setKey,可以看到最后執(zhí)行了removeExpire(db,key)函數(shù)去掉了過(guò)期時(shí)間:

void setKey(redisDb *db, robj *key, robj *val) {
  if (lookupKeyWrite(db,key) == NULL) {
      dbAdd(db,key,val);
  } else {
      dbOverwrite(db,key,val);
  }
  incrRefCount(val);
  // 去掉過(guò)期時(shí)間
  removeExpire(db,key);
  signalModifiedKey(db,key);
}
  • Redis不支持二級(jí)數(shù)據(jù)結(jié)構(gòu)(例如哈希、列表)內(nèi)部元素的過(guò)期功能,例如不能對(duì)列表類型的一個(gè)元素做過(guò)期時(shí)間設(shè)置。
  • setex命令作為set+expire的組合,不但是原子執(zhí)行,同時(shí)減少了一次網(wǎng)絡(luò)通訊的時(shí)間。

過(guò)期刪除策略

通常刪除某個(gè)key,我們有如下三種方式進(jìn)行處理

1 定時(shí)刪除

在設(shè)置某個(gè)key 的過(guò)期時(shí)間同時(shí),我們創(chuàng)建一個(gè)定時(shí)器,讓定時(shí)器在該過(guò)期時(shí)間到來(lái)時(shí),立即執(zhí)行對(duì)其進(jìn)行刪除的操作。

  • 優(yōu)點(diǎn):定時(shí)刪除對(duì)內(nèi)存是最友好的,能夠保存內(nèi)存的key一旦過(guò)期就能立即從內(nèi)存中刪除。
  • 缺點(diǎn):對(duì)CPU最不友好,在過(guò)期鍵比較多的時(shí)候,刪除過(guò)期鍵會(huì)占用一部分 CPU 時(shí)間,對(duì)服務(wù)器的響應(yīng)時(shí)間和吞吐量造成影響。

2 惰性刪除(Lazy delete)

設(shè)置該key 過(guò)期時(shí)間后,我們不去管它,當(dāng)需要該key時(shí),我們?cè)跈z查其是否過(guò)期,如果過(guò)期,我們就刪掉它,反之返回該key。

  • 優(yōu)點(diǎn):對(duì) CPU友好,我們只會(huì)在使用該鍵時(shí)才會(huì)進(jìn)行過(guò)期檢查,對(duì)于很多用不到的key不用浪費(fèi)時(shí)間進(jìn)行過(guò)期檢查。
  • 缺點(diǎn):對(duì)內(nèi)存不友好,如果一個(gè)鍵已經(jīng)過(guò)期,但是一直沒(méi)有使用,那么該鍵就會(huì)一直存在內(nèi)存中,如果數(shù)據(jù)庫(kù)中有很多這種使用不到的過(guò)期鍵,這些鍵便永遠(yuǎn)不會(huì)被刪除,內(nèi)存永遠(yuǎn)不會(huì)釋放。從而造成內(nèi)存泄漏。

3 定期刪除

每隔一段時(shí)間,我們就對(duì)一些key進(jìn)行檢查,刪除里面過(guò)期的key。

優(yōu)點(diǎn):可以通過(guò)限制刪除操作執(zhí)行的時(shí)長(zhǎng)和頻率來(lái)減少刪除操作對(duì) CPU 的影響。另外定期刪除,也能有效釋放過(guò)期鍵占用的內(nèi)存。

缺點(diǎn):難以確定刪除操作執(zhí)行的時(shí)長(zhǎng)和頻率。

  • 如果執(zhí)行的太頻繁,定期刪除策略變得和定時(shí)刪除策略一樣,對(duì)CPU不友好。
  • 如果執(zhí)行的太少,那又和惰性刪除一樣了,過(guò)期鍵占用的內(nèi)存不會(huì)及時(shí)得到釋放。
  • 另外最重要的是,在獲取某個(gè)鍵時(shí),如果某個(gè)鍵的過(guò)期時(shí)間已經(jīng)到了,但是還沒(méi)執(zhí)行定期刪除,那么就會(huì)返回這個(gè)鍵的值,這是業(yè)務(wù)不能忍受的錯(cuò)誤

Redis 使用的過(guò)期刪除策略

Redis所有的鍵都可以設(shè)置過(guò)期屬性,內(nèi)部保存在過(guò)期字典中。由于進(jìn)程內(nèi)保存大量的鍵,維護(hù)每個(gè)鍵精準(zhǔn)的過(guò)期刪除機(jī)制會(huì)導(dǎo)致消耗大量的CPU,對(duì)于單線程的Redis來(lái)說(shuō)成本過(guò)高,因此Redis采用惰性刪除和定時(shí)任務(wù)刪除機(jī)制實(shí)現(xiàn)過(guò)期鍵的內(nèi)存回收

惰性刪除:Redis的惰性刪除策略由 db.c/expireIfNeeded 函數(shù)實(shí)現(xiàn),所有鍵讀寫命令執(zhí)行之前都會(huì)調(diào)用 expireIfNeeded 函數(shù)對(duì)其進(jìn)行檢查,如果過(guò)期,則刪除該鍵,然后執(zhí)行鍵不存在的操作;未過(guò)期則不作操作,繼續(xù)執(zhí)行原有的命令。

定期刪除:由redis.c/activeExpireCycle 函數(shù)實(shí)現(xiàn),函數(shù)以一定的頻率運(yùn)行,每次運(yùn)行時(shí),都從一定數(shù)量的數(shù)據(jù)庫(kù)中取出一定數(shù)量的隨機(jī)鍵進(jìn)行檢查,并刪除其中的過(guò)期鍵。注意:并不是一次運(yùn)行就檢查所有的庫(kù),所有的鍵,而是隨機(jī)檢查一定數(shù)量的鍵。

定期刪除函數(shù)的運(yùn)行頻率,在Redis2.6版本中,規(guī)定每秒運(yùn)行10次,大概100ms運(yùn)行一次。在Redis2.8版本后,可以通過(guò)修改配置文件redis.conf 的 hz 選項(xiàng)來(lái)調(diào)整這個(gè)次數(shù)。

看上面對(duì)這個(gè)參數(shù)的解釋,建議不要將這個(gè)值設(shè)置超過(guò) 100,否則會(huì)對(duì)CPU造成比較大的壓力。

定時(shí)任務(wù)中刪除過(guò)期鍵邏輯采用了自適應(yīng)算法,根據(jù)鍵的過(guò)期比例、使用快慢兩種速率模式回收鍵,流程如下圖所示:

內(nèi)存淘汰策略 (逐出算法)

當(dāng)Redis所用內(nèi)存達(dá)到maxmemory上限時(shí)會(huì)觸發(fā)相應(yīng)的溢出控制策略。

具體策略受maxmemory-policy參數(shù)控制,Redis支持8種策略(有關(guān)LFU算法的,是從Redis4.0以后版本才有):

  • noeviction:默認(rèn)策略,不會(huì)刪除任何數(shù)據(jù),拒絕所有寫入操作并返回客戶端錯(cuò)誤信息(error)OOM command not allowed when used memory,此時(shí)Redis只響應(yīng)讀操作。生產(chǎn)一般不會(huì)選用
  • allkeys-lru 利用LRU算法移除任何key (不管數(shù)據(jù)有沒(méi)有設(shè)置超時(shí)屬性,直到騰出足夠空間為止)。
  • allkeys-lfu 利用LRU算法移除任何key (不管數(shù)據(jù)有沒(méi)有設(shè)置超時(shí)屬性,直到騰出足夠空間為止)
  • volatile-lru:根據(jù)LRU算法刪除設(shè)置了超時(shí)屬性(expire)的鍵,直到騰出足夠空間為止。如果沒(méi)有可刪除的鍵對(duì)象,回退到noeviction策略。
  • volatile-lfu:根據(jù)LFU算法刪除設(shè)置了超時(shí)屬性(expire)的鍵,直到騰出足夠空間為止。如果沒(méi)有可刪除的鍵對(duì)象,回退到noeviction策略。
  • allkeys-random 無(wú)差別的隨機(jī)移除,直到騰出足夠空間為止。
  • volatile-random:隨機(jī)刪除過(guò)期鍵,直到騰出足夠空間為止。
  • volatile-ttl:根據(jù)鍵值對(duì)象的ttl屬性,刪除最近將要過(guò)期數(shù)據(jù)。如果沒(méi)有,回退到noeviction策略。

在redis.conf 配置文件中,可以設(shè)置淘汰方式:

內(nèi)存溢出控制策略可以采用config set maxmemory-policy{policy}動(dòng)態(tài)配置。Redis支持豐富的內(nèi)存溢出應(yīng)對(duì)策略,可以根據(jù)實(shí)際需求靈活定制,比如當(dāng)設(shè)置volatile-lru策略時(shí),保證具有過(guò)期屬性的鍵可以根據(jù)LRU剔除,而未設(shè)置超時(shí)的鍵可以永久保留。還可以采用allkeys-lru策略把Redis變?yōu)榧兙彺娣?wù)器使用。當(dāng)Redis因?yàn)閮?nèi)存溢出刪除鍵時(shí),可以通過(guò)執(zhí)行info stats命令查看evicted_keys指標(biāo)找出當(dāng)前Redis服務(wù)器已剔除的鍵數(shù)量。

LFU

LFU 算法(Least Frequently Used,最不經(jīng)常使用):淘汰最近一段時(shí)間被訪問(wèn)次數(shù)最少的數(shù)據(jù),以次數(shù)作為參考。

需要指出的是 : LRU 算法或者 TTL 算法都是不是很精確算法,而是一個(gè)近似的算法。 Redis 不會(huì)通過(guò)對(duì)全部的鍵值對(duì)進(jìn)行比較來(lái)確定最精確的時(shí)間值,從而確定刪除哪個(gè)鍵值對(duì) , 因?yàn)檫@將消耗太多的時(shí)間 , 導(dǎo)致回收垃圾執(zhí)行的時(shí)間太長(zhǎng) , 造成服務(wù)停頓。

當(dāng)存在熱點(diǎn)數(shù)據(jù)時(shí),LRU的效率很好,但偶發(fā)性的、周期性的批量操作會(huì)導(dǎo)致LRU命中率急劇下降,緩存污染情況比較嚴(yán)重。這時(shí)使用LFU可能更好點(diǎn)

LRU

LRU算法, 最近最久未使用算法, Least Recently Used

下圖是一個(gè)淘汰的流程:

在Redis中LRU算法是一個(gè)近似算法,默認(rèn)情況下,Redis隨機(jī)挑選5個(gè)鍵,并且從中選取一個(gè)最近最久未使用的key進(jìn)行淘汰,在配置文件中可以通過(guò)maxmemory-samples的值來(lái)設(shè)置redis需要檢查key的個(gè)數(shù),但是檢查的越多,耗費(fèi)的時(shí)間也就越久,但是結(jié)構(gòu)越精確(也就是Redis從內(nèi)存中淘汰的對(duì)象未使用的時(shí)間也就越久),設(shè)置多少,綜合權(quán)衡吧~~

Redis 3.0對(duì)這個(gè)近似算法的優(yōu)化

新算法會(huì)維護(hù)一個(gè)候選池(大小為16),池中的數(shù)據(jù)根據(jù)訪問(wèn)時(shí)間進(jìn)行排序,第一次隨機(jī)選取的key都會(huì)放入池中,隨后每次隨機(jī)選取的key只有在訪問(wèn)時(shí)間小于池中最小的時(shí)間才會(huì)放入池中,直到候選池被放滿。當(dāng)放滿后,如果有新的key需要放入,則將池中最后訪問(wèn)時(shí)間最大(最近被訪問(wèn))的移除。當(dāng)需要淘汰時(shí),需要從池中撈出最久沒(méi)被訪問(wèn)的key淘汰掉就行了。

新舊算法的對(duì)比

下面的圖片是Redis官方文檔給出的新舊算法對(duì)比結(jié)果:

  • 淺灰色是被淘汰的數(shù)據(jù)
  • 灰色是沒(méi)有被淘汰掉的老數(shù)據(jù)
  • 綠色是新加入的數(shù)據(jù)

可以看到3.0的效果明顯比2.8的要得多,并且取樣數(shù)越大,越接近標(biāo)準(zhǔn)的LRU算法

為什么Redis不使用真正的LRU ?

原因很簡(jiǎn)單,理論的LRU需要你占用更大的內(nèi)存(每個(gè)key還需要保存前后key的地址), 但你從上圖就可以看出Redis 3.0使用的近似LRU算法使用起來(lái)的效果幾乎與理論的LRU等效了。

java實(shí)現(xiàn)LRU ?

Java自帶的集合框架非常強(qiáng)大,實(shí)現(xiàn)LRU算法可以直接使用LinkedHashMap集合框架,簡(jiǎn)單實(shí)現(xiàn)的話,只需要重寫 removeEldestEntry 方法即可。

import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class LRUCache extends LinkedHashMap {
    private static final long serialVersionUID = 1L;
    private final int capacity;
    private long accessCount = 0;
    private long hitCount = 0;
    public LRUCache(int capacity) {
        super(capacity+1, 1.1f, true);
        this.capacity = capacity;
    }
    public String get(String key) {
        accessCount++;
        if (super.containsKey(key)) {
            hitCount++;
        }
        String value = (String)super.get(key);
        return value;
    }
      public boolean containsKey(String key) {
          accessCount++;
          if (super.containsKey(key)) {
              hitCount++;
              return true;
          } else {
              return false;
          }
      }
      protected boolean removeEldestEntry(Entry eldest) {
          return size() > capacity;
      }
      public long getAccessCount() {
          return accessCount;
      }
      public long getHitCount() {
          return hitCount;
      }
}

這是LinkedHashMap的一個(gè)構(gòu)造函數(shù),傳入的第三個(gè)參數(shù)accessOrder為true的時(shí)候,就按訪問(wèn)順序?qū)inkedHashMap排序,為false的時(shí)候就按插入順序,默認(rèn)是為false的。當(dāng)把a(bǔ)ccessOrder設(shè)置為true后,就可以將最近訪問(wèn)的元素置于最前面。

public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

這是LinkedHashMap中另外一個(gè)方法,當(dāng)返回true的時(shí)候,就會(huì)remove其中最久的元素,可以通過(guò)重寫這個(gè)方法來(lái)控制緩存元素的刪除,當(dāng)緩存滿了后,就可以通過(guò)返回true刪除最久未被使用的元素,達(dá)到LRU的要求。

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

參考

《Redis 開發(fā)與運(yùn)維》
http://antirez.com/news/109
https://redis.io/docs/manual/eviction/
https://zhuanlan.zhihu.com/p/149528273
https://blog.51cto.com/u_15239532/2835914
https://www.geekxh.com/1.99.其他補(bǔ)充題目/11.htm
https://www.cnblogs.com/ysocean/p/12422635.html
https://blog.csdn.net/weixin_43230682/article/details/107670911

到此這篇關(guān)于淺談Redis內(nèi)存回收策略的文章就介紹到這了,更多相關(guān)Redis內(nèi)存回收內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis+Lua腳本實(shí)現(xiàn)計(jì)數(shù)器接口防刷功能(升級(jí)版)

    Redis+Lua腳本實(shí)現(xiàn)計(jì)數(shù)器接口防刷功能(升級(jí)版)

    這篇文章主要介紹了Redis+Lua腳本實(shí)現(xiàn)計(jì)數(shù)器接口防刷功能,使用腳本使得set命令和expire命令一同達(dá)到Redis被執(zhí)行且不會(huì)被干擾,在很大程度上保證了原子操作,對(duì)Redis實(shí)現(xiàn)計(jì)數(shù)器接口防刷功能感興趣的朋友一起看看吧
    2022-02-02
  • Redis批量刪除指定前綴的Key兩種方法

    Redis批量刪除指定前綴的Key兩種方法

    redis作為緩存服務(wù)器在項(xiàng)目中經(jīng)常使用,使用redis存儲(chǔ)數(shù)據(jù)時(shí),我們經(jīng)常會(huì)將key分組,這篇文章主要給大家介紹了關(guān)于Redis批量刪除指定前綴的Key兩種方法,需要的朋友可以參考下
    2024-01-01
  • Redis+IDEA實(shí)現(xiàn)單機(jī)鎖和分布式鎖的過(guò)程

    Redis+IDEA實(shí)現(xiàn)單機(jī)鎖和分布式鎖的過(guò)程

    這篇文章主要介紹了Redis+IDEA實(shí)現(xiàn)單機(jī)鎖和分布式鎖的過(guò)程,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • 關(guān)于Redis持久化的深入探究

    關(guān)于Redis持久化的深入探究

    Redis持久化是將內(nèi)存中的數(shù)據(jù)保存到磁盤,以防止數(shù)據(jù)丟失。Redis提供了兩種持久化方式:RDB和AOF,本文將給大家詳解介紹Redis持久化,感興趣的同學(xué)可以跟著小編一起來(lái)學(xué)習(xí)
    2023-05-05
  • Redis的常用命令小結(jié)

    Redis的常用命令小結(jié)

    本文主要介紹了Redis的常用命令小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Redis實(shí)戰(zhàn)之Redis實(shí)現(xiàn)異步秒殺優(yōu)化詳解

    Redis實(shí)戰(zhàn)之Redis實(shí)現(xiàn)異步秒殺優(yōu)化詳解

    這篇文章主要給大家介紹了Redis實(shí)戰(zhàn)之Redis實(shí)現(xiàn)異步秒殺優(yōu)化方法,文章通過(guò)圖片和代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的同學(xué)可以自己動(dòng)手試一下
    2023-09-09
  • redis事務(wù)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    redis事務(wù)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了redis事務(wù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • windows 64位下redis安裝教程

    windows 64位下redis安裝教程

    這篇文章主要為大家詳細(xì)介紹了windows 64位下redis安裝教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Redis高效查詢大數(shù)據(jù)的實(shí)踐與優(yōu)化詳細(xì)指南

    Redis高效查詢大數(shù)據(jù)的實(shí)踐與優(yōu)化詳細(xì)指南

    Redis 是一種高性能的鍵值存儲(chǔ)數(shù)據(jù)庫(kù),廣泛應(yīng)用于緩存,排行榜,計(jì)數(shù)器等場(chǎng)景,本文將圍繞如何高效查詢Redis中滿足條件的數(shù)據(jù)展開討論,感興趣的小伙伴可以了解下
    2025-04-04
  • windows環(huán)境下Redis+Spring緩存實(shí)例講解

    windows環(huán)境下Redis+Spring緩存實(shí)例講解

    這篇文章主要為大家詳細(xì)介紹了windows環(huán)境下Redis+Spring緩存實(shí)例教程,感興趣的小伙伴們可以參考一下
    2016-04-04

最新評(píng)論