中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Redis掃盲:淺談Redis面試必問——工程架構篇

發布時間:2020-07-18 04:54:12 來源:網絡 閱讀:2144 作者:Java_老男孩 欄目:編程語言

前言

接下來我們來一起研究下Redis工程架構相關的問題,這部分內容出現的概率相對大一些,因為并不是所有人都會去研究源碼,如果面試一味問源碼那么可能注定是一場尬聊。

面試時在不要求候選人對Redis非常熟練的前提下,工程問題將是不二之選

通過本文你將了解到以下內容:
1.Redis的內存回收詳解
2.Redis的持久化機制

Q1:了解Redis的內存回收嗎?講講你的理解

1.1 為什么要回收內存?

Redis作為內存型數據庫,如果單純的只進不出早晚就撐爆了,事實上很多把Redis當做主存儲DB用的家伙們早晚會嘗到這個苦果,當然除非你家廠子確實不差錢,數T級別的內存都毛毛雨,或者數據增長一定程度之后不再增長的場景,就另當別論了。

對于我們這種把節約成本當做KPI的普通廠子,還是把Redis當緩存用比較符合家里的經濟條件,所以這么看面試官的問題還算是比較貼合實際,比起那些手撕RBTree好一些,如果問題剛好在你知識射程范圍內,先給面試官點個贊再說!

為了讓Redis服務安全穩定的運行,讓使用內存保持在一定的閾值內是非常有必要的,因此我們就需要刪除該刪除的,清理該清理的,把內存留給需要的鍵值對,試想一條大河需要設置幾個警戒水位來確保不決堤不枯竭,Redis也是一樣的,只不過Redis只關心決堤即可,來一張圖:

Redis掃盲:淺談Redis面試必問——工程架構篇

圖中設定機器內存為128GB,占用64GB算是比較安全的水平,如果內存接近80%也就是100GB左右,那么認為Redis目前承載能力已經比較大了,具體的比例可以根據公司和個人的業務經驗來確定。

筆者只是想表達出于安全和穩定的考慮,不要覺得128GB的內存就意味著存儲128GB的數據,都是要打折的。

1.2 內存從哪里回收?

Redis占用的內存是分為兩部分:存儲鍵值對消耗和本身運行消耗。顯然后者我們無法回收,因此只能從鍵值對下手了,鍵值對可以分為幾種:帶過期的、不帶過期的、熱點數據、冷數據。對于帶過期的鍵值是需要刪除的,如果刪除了所有的過期鍵值對之后內存仍然不足怎么辦?那只能把部分數據給踢掉了。

人生無處不取舍,這個讓筆者腦海浮現了《泰坦尼克》,郵輪撞到了冰山頃刻間海水涌入,面臨數量不足的救生艇,人們做出了抉擇:讓女士和孩童先走,紳士們選擇留下,海上逃生場景如圖:

Redis掃盲:淺談Redis面試必問——工程架構篇

Redis掃盲:淺談Redis面試必問——工程架構篇

1.3 如何實施過期鍵值對的刪除?

要實施對鍵值對的刪除我們需要明白如下幾點:

  • 帶過期超時的鍵值對存儲在哪里?
  • 如何判斷帶超時的鍵值對是否可以被刪除了?
  • 刪除機制有哪些以及如何選擇

1.3.1 鍵值對的存儲

老規矩來到github看下源碼,src/server.h中給的redisDb結構體給出了答案:

typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB */
    dict *expires;              /* Timeout of keys with a timeout set */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    int id;                     /* Database ID */
    long long avg_ttl;          /* Average TTL, just for stats */
    unsigned long expires_cursor; /* Cursor of the active expire cycle. */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;

Redis本質上就是一個大的key-value,key就是字符串,value有是幾種對象:字符串、列表、有序列表、集合、哈希等,這些key-value都是存儲在redisDb的dict中的,來看下黃健宏畫的一張非常贊的圖:

Redis掃盲:淺談Redis面試必問——工程架構篇

看到這里,對于刪除機制又清晰了一步,我們只要把redisDb中dict中的目標key-value刪掉就行,不過貌似沒有這么簡單,Redis對于過期鍵值對肯定有自己的組織規則,讓我們繼續研究吧!

