搜尋
首頁資料庫Redis一文詳解Redis中的LRU演算法
一文詳解Redis中的LRU演算法Oct 13, 2021 am 10:33 AM
lru演算法redis

這篇文章帶大家了解Redis中的LRU(Least Recently Used),希望對大家有幫助!

一文詳解Redis中的LRU演算法

Redis是基於記憶體儲存的key-value資料庫,我們知道記憶體雖然快但空間小,當實體記憶體達到上限時,系統就會跑的很慢,這是因為swap機制會將部分內存的數據轉移到swap分區中,通過與swap的交換保證系統繼續運行;但是swap屬於硬碟存儲,速度遠遠比不上內存,尤其是對於Redis這種QPS非常高的服務,發生這種情況是無法接收的。 (注意如果swap分割記憶體也滿了,系統就會發生錯誤!)【相關推薦:Redis影片教學

Linux作業系統可以透過free -m查看swap大小:\

一文詳解Redis中的LRU演算法

因此如何防止Redis發生這種情況非常重要(面試官問到Redis幾乎沒有不問這個知識點的)。

2、maxmemory配置

Redis針對上述問題提供了maxmemory配置,這個配置可以指定Redis記憶體的最大資料集,通常情況都是在redis.conf檔中進行配置,也可以運作時使用CONFIG SET指令進行一次性配置。
redis.conf檔案中的設定項目示意圖:\

一文詳解Redis中的LRU演算法

#預設情況maxmemory設定項並未啟用,Redis官方介紹64位元操作系統預設無記憶體限制,32位元作業系統預設3GB隱式記憶體配置,若maxmemory 為0,代表記憶體不受限。

因此我們在做快取架構時,要根據硬體資源 業務需求做適合的maxmemory配置。

3、記憶體達到maxmemory怎麼辦

很顯然配置了最大內存,當maxmemory達到了最大上限之後Redis不可能不干活了,那麼Redis是怎麼來處理這個問題的呢?這就是本文的重點,Redis 提供了maxmemory-policy淘汰策略(本文只講述LRU不涉及LFU,LFU在下一篇文章講述),對滿足條件的key進行刪除,辭舊迎新。
maxmemory-policy淘汰策略:

  • noeviction: 當達到記憶體限制並且客戶端嘗試執行可能導致使用更多記憶體的命令時回傳錯誤,簡單來說讀取操作仍然允許,但是不准寫入新的數據,del(刪除)請求可以
  • allkeys-lru: 從全體key中,透過lru(Least Recently Used - 最近最少使用)演算法進行淘汰
  • allkeys-random: 從全體key中,隨機進行淘汰
  • volatile-lru: 從設定了過期時間的全部key中,透過lru(Least Recently Used - 最近最少使用)演算法進行淘汰,這樣可以保證未設定過期時間需要被持久化的數據,不會被選中淘汰
  • volatile-random: 從設定了過期時間的全部key中,隨機進行淘汰
  • volatile-ttl: 從設定了過期時間的全部key中,透過比較key的剩餘過期時間TTL的值,TTL越小越先被淘汰

#還有volatile-lfu/allkeys-lfu這個留到下文一起探討,兩個演算法不一樣!

random隨機淘汰只需要隨機取一些key來刪除,釋放記憶體空間即可;ttl過期時間小先淘汰也可以透過比較ttl的大小,將ttl值小的key刪除,釋放記憶體空間即可。
那LRU是怎麼實現的呢? Redis又是如何知道哪個key最近被使用了,哪個key最近沒有被使用呢?

4、LRU演算法實作

我們先用Java的容器實作一個簡單的LRU演算法,我們使用ConcurrentHashMap做key-value結果儲存元素的映射關係,使用ConcurrentLinkedDeque來維持key的訪問順序。
LRU實作程式碼:

package com.lizba.redis.lru;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;

/**
 * <p>
 *      LRU简单实现
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/9/17 23:47
 */
public class SimpleLru {

