婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 緩存替換策略及應用(以Redis、InnoDB為例)

緩存替換策略及應用(以Redis、InnoDB為例)

熱門標簽:北京400電話辦理收費標準 貴州電銷卡外呼系統 日本中國地圖標注 十堰營銷電銷機器人哪家便宜 鄭州人工智能電銷機器人系統 山東外呼銷售系統招商 超呼電話機器人 魔獸2青云地圖標注 宿遷便宜外呼系統平臺

1 概述

在操作系統的頁面管理中,內存會維護一部分數據以備進程使用,但是由于內存的大小必然是遠遠小于硬盤的,當某些進程訪問到內存中沒有的數據時,必然需要從硬盤中讀進內存,所以迫于內存容量的壓力下迫使操作系統將一些頁換出,或者說踢出,而決定將哪些(個)頁面踢出就是內存替換策略。

我們考慮內存中的頁實際上是整個系統頁的子集,所以內存可以當成系統中虛擬內存的緩存(Cache),所以頁面置換算法就是緩存替換的方法。

一般意義下,選取頁面置換算法即選取一個緩存命中率更高的或者說缺頁率更低的算法,但實際上有時候隨著算法緩存命中率提升,算法復雜度也在上升,所以帶來的系統開銷也是在上升的,所以我們不得不在系統開銷和算法復雜程度中間取一個折中,比如Redis中采取的類LRU緩存置換策略實際上在大多數情況下比起理想LRU緩存命中率是低的,但理想LRU實現起來會給系統帶來更大的開銷,得不償失。

2 頁面置換算法

下面介紹最常見的五種置換策略,其中最佳置換算法是理論上很難實現的,其余的FIFO、LRU、LFU,其算法復雜度、開銷是遞增的,但是缺頁率也是越來越低,或者說越來越接近最佳置換策略的。最后一種時鐘置換算法是一種近似的LRU算法,是保證了低系統開銷下同時較低的缺頁率的一種折中選擇。

2.1 最佳(Optimal)置換算法

最佳置換算法,其所選擇的被淘汰的頁面將是以后永不使用的,或是在最長(未來)時間內不再被訪問的頁面。聽名字定義很顯然頁面未來的使用情況它就不可預測,所以最佳置換算法只是理論上的最優方法,實際上在大多數情況下并沒法完成,所以更多的是作為衡量其他置換算法的標準。

2.2 先進先出(FIFO)置換算法

許多早期的操作系統為了保證算法的簡單,避免高復雜度的算法,嘗試了最簡單的置換策略,即FIFO置換策略,顧名思義就是維護一個頁的隊列,最先進入內存的頁最先被淘汰,這樣的算法復雜度低,系統開銷也極低,但是顯然命中率會下降。

另外考慮一種情況,增大緩存時,一般意義下緩存的命中率必然會上升,但是FIFO算法則在有的時候則會下降,這種現象叫做Belady現象。原因是FIFO算法沒有考慮到程序的局部性原則,踢出的頁面很可能是程序經常性訪問的頁面。

Belady現象的實例分析可以參考belady

2.3 最近最少使用(Least Recently Used,LRU)置換算法

為了解決Belady現象,同時也是基于程序的局部性原則(Locality of reference,指的是在計算機科學計算機科學領域中應用程序在訪問內存的時候,傾向于訪問內存中較為靠近的值。)就有了LRU置換策略,從程序代碼的角度理解局部性原則就是,程序一般傾向于頻繁的訪問某些代碼(比如循環)或者數據結構(比如循環訪問的數組),因此我們應該使用歷史訪問數據來決定,哪些頁應該被踢出,哪些頁不應該被踢出,具體的就是,最近沒有被訪問的頁優先被踢出。這個其實不難理解,通過一個訪問順序的隊列,每次訪問某個頁,就將此頁移到隊首,當內存(緩存)滿了時優先從隊尾踢出相應的頁。(下面給出一個例子,表來自操作系統導論第22章)

但是顯然的是,LRU會帶來更大的系統開銷,因為我們需要頻繁的將訪問過的頁置于訪問序列的首部,這就需要對訪問隊列的內容進行頻繁的增刪,而FIFO只需要簡單排列即可。