redisDb的expires成員的類型也是dict,和鍵值對是一樣的,本質上expires是dict的子集,expires保存的是所有帶過期的鍵值對,稱之為過期字典吧,它才是我們研究的重點。

對于鍵,我們可以設置絕對和相對過期時間、以及查看剩余時間:

  • 使用EXPIRE和PEXPIRE來實現鍵值對的秒級和毫秒級生存時間設定,這是相對時長的過期設置
  • 使用EXPIREAT和EXPIREAT來實現鍵值對在某個秒級和毫秒級時間戳時進行過期刪除,屬于絕對過期設置
  • 通過TTL和PTTL來查看帶有生存時間的鍵值對的剩余過期時間

上述三組命令在設計緩存時用處比較大,有心的讀者可以留意。

過期字典expires和鍵值對空間dict存儲的內容并不完全一樣,過期字典expires的key是指向Redis對應對象的指針,其value是long long型的unix時間戳,前面的EXPIRE和PEXPIRE相對時長最終也會轉換為時間戳,來看下過期字典expires的結構,筆者畫了個圖:

Redis掃盲:淺談Redis面試必問——工程架構篇

1.3.2 鍵值對的過期刪除判斷

判斷鍵是否過期可刪除,需要先查過期字典是否存在該值,如果存在則進一步判斷過期時間戳和當前時間戳的相對大小,做出刪除判斷,簡單的流程如圖:

Redis掃盲:淺談Redis面試必問——工程架構篇

1.3.3 鍵值對的刪除策略

經過前面的幾個環節,我們知道了Redis的兩種存儲位置:鍵空間和過期字典,以及過期字典expires的結構、判斷是否過期的方法,那么該如何實施刪除呢?

先拋開Redis來想一下可能的幾種刪除策略:

  • 定時刪除:在設置鍵的過期時間的同時,創建定時器,讓定時器在鍵過期時間到來時,即刻執行鍵值對的刪除;
  • 定期刪除:每隔特定的時間對數據庫進行一次掃描,檢測并刪除其中的過期鍵值對;
  • 惰性刪除:鍵值對過期暫時不進行刪除,至于刪除的時機與鍵值對的使用有關,當獲取鍵時先查看其是否過期,過期就刪除,否則就保留;

在上述的三種策略中定時刪除和定期刪除屬于不同時間粒度的主動刪除惰性刪除屬于被動刪除

三種策略都有各自的優缺點:

定時刪除對內存使用率有優勢,但是對CPU不友好,惰性刪除對內存不友好,如果某些鍵值對一直不被使用,那么會造成一定量的內存浪費,定期刪除是定時刪除和惰性刪除的折中。

Reids采用的是惰性刪除和定時刪除的結合,一般來說可以借助最小堆來實現定時器,不過Redis的設計考慮到時間事件的有限種類和數量,使用了無序鏈表存儲時間事件,這樣如果在此基礎上實現定時刪除,就意味著O(N)遍歷獲取最近需要刪除的數據。

但是我覺得antirez如果非要使用定時刪除,那么他肯定不會使用原來的無序鏈表機制,所以個人認為已存在的無序鏈表不能作為Redis不使用定時刪除的根本理由冒昧猜測唯一可能的是antirez覺得沒有必要使用定時刪除

Redis掃盲:淺談Redis面試必問——工程架構篇

1.3.4 定期刪除的實現細節

定期刪除聽著很簡單,但是如何控制執行的頻率和時長呢?

試想一下如果執行頻率太少就退化為惰性刪除了,如果執行時間太長又和定時刪除類似了,想想還確實是個難題!并且執行定期刪除的時機也需要考慮,所以我們繼續來看看Redis是如何實現定期刪除的吧!筆者在src/expire.c文件中找到了activeExpireCycle函數,定期刪除就是由此函數實現的,在代碼中antirez做了比較詳盡的注釋,不過都是英文的,試著讀了一下模模糊糊弄個大概所以學習英文并閱讀外文資料是很重要的學習途徑

