Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

前言

接下來(lái)我們來(lái)一起研究下redis工程架構(gòu)相關(guān)的問(wèn)題,這部分內(nèi)容出現(xiàn)的概率相對(duì)大一些,因?yàn)椴⒉皇撬腥硕紩?huì)去研究源碼,如果面試一味問(wèn)源碼那么可能注定是一場(chǎng)尬聊。

梨樹網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)于2013年創(chuàng)立到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。

面試時(shí)在不要求候選人對(duì)Redis非常熟練的前提下,工程問(wèn)題將是不二之選。

通過(guò)本文你將了解到以下內(nèi)容:
1.Redis的內(nèi)存回收詳解
2.Redis的持久化機(jī)制

Q1:了解Redis的內(nèi)存回收嗎?講講你的理解

1.1 為什么要回收內(nèi)存?

Redis作為內(nèi)存型數(shù)據(jù)庫(kù),如果單純的只進(jìn)不出早晚就撐爆了,事實(shí)上很多把Redis當(dāng)做主存儲(chǔ)DB用的家伙們?cè)缤頃?huì)嘗到這個(gè)苦果,當(dāng)然除非你家廠子確實(shí)不差錢,數(shù)T級(jí)別的內(nèi)存都毛毛雨,或者數(shù)據(jù)增長(zhǎng)一定程度之后不再增長(zhǎng)的場(chǎng)景,就另當(dāng)別論了。

對(duì)于我們這種把節(jié)約成本當(dāng)做KPI的普通廠子,還是把Redis當(dāng)緩存用比較符合家里的經(jīng)濟(jì)條件,所以這么看面試官的問(wèn)題還算是比較貼合實(shí)際,比起那些手撕RBTree好一些,如果問(wèn)題剛好在你知識(shí)射程范圍內(nèi),先給面試官點(diǎn)個(gè)贊再說(shuō)!

為了讓Redis服務(wù)安全穩(wěn)定的運(yùn)行,讓使用內(nèi)存保持在一定的閾值內(nèi)是非常有必要的,因此我們就需要?jiǎng)h除該刪除的,清理該清理的,把內(nèi)存留給需要的鍵值對(duì),試想一條大河需要設(shè)置幾個(gè)警戒水位來(lái)確保不決堤不枯竭,Redis也是一樣的,只不過(guò)Redis只關(guān)心決堤即可,來(lái)一張圖:

Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

圖中設(shè)定機(jī)器內(nèi)存為128GB,占用64GB算是比較安全的水平,如果內(nèi)存接近80%也就是100GB左右,那么認(rèn)為Redis目前承載能力已經(jīng)比較大了,具體的比例可以根據(jù)公司和個(gè)人的業(yè)務(wù)經(jīng)驗(yàn)來(lái)確定。

筆者只是想表達(dá)出于安全和穩(wěn)定的考慮,不要覺(jué)得128GB的內(nèi)存就意味著存儲(chǔ)128GB的數(shù)據(jù),都是要打折的。

1.2 內(nèi)存從哪里回收?

Redis占用的內(nèi)存是分為兩部分:存儲(chǔ)鍵值對(duì)消耗和本身運(yùn)行消耗。顯然后者我們無(wú)法回收,因此只能從鍵值對(duì)下手了,鍵值對(duì)可以分為幾種:帶過(guò)期的、不帶過(guò)期的、熱點(diǎn)數(shù)據(jù)、冷數(shù)據(jù)。對(duì)于帶過(guò)期的鍵值是需要?jiǎng)h除的,如果刪除了所有的過(guò)期鍵值對(duì)之后內(nèi)存仍然不足怎么辦?那只能把部分?jǐn)?shù)據(jù)給踢掉了。

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

Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

1.3 如何實(shí)施過(guò)期鍵值對(duì)的刪除?

要實(shí)施對(duì)鍵值對(duì)的刪除我們需要明白如下幾點(diǎn):

  • 帶過(guò)期超時(shí)的鍵值對(duì)存儲(chǔ)在哪里?
  • 如何判斷帶超時(shí)的鍵值對(duì)是否可以被刪除了?
  • 刪除機(jī)制有哪些以及如何選擇?

1.3.1 鍵值對(duì)的存儲(chǔ)

老規(guī)矩來(lái)到github看下源碼,src/server.h中給的redisDb結(jié)構(gòu)體給出了答案:

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本質(zhì)上就是一個(gè)大的key-value,key就是字符串,value有是幾種對(duì)象:字符串、列表、有序列表、集合、哈希等,這些key-value都是存儲(chǔ)在redisDb的dict中的,來(lái)看下黃健宏畫的一張非常贊的圖:

Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