2.4 最不經常使用(Least Frequently Used,LFU)置換算法

如果說LRU是通過所有頁面的最近使用情況或者說訪問時間來看,那么LFU即通過訪問頻率來決定哪些頁面需要被踢出,根據程序的局部性原則,顯然LFU會帶來更高的緩存命中率,但是考慮到需要記錄頻率并且頻繁的調整隊列,實際上帶來的系統開銷會比LRU更大。

下圖描述了一個LFU的執行過程,大寫字母為相應的頁,圓圈中的數字代表訪問頻率,訪問頻率最低的在隊列的最后,當需要踢出時,優先踢出隊列末尾的頁。

2.5 時鐘(CLOCK)置換算法

上文談到了LRU和LFU會帶來更高的緩存命中率,但是計算開銷顯然會更大,給系統帶來更高的時間開銷,而一些類LRU算法就出現了,比如時鐘算法。

系統中的所有頁都放在一個循環列表中。時鐘指針(clock hand)開始時指向某個特定的頁(哪個頁不重要)。當必須進行頁替換時,操作系統檢查當前指向的頁P的使用位是1還是0。如果是1,則意味著頁面P最近被使用, 因此不適合被替換。然后,P的使用位設置為0,時鐘指針遞增到下一頁(P+1)。該算法一直持續到找到一個使用位為 0 的頁,使用位為 0 意味著這個頁最近沒有被使用過(在最壞的情況下,所有的頁都已經被使用了,那么就將所有頁的使用位都設置為 0)。

3 樸素LRU的實現

以leetcode146. LRU 緩存機制為例,最直觀樸素的LRU緩存機制可以使用哈希表以及雙向鏈表實現,當然Java的集合LinkedHashMap即實現了一個哈希表+鏈表的組合,可以直接調用實現。

但為了更形象的理解LRU的機制,采用HashMap以及手寫一個雙向鏈表來實現。具體的結構如下圖所示

維護一個大小為緩存容量的map,key值為緩存數據的key,value存儲指向相應節點的引用,數據鏈表使用雙向鏈表便于增刪,當訪問到某條數據時,通過map以O(1)復雜度定位到數據的應用,然后

  • 刪除訪問節點
  • 添加訪問節點到隊首

當節點數量>緩存容量時,刪除隊尾元素,同時移除map中的數據,具體實現如下。

public class LRUCache {
class DLinkedNode {
    int key, value;
    DLinkedNode pre;
    DLinkedNode next;
    public DLinkedNode(){}
    public DLinkedNode(int key, int value){this.key = key; this.value = value;}
}
    private MapInteger, DLinkedNode> cache = new HashMap>();
    private int size;
    private int cal;
    private DLinkedNode head, tail;
    public LRUCache(int capacity) {
        size = 0;
        cal = capacity;
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.pre = head;
    }
    
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            size++;
            addToHead(newNode);
            if (size > cal) {
                DLinkedNode tail = removeTail();
                cache.remove(tail.key);
                size--;
            }
        } else {
            node.value = value;
            moveToHead(node);
        }
    }

    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    private void removeNode(DLinkedNode node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

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

    private DLinkedNode removeTail() {
        DLinkedNode realTail = tail.pre;
        removeNode(realTail);
        return realTail;
    }
}

4 LRU的實際應用

4.1 以Redis為例

上面談到要實現一個樸素的LRU算法,需要維護一個雙向鏈表,存儲前驅、后繼指針,必然會在寸金寸土的緩存(內存)中帶來不必要的開銷。上述提到的時鐘算法就是一種類LRU算法,用更少的系統開銷帶來了接近樸素LRU的命中率。而事實上,Redis中采取的LRU算法也是一種類LRU算法,它也帶來了時鐘算法類似的效果。具體的是Redis通過隨機選取幾個key值,淘汰時間戳最靠前的key,涉及到LRU的淘汰策略maxmemory_policy(這個字段可以選擇不同的緩存淘汰策略,Redis一種提供了8種,本文只分析其中與LRU有關的)的賦值兩種:

  • allkeys-lru: 對所有的鍵都采取LRU淘汰
  • volatile-lru: 僅對設置了過期時間的鍵采取LRU淘汰