由于筆者對Redis源碼了解不多,只能做個模糊版本的解讀,所以難免有問題,還是建議有條件的讀者自行前往源碼區閱讀,拋磚引玉看下筆者的模糊版本:

  • 該算法是個自適應的過程,當過期的key比較少時那么就花費很少的cpu時間來處理,如果過期的key很多就采用激進的方式來處理,避免大量的內存消耗,可以理解為判斷過期鍵多就多跑幾次,少則少跑幾次;
  • 由于Redis中有很多數據庫db,該算法會逐個掃描,本次結束時繼續向后面的db掃描,是個閉環的過程;
  • 定期刪除有快速循環和慢速循環兩種模式,主要采用慢速循環模式,其循環頻率主要取決于server.hz,通常設置為10,也就是每秒執行10次慢循環定期刪除,執行過程中如果耗時超過25%的CPU時間就停止;
  • 慢速循環的執行時間相對較長,會出現超時問題,快速循環模式的執行時間不超過1ms,也就是執行時間更短,但是執行的次數更多,在執行過程中發現某個db中抽樣的key中過期key占比低于25%則跳過;

主體意思:定期刪除是個自適應的閉環并且概率化的抽樣掃描過程,過程中都有執行時間和cpu時間的限制,如果觸發閾值就停止,可以說是盡量在不影響對客戶端的響應下潤物細無聲地進行的。

1.3.5 DEL刪除鍵值對

在Redis4.0之前執行del操作時如果key-value很大,那么可能導致阻塞,在新版本中引入了BIO線程以及一些新的命令,實現了del的延時懶刪除,最后會有BIO線程來實現內存的清理回收。

1.4 內存淘汰機制

為了保證Redis的安全穩定運行,設置了一個max-memory的閾值,那么當內存用量到達閾值,新寫入的鍵值對無法寫入,此時就需要內存淘汰機制,在Redis的配置中有幾種淘汰策略可以選擇,詳細如下:

  • noeviction: 當內存不足以容納新寫入數據時,新寫入操作會報錯;
  • allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中移除最近最少使用的 key;
  • allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中隨機移除某個 key;
  • volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的 key;
  • volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個 key;
  • volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的 key 優先移除;

后三種策略都是針對過期字典的處理,但是在過期字典為空時會noeviction一樣返回寫入失敗,毫無策略地隨機刪除也不太可取,所以一般選擇第二種allkeys-lru基于LRU策略進行淘汰。

個人認為antirez一向都是工程化思維,善于使用概率化設計來做近似實現,LRU算法也不例外,Redis中實現了近似LRU算法,并且經過幾個版本的迭代效果已經比較接近理論LRU算法的效果了,這個也是個不錯的內容,由于篇幅限制,本文計劃后續單獨講LRU算法時再進行詳細討論。

1.5 過期鍵刪除和內存淘汰的關系

過期健刪除策略強調的是對過期健的操作,如果有健過期而內存足夠,Redis不會使用內存淘汰機制來騰退空間,這時會優先使用過期健刪除策略刪除過期健。

內存淘汰機制強調的是對內存數據的淘汰操作,當內存不足時,即使有的健沒有到達過期時間或者根本沒有設置過期也要根據一定的策略來刪除一部分,騰退空間保證新數據的寫入。

Q2:講講你對Redis持久化機制的理解。

個人認為Redis持久化既是數據庫本身的亮點,也是面試的熱點,主要考察的方向包括:RDB機制原理、AOF機制原理、各自的優缺點、工程上的對于RDB和AOF的取舍、新版本Redis混合持久化策略等,如能把握要點,持久化問題就過關了。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

阿尔山市| 米林县| 县级市| 息烽县| 鲁甸县| 广宁县| 来安县| 蚌埠市| 卓资县| 堆龙德庆县| 澄江县| 汝南县| 清水县| 洞头县| 桐梓县| 科尔| 同江市| 雷波县| 肥乡县| 西昌市| 通榆县| 石渠县| 屏山县| 通海县| 广汉市| 个旧市| 钟祥市| 稷山县| 平泉县| 赫章县| 枝江市| 军事| 南京市| 尼勒克县| 信宜市| 闵行区| 会东县| 阜平县| 公安县| 曲麻莱县| 延吉市|