Redis過期--淘汰機制的解析和記憶體占用過高的解決方案

来源:https://www.cnblogs.com/xlecho/archive/2019/11/10/11832128.html
-Advertisement-
Play Games

echo編輯整理,歡迎轉載,轉載請聲明文章來源。歡迎添加echo微信(微信號:t2421499075)交流學習。 百戰不敗,依不自稱常勝,百敗不頹,依能奮力前行。——這才是真正的堪稱強大!!! Redis在我們平時的開發或者練習的時候,往往很容易忽略一個問題,那就是我們的Redis記憶體占滿的問題。但 ...


echo編輯整理,歡迎轉載,轉載請聲明文章來源。歡迎添加echo微信(微信號:t2421499075)交流學習。 百戰不敗,依不自稱常勝,百敗不頹,依能奮力前行。——這才是真正的堪稱強大!!!


Redis在我們平時的開發或者練習的時候,往往很容易忽略一個問題,那就是我們的Redis記憶體占滿的問題。但是在真是的商業開發中,Redis的實際占滿是真正會存在這樣的問題的。那麼如果Redis在某一刻占滿記憶體,我們又沒有對它進行相應的設置它會出現什麼情況呢?會不會導致我們整個因為使用Redis而整個業務垮掉?這就是我們本篇文章所要講述的問題。

什麼是Redis淘汰機制

Redis記憶體淘汰機制其實簡單講就是將過期的數據或者很久沒有訪問,或者在一段時間內很少有訪問的數據進行刪除。它分為很多中,有主動的數據淘汰,如:用戶設定過期時間。有被動的淘汰,比如:Redis數據占滿了記憶體,這個時候就會將過期的數據或者很久沒有訪問的數據刪除掉。

Redis的淘汰有哪些類型

  • 定時過期:每個設置過期時間的key都需要創建一個定時器,到過期時間就會立即清除。該策略可以立即清除過期的數據,對記憶體很友好;但是會占用大量的CPU資源去處理過期的數據,從而影響緩存的響應時間和吞吐量。
  • 惰性過期:只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節省CPU資源,卻對記憶體非常不友好。極端情況可能出現大量的過期key沒有再次被訪問,從而不會被清除,占用大量記憶體。
  • 定期過期:每隔一定的時間,會掃描一定數量的資料庫的expires字典中一定數量的key,並清除其中已過期的key。該策略是前兩者的一個折中方案。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和記憶體資源達到最優的平衡效果。
    (expires字典會保存所有設置了過期時間的key的過期時間數據,其中,key是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的UNIX時間戳表示的過期時間。鍵空間是指該Redis集群中保存的所有鍵。)

定時過期的問題:緩存雪崩

很多人可能對這個詞很熟悉,因為有很多的商業案例展示了血淋淋的教訓,但是也有一部分人估計沒有接觸過。緩存雪崩其實也不是什麼新詞,它主要的引起原因就是指緩存中數據大批量的到過期時間。定時過期它本身就有一個缺點,那就是會占用大量的CPU資源,如果我們主動設置過期時間的鍵過多,在同一時間過期,很有可能就會造就我們Redis掛掉。但是這並不是最可怕的,雪崩不僅僅影響自己,還在我們的業務中影響資料庫。因為我們很多業務設計都是在我們Redis的數據過期之後,從新查詢資料庫,但我們Redis主動批量過期的時候,會有大量的請求發送到我們的資料庫,很有可能導致我們的資料庫也掛掉。這才是最大的問題所在。

解決方案:

  • 緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
  • 如果緩存資料庫是分散式部署,將熱點數據均勻分佈在不同搞得緩存資料庫中。
  • 設置熱點數據永遠不過期。

從幾種淘汰策略中其實我們可以看到基本的一些問題所在,所以我們在使用緩存的時候最好有一個全面的瞭解和全面的考慮應對。在實際開發中,我們更應該多去關註的和瞭解的是定期過期,因為它涉及真實開發中的一些問題。所以我們應該提前設置好。

怎麼設置定期過期最大使用記憶體

定期過期的最大記憶體設置在我們的redis.conf文件中,我們可以在該文件中看到這個配置:maxmemory <bytes>,但是這個配置一般都是註釋掉的,也就是說安裝之後如果我們沒有主動對他進行配置,那麼他就不會有預設大小值。對應的它不設置的情況下,那麼它可以使用多少的記憶體空間呢?這個跟系統有關。如果說我們將Redis安裝在32位的系統上,它的最大使用記憶體空間應該是在3G左右,如果是64位的系統,那麼可以將我們的記憶體占滿。當然如果真正占滿記憶體,這是一件比較惡劣的事情,不僅僅訪問Redis的時候,我們不能在進行寫的操作,而且我們系統本身的其他操作也會受到限制。所以我們可以採用命令來對它進行一個初始化的設置

config set maxmemory 268435456

使用命令進行設置之後我們需要重啟Redis才能生效。當然我們也可以直接找到Redis的安裝目錄,然後使用vi命令,直接更改配置文件中的對應的該內容,更改完之後,重啟即可。