    /** 数据缓存 */
    private ConcurrentHashMap<string> cacheData;
    /** 访问顺序记录 */
    private ConcurrentLinkedDeque<string> sequence;
    /** 缓存容量 */
    private int capacity;

    public SimpleLru(int capacity) {
        this.capacity = capacity;
        cacheData = new ConcurrentHashMap(capacity);
        sequence = new ConcurrentLinkedDeque();
    }


    /**
     * 设置值
     *
     * @param key
     * @param value
     * @return
     */
    public Object setValue(String key, Object value) {
        // 判断是否需要进行LRU淘汰
        this.maxMemoryHandle();
        // 包含则移除元素,新访问的元素一直保存在队列最前面
        if (sequence.contains(key)) {
            sequence.remove();
        }
        sequence.addFirst(key);
        cacheData.put(key, value);
        return value;
    }


    /**
     * 达到最大内存,淘汰最近最少使用的key
     */
    private void maxMemoryHandle() {
        while (sequence.size() >= capacity) {
            String lruKey = sequence.removeLast();
            cacheData.remove(lruKey);
            System.out.println("key: " + lruKey + "被淘汰!");
        }
    }


    /**
     * 获取访问LRU顺序
     *
     * @return
     */
    public List<string> getAll() {
        return Arrays.asList(sequence.toArray(new String[] {}));
    }
}复制代码</string></string></string>

測試程式碼:

package com.lizba.redis.lru;

/**
 * <p>
 *      测试最近最少使用
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/9/18 0:00
 */
public class TestSimpleLru {