可以根據實際情況選擇上述兩種LRU淘汰策略,在一般情況下,程序都存在局部性,或者說冪次分布(二八法則),即少數數據比其他大多數數據訪問的更多,所以通常使用allkeys-lru策略,而當有需求需要長期保留一部分數據在內存中時選取volatile-lru即只規定部分需要淘汰的數據。

下面看一下具體是如何設置的,Redis整體上是一個大的字典,key是一個string,而value都會保存為一個robj(Redis Object),對于robj的定義如下

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits access time). */
    int refcount;
    void *ptr;
} robj;

其中LRU_BITS即Redis為每個鍵值對配備的一個記錄時間戳的bit位,Redis的LFU替換策略中也使用這個空間,創建對象時會寫入到lru_clock這個字段中,訪問對象時會對其進行更新,具體的空間的大小被定義為一個常量

#define REDIS_LRU_BITS 24

大小為24,即使用一個額外的24bit的空間記錄相對時間戳(即對unix time取模之后的結果),默認的時間戳分辨率是1秒,在這種情況下,24bits的空間如果溢出的話需要194天,而作為頻繁更新的緩存而言,這個空間夠用了。

上面提到了Redis會隨機采樣,比較其訪問時間哪個更靠前,當需要替換時優先踢出采樣結果最靠前的鍵值對,具體的采樣個數在最開始是選取3個key,但是效果并不好,后來增加到N個key,但是默認是5個,即默認隨機選取5個key,最先淘汰掉這5個中距離上次訪問時間最久的,Redis3.0中又改善了算法的性能,即提供了一個采樣池(pool),候選采樣池默認大小為16,能夠填充16個key,更新時從Redis鍵空間隨機選擇N個key,分別計算它們的空閑時間idle,key只會在pool不滿或者空閑時間大于pool里最小的時,才會進入pool,然后從pool中選擇空閑時間最大的key淘汰掉。

具體的這個候選集合結構體如下

struct evictionPoolEntry {
    unsigned long long idle; // 用于淘汰排序,在不同算法中意義不同優先淘汰值大的,單位是ms
    sds key;  // 鍵的名字
    // ...
};

所以關鍵在pool中的idle字段的計算,實際上只需要使用當前的時間戳減去lru_clock即可,但是所記錄的時間戳都是取模之后的結果,所以還需要比較當前計算出來的時間戳是否大于lru_clock,如果不是,則需要將當前

時間戳+194天(模數)再減去lru_clock。具體的計算過程如下

// 以秒為精度計算對象距離上一次訪問的間隔時間,然后轉化成毫秒返回
unsigned long long estimateObjectIdleTime(robj *o) {
    unsigned long long lruclock = LRU_CLOCK();
    if (lruclock >= o->lru) {
        return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;
    } else {
        return (lruclock + (LRU_CLOCK_MAX - o->lru)) *
                    LRU_CLOCK_RESOLUTION;
    }
}

另外Redis官方文檔里有對于候選數為5、10在redis2.8無侯選池,以及3.0加侯選池的對比。

四張圖依次是,理論LRU的使用情況、有pool采樣數為10的候選情況、無pool采樣數為5的情況、有pool采樣數為10的情況。其中

  • 綠色部分是新添加的key
  • 灰色部分是最近使用的key淺
  • 灰色部分是替換的key

可以看出采取Redis3.0的采取維護一個候選淘汰池的方法已經能夠接近全局比較情況下也即樸素LRU的結果。

詳細的分析可以參考https://redis.io/topics/lru-cache

4.2 以MySQL的InnoDB引擎為例

此處InnoDB的緩存概念不做過多贅述,只簡單介紹其中LRU的應用,InnoDB會把cpu頻繁使用的數據存儲在主存的緩沖池(Buffer Pool)中,鑒于MySQL在使用過程中存在著經常性的全表掃描,所以如果使用樸素LRU必然會頻繁的大面積替換,造成極低的緩存命中率。

所以InnoDB采取了一種冷熱分離的思路,即將整個緩沖池分為冷區和熱區或者說年輕區(New Sublist)和老區(Old Sublist)