看到這里,對(duì)于刪除機(jī)制又清晰了一步,我們只要把redisDb中dict中的目標(biāo)key-value刪掉就行,不過(guò)貌似沒(méi)有這么簡(jiǎn)單,Redis對(duì)于過(guò)期鍵值對(duì)肯定有自己的組織規(guī)則,讓我們繼續(xù)研究吧!

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

對(duì)于鍵,我們可以設(shè)置絕對(duì)和相對(duì)過(guò)期時(shí)間、以及查看剩余時(shí)間:

  • 使用EXPIRE和PEXPIRE來(lái)實(shí)現(xiàn)鍵值對(duì)的秒級(jí)和毫秒級(jí)生存時(shí)間設(shè)定,這是相對(duì)時(shí)長(zhǎng)的過(guò)期設(shè)置
  • 使用EXPIREAT和EXPIREAT來(lái)實(shí)現(xiàn)鍵值對(duì)在某個(gè)秒級(jí)和毫秒級(jí)時(shí)間戳?xí)r進(jìn)行過(guò)期刪除,屬于絕對(duì)過(guò)期設(shè)置
  • 通過(guò)TTL和PTTL來(lái)查看帶有生存時(shí)間的鍵值對(duì)的剩余過(guò)期時(shí)間

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

過(guò)期字典expires和鍵值對(duì)空間dict存儲(chǔ)的內(nèi)容并不完全一樣,過(guò)期字典expires的key是指向Redis對(duì)應(yīng)對(duì)象的指針,其value是long long型的unix時(shí)間戳,前面的EXPIRE和PEXPIRE相對(duì)時(shí)長(zhǎng)最終也會(huì)轉(zhuǎn)換為時(shí)間戳,來(lái)看下過(guò)期字典expires的結(jié)構(gòu),筆者畫了個(gè)圖:

Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

1.3.2 鍵值對(duì)的過(guò)期刪除判斷

判斷鍵是否過(guò)期可刪除,需要先查過(guò)期字典是否存在該值,如果存在則進(jìn)一步判斷過(guò)期時(shí)間戳和當(dāng)前時(shí)間戳的相對(duì)大小,做出刪除判斷,簡(jiǎn)單的流程如圖:

Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

1.3.3 鍵值對(duì)的刪除策略

經(jīng)過(guò)前面的幾個(gè)環(huán)節(jié),我們知道了Redis的兩種存儲(chǔ)位置:鍵空間和過(guò)期字典,以及過(guò)期字典expires的結(jié)構(gòu)、判斷是否過(guò)期的方法,那么該如何實(shí)施刪除呢?

先拋開(kāi)Redis來(lái)想一下可能的幾種刪除策略:

  • 定時(shí)刪除:在設(shè)置鍵的過(guò)期時(shí)間的同時(shí),創(chuàng)建定時(shí)器,讓定時(shí)器在鍵過(guò)期時(shí)間到來(lái)時(shí),即刻執(zhí)行鍵值對(duì)的刪除;
  • 定期刪除:每隔特定的時(shí)間對(duì)數(shù)據(jù)庫(kù)進(jìn)行一次掃描,檢測(cè)并刪除其中的過(guò)期鍵值對(duì);
  • 惰性刪除:鍵值對(duì)過(guò)期暫時(shí)不進(jìn)行刪除,至于刪除的時(shí)機(jī)與鍵值對(duì)的使用有關(guān),當(dāng)獲取鍵時(shí)先查看其是否過(guò)期,過(guò)期就刪除,否則就保留;

在上述的三種策略中定時(shí)刪除和定期刪除屬于不同時(shí)間粒度的主動(dòng)刪除,惰性刪除屬于被動(dòng)刪除

三種策略都有各自的優(yōu)缺點(diǎn):

定時(shí)刪除對(duì)內(nèi)存使用率有優(yōu)勢(shì),但是對(duì)CPU不友好,惰性刪除對(duì)內(nèi)存不友好,如果某些鍵值對(duì)一直不被使用,那么會(huì)造成一定量的內(nèi)存浪費(fèi),定期刪除是定時(shí)刪除和惰性刪除的折中。

Reids采用的是惰性刪除和定時(shí)刪除的結(jié)合,一般來(lái)說(shuō)可以借助最小堆來(lái)實(shí)現(xiàn)定時(shí)器,不過(guò)Redis的設(shè)計(jì)考慮到時(shí)間事件的有限種類和數(shù)量,使用了無(wú)序鏈表存儲(chǔ)時(shí)間事件,這樣如果在此基礎(chǔ)上實(shí)現(xiàn)定時(shí)刪除,就意味著O(N)遍歷獲取最近需要?jiǎng)h除的數(shù)據(jù)。