    public static void main(String[] args) {
        SimpleLru lru = new SimpleLru(8);
        for (int i = 0; i <p>##測試結果:<strong>\</strong></p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/image/775/594/750/163409191823487一文詳解Redis中的LRU演算法?x-oss-process=image/resize,p_40" class="lazy" title="163409191823487一文詳解Redis中的LRU演算法" alt="一文詳解Redis中的LRU演算法"></p>從上數的測試結果可以看出,先加入的key0,key1被淘汰了,最後加入的key也是最新的key保存在sequence的隊頭。 <p>透過這個方案,可以很簡單的實作LRU演算法;但缺點也十分明顯,方案需要使用額外的資料結構來保存key的存取順序,這會使Redis記憶體消耗增加,本身用來優化記憶體的方案,卻要消耗不少內存,顯然是不行的。 <br><br></p><h2 id="Redis的近似LRU">5、Redis的近似LRU</h2><p>針對這種情況,Redis使用了近似LRU演算法,並不是完完全全準確的淘汰掉最近最不經常使用的key,但是總體的準確度也可以得到保證。 <br>近似LRU演算法非常簡單,在Redis的key物件中,增加24bit用於儲存最近一次存取的系統時間戳,當客戶端對Redis服務端發送key的寫入相關請求時,發現記憶體達到maxmemory,此時觸發惰性刪除;Redis服務透過隨機採樣,選擇5個滿足條件的key(注意這個隨機採樣<strong>allkeys-lru</strong>是從所有的key中隨機採樣,<strong>volatile-lru</strong>是從設定了過期時間的所有key中隨機採樣),透過key物件中記錄的最近存取時間戳進行比較,淘汰掉這5個key中最舊的key;如果記憶體仍然不夠,就繼續重複這個步驟。 <br></p><p><strong>注意,5是Redis預設的隨機取樣數值大小,它可以透過redis.conf中的maxmemory_samples進行配置:</strong>\</p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/image/866/655/134/163409192913128一文詳解Redis中的LRU演算法?x-oss-process=image/resize,p_40" class="lazy" title="163409192913128一文詳解Redis中的LRU演算法" alt="一文詳解Redis中的LRU演算法"></p><p><strong>針對上述的隨機LRU演算法,Redis官方給出了一張測試準確性的數據圖:</strong></p>
  • 最上層淺灰色表示被淘汰的key ,圖一是標準的LRU演算法淘汰的示意圖
  • 中間深灰色層表示未被淘汰的舊key
  • 最下層淺綠色表示最近被訪問的key

一文詳解Redis中的LRU演算法

在Redis 3.0 maxmemory_samples設定為10的時候,Redis的近似LRU演算法已經非常的接近真實LRU演算法了,但是顯然maxmemory_samples設定為10比maxmemory_samples 設定為5要更加消耗CPU計算時間,因為每次採樣的樣本資料增大,計算時間也會增加。
Redis3.0的LRU比Redis2.8的LRU演算法更準確,是因為Redis3.0增加了一個與maxmemory_samples相同大小的淘汰池,每次淘汰key的時候,先與淘汰池中等待被淘汰的key進行比較,最後淘汰掉最老舊的key,其實就是被選中淘汰的key放到一起再比較一下,淘汰其中最舊的。

6、有問題

LRU演算法看似比較好用,但是也存在不合理的地方,例如A和B兩個key,在發生淘汰時的前一個小時前同一時刻加入Redis,A在前49分鐘被訪問了1000次,但是後11分鐘沒有被訪問;B在這一小時內僅第59分鐘被訪問了1次;此時如果使用LRU演算法,如果A、 B都被Redis採樣選中,A將會被淘汰很顯然這個是不合理的。
針對這種情況Redis 4.0添加了LFU演算法,(Least frequently used) 最不常使用,這種演算法比LRU更加合理,下文將會一起學習中淘汰演算法,如有需要請關注我的專欄。

更多程式相關知識,請造訪:程式設計教學! !

以上是一文詳解Redis中的LRU演算法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:掘金社区。如有侵權,請聯絡admin@php.cn刪除
es和redis区别es和redis区别Jul 06, 2019 pm 01:45 PM

Redis是现在最热门的key-value数据库,Redis的最大特点是key-value存储所带来的简单和高性能;相较于MongoDB和Redis,晚一年发布的ES可能知名度要低一些,ES的特点是搜索,ES是围绕搜索设计的。

一起来聊聊Redis有什么优势和特点一起来聊聊Redis有什么优势和特点May 16, 2022 pm 06:04 PM

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于redis的一些优势和特点,Redis 是一个开源的使用ANSI C语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式存储数据库,下面一起来看一下,希望对大家有帮助。

实例详解Redis Cluster集群收缩主从节点实例详解Redis Cluster集群收缩主从节点Apr 21, 2022 pm 06:23 PM

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis Cluster集群收缩主从节点的相关问题,包括了Cluster集群收缩概念、将6390主节点从集群中收缩、验证数据迁移过程是否导致数据异常等,希望对大家有帮助。

详细解析Redis中命令的原子性详细解析Redis中命令的原子性Jun 01, 2022 am 11:58 AM

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于原子操作中命令原子性的相关问题,包括了处理并发的方案、编程模型、多IO线程以及单命令的相关内容,下面一起看一下,希望对大家有帮助。

Redis实现排行榜及相同积分按时间排序功能的实现Redis实现排行榜及相同积分按时间排序功能的实现Aug 22, 2022 pm 05:51 PM

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis实现排行榜及相同积分按时间排序,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,希望对大家有帮助。

实例详解Redis实现排行榜及相同积分按时间排序功能的实现实例详解Redis实现排行榜及相同积分按时间排序功能的实现Aug 26, 2022 pm 02:09 PM

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis实现排行榜及相同积分按时间排序,本文通过实例代码给大家介绍的非常详细,下面一起来看一下,希望对大家有帮助。

一文搞懂redis的bitmap一文搞懂redis的bitmapApr 27, 2022 pm 07:48 PM

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了bitmap问题,Redis 为我们提供了位图这一数据结构,位图数据结构其实并不是一个全新的玩意,我们可以简单的认为就是个数组,只是里面的内容只能为0或1而已,希望对大家有帮助。

一起聊聊Redis实现秒杀的问题一起聊聊Redis实现秒杀的问题May 27, 2022 am 11:40 AM

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于实现秒杀的相关内容,包括了秒杀逻辑、存在的链接超时、超卖和库存遗留的问题,下面一起来看一下,希望对大家有帮助。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
1 個月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境