默認情況下距離鏈表尾3/8以上的位置稱為新子列表(熱點區域),以下的位置稱為舊子列表(冷區域),某個頁面初次加載到緩沖池時,放在old區域的頭部。在對某個處于old區域的緩沖頁進行第一次訪問時,就在它對應的控制塊中記錄下這個訪問時間,如果后續的訪問時間和第一次訪問的時間在某個時間間隔內(默認為1000ms),那么該頁面就不會從老的區域移動到年輕區域的頭部,否則將他移動到年輕區域的頭部。

而當緩沖池滿時需要淘汰數據就從old區域的尾部進行淘汰,這樣數據起碼需要兩次(一次為加載到內存,第二次為大于間隔時間的讀取)合理的操作才能移動到年輕區域,有效的預防了全表掃描帶來的命中率降低問題。

更加詳細具體的描述可以參見MySQL官方文檔對InnoDB buffer poll的解釋。

到此這篇關于緩存替換策略及應用(以Redis、InnoDB為例)的文章就介紹到這了,更多相關redis緩存替換策略內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • 詳解Spring boot使用Redis集群替換mybatis二級緩存
  • Redis緩存常用4種策略原理詳解

標簽:臺州 江蘇 北京 吉安 果洛 楊凌 大慶 朝陽

巨人網絡通訊聲明:本文標題《緩存替換策略及應用(以Redis、InnoDB為例)》,本文關鍵詞  緩存,替換,策略,及,應用,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《緩存替換策略及應用(以Redis、InnoDB為例)》相關的同類信息!
  • 本頁收集關于緩存替換策略及應用(以Redis、InnoDB為例)的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    国产传媒久久文化传媒| 国产精品一区免费视频| 亚洲bdsm女犯bdsm网站| 久久精品国产久精国产| 色综合色综合色综合| 精品福利一区二区三区| 亚洲国产综合视频在线观看| 粉嫩13p一区二区三区| 欧美一区二区不卡视频| 一区二区成人在线| av一二三不卡影片| 欧美国产视频在线| 国产精品亚洲专一区二区三区| 国产视频一区在线播放| 国产精品视频yy9299一区| 亚洲国产成人av网| 欧美性大战久久久| 亚洲精品国产一区二区精华液| 国产电影精品久久禁18| 成人的网站免费观看| 欧美国产精品专区| 亚洲一区二区三区精品在线| 在线观看日韩毛片| 国产剧情av麻豆香蕉精品| 一区二区三区欧美日韩| 成人永久aaa| 精品久久久久一区| 国产麻豆一精品一av一免费| 亚洲va天堂va国产va久| 日韩一区二区三区电影 | 国产精品久久久久久久蜜臀| 久久er精品视频| 中文字幕在线观看一区二区| 伊人婷婷欧美激情| 欧美一区二区人人喊爽| 韩国av一区二区三区在线观看| 久久这里只有精品首页| 91香蕉视频黄| 91精品欧美一区二区三区综合在| 国产一区二区精品久久| 一区二区三区在线视频免费观看| 欧美一区二区久久| 99精品在线观看视频| 久久久777精品电影网影网| 日韩亚洲欧美综合| 91无套直看片红桃| 国产在线精品免费| 2023国产一二三区日本精品2022| www.在线成人| 久久成人精品无人区| 欧美变态tickle挠乳网站| 91小视频在线| 综合激情网...| 日韩欧美在线网站| 91福利在线免费观看| 国产一区二区伦理| 三级不卡在线观看| 亚洲激情欧美激情| 亚洲国产精品精华液ab| 日韩精品一区二区三区四区视频| 99精品偷自拍| 国产精品99久久久久| 一区在线观看视频| 一区二区三区四区蜜桃| 国内外成人在线| 欧美三级一区二区| 欧美激情一区二区三区蜜桃视频 | 色综合一区二区| 精品久久久久久久一区二区蜜臀| **性色生活片久久毛片| 狠狠色综合播放一区二区| 91久久人澡人人添人人爽欧美 | 91麻豆精品91久久久久久清纯| 国产视频不卡一区| 热久久久久久久| 在线一区二区三区四区| 久久精品视频免费| 老司机一区二区| 欧美人伦禁忌dvd放荡欲情| 国产精品久久久一本精品| 韩国三级在线一区| 欧美人妇做爰xxxⅹ性高电影 | 7777女厕盗摄久久久| 亚洲精品一二三四区| 不卡一二三区首页| 久久精品一区八戒影视| 麻豆中文一区二区| 欧美精品视频www在线观看 | 午夜电影网一区| 色一情一伦一子一伦一区| 国产精品久久久久久久久果冻传媒 | 天天色天天操综合| 91蜜桃传媒精品久久久一区二区| 欧美变态口味重另类| 麻豆免费看一区二区三区| 91精品国产美女浴室洗澡无遮挡| 亚洲国产精品天堂| 欧美探花视频资源| 亚洲国产成人av网| 欧美日韩一区中文字幕| 一区二区激情小说| 欧美亚洲自拍偷拍| 亚洲综合在线第一页| 欧洲生活片亚洲生活在线观看| 亚洲精品久久久久久国产精华液| 色综合天天视频在线观看| 亚洲欧美aⅴ...| 欧美性受极品xxxx喷水| 丝袜脚交一区二区| 日韩精品一区二区三区在线| 国产在线视频一区二区三区| 亚洲国产成人自拍| 色一情一乱一乱一91av| 亚洲一区在线免费观看| 欧美精选一区二区| 激情深爱一区二区| 国产精品久线在线观看| 欧美三电影在线| 玖玖九九国产精品| 国产欧美日韩另类一区| 色综合久久九月婷婷色综合| 亚洲国产另类av| 亚洲精品在线一区二区| 不卡视频在线观看| 天天操天天干天天综合网| 久久综合色综合88| 91免费观看在线| 蜜臂av日日欢夜夜爽一区| 久久―日本道色综合久久| jiyouzz国产精品久久| 亚洲宅男天堂在线观看无病毒| 欧美一区二区三区在线看| 国产一区二区三区四区五区入口| 国产精品剧情在线亚洲| 欧美日韩一区二区不卡| 黄色成人免费在线| 中文字幕一区二区在线观看| 在线不卡a资源高清| 国产在线精品一区二区不卡了 | 国产日韩欧美不卡在线| 欧美三级视频在线观看| 国产成人综合网站| 中文字幕一区在线观看视频| 欧美性欧美巨大黑白大战| 国产v综合v亚洲欧| 亚洲国产裸拍裸体视频在线观看乱了| 欧美精品一区二区精品网| 粉嫩av一区二区三区| 亚洲成人久久影院| 国产精品久久久久国产精品日日| 91精品国产全国免费观看| 91丨porny丨中文| 国产一区久久久| 亚洲影视在线播放| 国产日韩欧美精品一区| 欧美一级欧美三级| 在线免费观看日韩欧美| 精品国产一区二区精华| 欧美色图天堂网| 国产91精品露脸国语对白| 日本亚洲最大的色成网站www| 亚洲欧洲av在线| 国产日韩亚洲欧美综合| 久久免费看少妇高潮| 91麻豆精品91久久久久久清纯| 在线欧美日韩精品| 91久久精品国产91性色tv| 不卡一卡二卡三乱码免费网站| 国产a久久麻豆| 狠狠狠色丁香婷婷综合激情| 午夜精品久久久久久久久久| 亚洲制服丝袜一区| 亚洲一区二区三区四区在线观看| 亚洲同性同志一二三专区| 久久久美女毛片| 精品国产三级电影在线观看| 在线成人av网站| 日韩一级二级三级| 欧美成人精品二区三区99精品| 91精品国产欧美一区二区成人| 欧美日韩中文字幕一区| av在线这里只有精品| 国产福利一区二区三区视频| 美女在线视频一区| 美日韩一区二区| 国产资源精品在线观看| 国产一区二区视频在线| 成人激情电影免费在线观看| 日韩国产在线一| 一区二区三区视频在线观看| 亚洲一区二区3| 亚洲成人免费av| 亚洲一区中文日韩| 日韩福利视频导航| 奇米一区二区三区av| 激情国产一区二区| 成人视屏免费看| 91丝袜美腿高跟国产极品老师| 欧美制服丝袜第一页| 欧美日韩的一区二区|