但是我覺(jué)得antirez如果非要使用定時(shí)刪除,那么他肯定不會(huì)使用原來(lái)的無(wú)序鏈表機(jī)制,所以個(gè)人認(rèn)為已存在的無(wú)序鏈表不能作為Redis不使用定時(shí)刪除的根本理由,冒昧猜測(cè)唯一可能的是antirez覺(jué)得沒(méi)有必要使用定時(shí)刪除。

Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇

1.3.4 定期刪除的實(shí)現(xiàn)細(xì)節(jié)

定期刪除聽(tīng)著很簡(jiǎn)單,但是如何控制執(zhí)行的頻率和時(shí)長(zhǎng)呢?

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

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

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

主體意思:定期刪除是個(gè)自適應(yīng)的閉環(huán)并且概率化的抽樣掃描過(guò)程,過(guò)程中都有執(zhí)行時(shí)間和cpu時(shí)間的限制,如果觸發(fā)閾值就停止,可以說(shuō)是盡量在不影響對(duì)客戶端的響應(yīng)下潤(rùn)物細(xì)無(wú)聲地進(jìn)行的。

1.3.5 DEL刪除鍵值對(duì)

在Redis4.0之前執(zhí)行del操作時(shí)如果key-value很大,那么可能導(dǎo)致阻塞,在新版本中引入了BIO線程以及一些新的命令,實(shí)現(xiàn)了del的延時(shí)懶刪除,最后會(huì)有BIO線程來(lái)實(shí)現(xiàn)內(nèi)存的清理回收。

1.4 內(nèi)存淘汰機(jī)制

為了保證Redis的安全穩(wěn)定運(yùn)行,設(shè)置了一個(gè)max-memory的閾值,那么當(dāng)內(nèi)存用量到達(dá)閾值,新寫入的鍵值對(duì)無(wú)法寫入,此時(shí)就需要內(nèi)存淘汰機(jī)制,在Redis的配置中有幾種淘汰策略可以選擇,詳細(xì)如下:

  • noeviction: 當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),新寫入操作會(huì)報(bào)錯(cuò);
  • allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中移除最近最少使用的 key;
  • allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中隨機(jī)移除某個(gè) key;
  • volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過(guò)期時(shí)間的鍵空間中,移除最近最少使用的 key;
  • volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過(guò)期時(shí)間的鍵空間中,隨機(jī)移除某個(gè) key;
  • volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在設(shè)置了過(guò)期時(shí)間的鍵空間中,有更早過(guò)期時(shí)間的 key 優(yōu)先移除;

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

個(gè)人認(rèn)為antirez一向都是工程化思維,善于使用概率化設(shè)計(jì)來(lái)做近似實(shí)現(xiàn),LRU算法也不例外,Redis中實(shí)現(xiàn)了近似LRU算法,并且經(jīng)過(guò)幾個(gè)版本的迭代效果已經(jīng)比較接近理論LRU算法的效果了,這個(gè)也是個(gè)不錯(cuò)的內(nèi)容,由于篇幅限制,本文計(jì)劃后續(xù)單獨(dú)講LRU算法時(shí)再進(jìn)行詳細(xì)討論。

1.5 過(guò)期鍵刪除和內(nèi)存淘汰的關(guān)系

過(guò)期健刪除策略強(qiáng)調(diào)的是對(duì)過(guò)期健的操作,如果有健過(guò)期而內(nèi)存足夠,Redis不會(huì)使用內(nèi)存淘汰機(jī)制來(lái)騰退空間,這時(shí)會(huì)優(yōu)先使用過(guò)期健刪除策略刪除過(guò)期健。

內(nèi)存淘汰機(jī)制強(qiáng)調(diào)的是對(duì)內(nèi)存數(shù)據(jù)的淘汰操作,當(dāng)內(nèi)存不足時(shí),即使有的健沒(méi)有到達(dá)過(guò)期時(shí)間或者根本沒(méi)有設(shè)置過(guò)期也要根據(jù)一定的策略來(lái)刪除一部分,騰退空間保證新數(shù)據(jù)的寫入。

Q2:講講你對(duì)Redis持久化機(jī)制的理解。

個(gè)人認(rèn)為Redis持久化既是數(shù)據(jù)庫(kù)本身的亮點(diǎn),也是面試的熱點(diǎn),主要考察的方向包括:RDB機(jī)制原理、AOF機(jī)制原理、各自的優(yōu)缺點(diǎn)、工程上的對(duì)于RDB和AOF的取舍、新版本Redis混合持久化策略等,如能把握要點(diǎn),持久化問(wèn)題就過(guò)關(guān)了。

網(wǎng)頁(yè)題目:Redis掃盲:淺談Redis面試必問(wèn)——工程架構(gòu)篇
文章出自:http://muchs.cn/article24/iehpce.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、服務(wù)器托管、定制開(kāi)發(fā)、自適應(yīng)網(wǎng)站App設(shè)計(jì)、網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)