定期過期的淘汰策略

  • volatile-lru:根據LRU演算法生成的過期時間來刪除。
  • allkeys-lru:根據LRU演算法刪除任何key。
  • volatile-lfu:從所有配置了過期時間的鍵中驅逐使用頻率最少的鍵
  • allkeys-lfu:從所有鍵中驅逐使用頻率最少的鍵
  • volatile-random:根據過期設置來隨機刪除key。
  • allkeys-random:無差別隨機刪。
  • volatile-ttl:根據最近過期時間來刪除(輔以TTL)
  • noeviction:誰也不刪,直接在寫操作時返回錯誤。

隨機淘汰策略

隨機找hash桶再次hash指定位置的dictEntry即可。就是在場景REDIS_MAXMEMORY_VOLATILE_RANDOM和REDIS_MAXMEMORY_ALLKEYS_LRU情況下的待淘汰的key。我們可以一觀它的源碼:

dictEntry *dictGetRandomKey(dict *d)
{
    dictEntry *he, *orighe;
    unsigned int h;
    int listlen, listele;
 
    if (dictSize(d) == 0) return NULL;
 
    if (dictIsRehashing(d)) _dictRehashStep(d);
 
    if (dictIsRehashing(d)) {
        // T = O(N)
        do {
            h = random() % (d->ht[0].size+d->ht[1].size);
            he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
                                      d->ht[0].table[h];
        } while(he == NULL);
    } else {
        // T = O(N)
        do {
            h = random() & d->ht[0].sizemask;
            he = d->ht[0].table[h];
        } while(he == NULL);
    }
 
    /* Now we found a non empty bucket, but it is a linked
     * list and we need to get a random element from the list.
     * The only sane way to do so is counting the elements and
     * select a random index. */
    listlen = 0;
    orighe = he;
    while(he) {
        he = he->next;
        listlen++;
    }
    listele = random() % listlen;
    he = orighe;
    // T = O(1)
    while(listele--) he = he->next;
 
    return he;
}

TTL時間淘汰

for (k = 0; k < server.maxmemory_samples; k++) {
    sds thiskey;
    long thisval;

    de = dictGetRandomKey(dict);
    thiskey = dictGetKey(de);
    thisval = (long) dictGetVal(de);

    /* Expire sooner (minor expire unix timestamp) is better
     * candidate for deletion */
    if (bestkey == NULL || thisval < bestval) {
        bestkey = thiskey;
        bestval = thisval;
    }
}

更多源碼,請參考redis官網。這裡只是簡單的展示兩種。我們可以根據這樣的代碼來對我們的redis的定期過期做一個合理的配置

思考:

基於一個數據結構做緩存,怎麼實現一個LRU演算法?

做一個有底線的博客主


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 23種GoF設計模式概述 在前面,我們對 GoF 的 23 種設計模式進行了分類,這裡先對各個設計模式的功能進行簡要介紹,以便有個大概瞭解。後面的章節再進行詳細介紹。 創建型模式 關註於怎麼創建對象的創建型模式,他們將對象的創建與使用相互分離,對象的使用者無需關心如何創建對象,只知道怎麼使用就行,以 ...
  • 1 基礎概念 1.1. 什麼是事務 什麼是事務?舉個生活的例子 :你去小賣部買東西,“一手交錢,一手交貨“就是一個事務的例子,交錢和交貨必須全部成功,事務才算成功,任一個活動失敗,事務將撤銷所有已成功的活動。明白上述例子,再來看事務的定義 :事務可以看做是一次大的活動,它由不同的小活動組成,這些活動 ...
  • 本段源碼可以學習的地方: 1. 考慮到效率問題,可以通過上下文的機制,在屬性被訪問的時候臨時構建; 2. 可以重寫一些魔術方法,比如 __new__ 方法,在調用 object.__new__(cls) 前後進行屬性的一些小設置; 3. 在本庫中使用的重寫魔術方法,上下文這兩種基礎之上,我們可以想... ...
  • '''''' ''' 1 python的定義 是一門弱類型的解釋性的高級編程語言 這裡的高級是相對低級(例如:彙編語言等) 高級編程語言和低級編程語言的區別 1、前者更接近於人的理解--字母組成的語法 2、後者更接近於計算器的理解--位元組碼、二進位 2 python的特點 人生苦短,我用python... ...
  • (手機橫屏看源碼更方便) 簡介 線程系列我們基本就學完了,這一個系列我們基本都是圍繞著線程池在講,其實關於線程還有很多東西可以講,後面有機會我們再補充進來。當然,如果你有什麼好的想法,也可以公從號右下角聯繫我。 重要知識點 直接上圖,看著這張圖我相信你能夠回憶起很多東西,也可以看著這張圖來自己提問自 ...
  • 一、常量 1.常量的定義:final修飾的實例變數是不可變的,這種變數一般和static聯合使用,被稱為“常量” 2.常量的語法格式: public static final 類型 常量名 = 值; java規範中要求所有常量的名字全部大寫,每個單詞之間使用下劃線連接 package com.bjp ...
  • 新聞 "邀請博客主們:2019年的F Advent日曆" "宣告ML.NET 1.4" ".NET Core與Jupyter筆記本" "在Jupyter筆記本中使用ML.NET" "用於Windows桌面的.NET Core 3" "宣告.NET Core 3.1預覽版2" "在.NET Core ...
  • PSR是PHP Standards Recommendation的簡稱,意為PHP推薦標準。要想瞭解PSR,首先得知道制定這一標準的人/組織是誰————PHP-FIG。 PHP-FIG PHP-FIG全稱為PHP Framework Interop Group,是一個組織,這個組織的成員由一些PHP ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...