欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長(zhǎng)資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          本篇文章給大家整理分享一些Redis高頻面試題,帶大家過一遍Redis核心知識(shí)點(diǎn),涉及到數(shù)據(jù)結(jié)構(gòu)、內(nèi)存模型、 IO 模型、持久化 RDB等,希望對(duì)大家有所幫助!

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          Redis 為什么這么快?

          很多人只知道是 K/V NoSQl 內(nèi)存數(shù)據(jù)庫,單線程……這都是沒有全面理解 Redis 導(dǎo)致無法繼續(xù)深問下去。

          這個(gè)問題是基礎(chǔ)摸底,我們可以從 Redis 不同數(shù)據(jù)類型底層的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)、完全基于內(nèi)存、IO 多路復(fù)用網(wǎng)絡(luò)模型、線程模型、漸進(jìn)式 rehash……

          到底有多快?

          我們可以先說到底有多快,根據(jù)官方數(shù)據(jù),Redis 的 QPS 可以達(dá)到約 100000(每秒請(qǐng)求數(shù)),有興趣的可以參考官方的基準(zhǔn)程序測(cè)試《How fast is Redis?》,地址:redis.io/topics/benc…

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          橫軸是連接數(shù),縱軸是 QPS。

          這張圖反映了一個(gè)數(shù)量級(jí),通過量化讓面試官覺得你有看過官方文檔,很嚴(yán)謹(jǐn)。

          基于內(nèi)存實(shí)現(xiàn)

          Redis 是基于內(nèi)存的數(shù)據(jù)庫,跟磁盤數(shù)據(jù)庫相比,完全吊打磁盤的速度。

          不論讀寫操作都是在內(nèi)存上完成的,我們分別對(duì)比下內(nèi)存操作與磁盤操作的差異。

          磁盤調(diào)用

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          內(nèi)存操作

          內(nèi)存直接由 CPU 控制,也就是 CPU 內(nèi)部集成的內(nèi)存控制器,所以說內(nèi)存是直接與 CPU 對(duì)接,享受與 CPU 通信的最優(yōu)帶寬。

          最后以一張圖量化系統(tǒng)的各種延時(shí)時(shí)間(部分?jǐn)?shù)據(jù)引用 Brendan Gregg)

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          高效的數(shù)據(jù)結(jié)構(gòu)

          學(xué)習(xí) MySQL 的時(shí)候我知道為了提高檢索速度使用了 B+ Tree 數(shù)據(jù)結(jié)構(gòu),所以 Redis 速度快應(yīng)該也跟數(shù)據(jù)結(jié)構(gòu)有關(guān)。

          Redis 一共有 5 種數(shù)據(jù)類型,String、List、Hash、Set、SortedSet

          不同的數(shù)據(jù)類型底層使用了一種或者多種數(shù)據(jù)結(jié)構(gòu)來支撐,目的就是為了追求更快的速度。

          碼哥寄語:我們可以分別說明每種數(shù)據(jù)類型底層的數(shù)據(jù)結(jié)構(gòu)優(yōu)點(diǎn),很多人只知道數(shù)據(jù)類型,而說出底層數(shù)據(jù)結(jié)構(gòu)就能讓人眼前一亮。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          SDS 簡(jiǎn)單動(dòng)態(tài)字符串優(yōu)勢(shì)

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          • SDS 中 len 保存這字符串的長(zhǎng)度,O(1) 時(shí)間復(fù)雜度查詢字符串長(zhǎng)度信息。

          • 空間預(yù)分配:SDS 被修改后,程序不僅會(huì)為 SDS 分配所需要的必須空間,還會(huì)分配額外的未使用空間。

          • 惰性空間釋放:當(dāng)對(duì) SDS 進(jìn)行縮短操作時(shí),程序并不會(huì)回收多余的內(nèi)存空間,而是使用 free 字段將這些字節(jié)數(shù)量記錄下來不釋放,后面如果需要 append 操作,則直接使用 free 中未使用的空間,減少了內(nèi)存的分配。

          zipList 壓縮列表

          壓縮列表是 List 、hash、 sorted Set 三種數(shù)據(jù)類型底層實(shí)現(xiàn)之一。

          當(dāng)一個(gè)列表只有少量數(shù)據(jù)的時(shí)候,并且每個(gè)列表項(xiàng)要么就是小整數(shù)值,要么就是長(zhǎng)度比較短的字符串,那么 Redis 就會(huì)使用壓縮列表來做列表鍵的底層實(shí)現(xiàn)。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          這樣內(nèi)存緊湊,節(jié)約內(nèi)存。

          quicklist

          后續(xù)版本對(duì)列表數(shù)據(jù)結(jié)構(gòu)進(jìn)行了改造,使用 quicklist 代替了 ziplist 和 linkedlist。

          quicklist 是 ziplist 和 linkedlist 的混合體,它將 linkedlist 按段切分,每一段使用 ziplist 來緊湊存儲(chǔ),多個(gè) ziplist 之間使用雙向指針串接起來。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          skipList 跳躍表

          sorted set 類型的排序功能便是通過「跳躍列表」數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)。

          跳躍表(skiplist)是一種有序數(shù)據(jù)結(jié)構(gòu),它通過在每個(gè)節(jié)點(diǎn)中維持多個(gè)指向其他節(jié)點(diǎn)的指針,從而達(dá)到快速訪問節(jié)點(diǎn)的目的。

          跳表在鏈表的基礎(chǔ)上,增加了多層級(jí)索引,通過索引位置的幾個(gè)跳轉(zhuǎn),實(shí)現(xiàn)數(shù)據(jù)的快速定位,如下圖所示:

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          整數(shù)數(shù)組(intset)

          當(dāng)一個(gè)集合只包含整數(shù)值元素,并且這個(gè)集合的元素?cái)?shù)量不多時(shí),Redis 就會(huì)使用整數(shù)集合作為集合鍵的底層實(shí)現(xiàn),節(jié)省內(nèi)存。

          單線程模型

          碼哥寄語:我們需要注意的是,Redis 的單線程指的是 Redis 的網(wǎng)絡(luò) IO (6.x 版本后網(wǎng)絡(luò) IO 使用多線程)以及鍵值對(duì)指令讀寫是由一個(gè)線程來執(zhí)行的。 對(duì)于 Redis 的持久化、集群數(shù)據(jù)同步、異步刪除等都是其他線程執(zhí)行。

          千萬別說 Redis 就只有一個(gè)線程。

          單線程指的是 Redis 鍵值對(duì)讀寫指令的執(zhí)行是單線程。

          先說官方答案,讓人覺得足夠嚴(yán)謹(jǐn),而不是人云亦云去背誦一些博客。

          官方答案:因?yàn)?Redis 是基于內(nèi)存的操作,CPU 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是機(jī)器內(nèi)存的大小或者網(wǎng)絡(luò)帶寬。既然單線程容易實(shí)現(xiàn),而且 CPU 不會(huì)成為瓶頸,那就順理成章地采用單線程的方案了。原文地址:redis.io/topics/faq。

          為啥不用多線程執(zhí)行充分利用 CPU 呢?

          在運(yùn)行每個(gè)任務(wù)之前,CPU 需要知道任務(wù)在何處加載并開始運(yùn)行。也就是說,系統(tǒng)需要幫助它預(yù)先設(shè)置 CPU 寄存器和程序計(jì)數(shù)器,這稱為 CPU 上下文。

          切換上下文時(shí),我們需要完成一系列工作,這是非常消耗資源的操作。

          引入多線程開發(fā),就需要使用同步原語來保護(hù)共享資源的并發(fā)讀寫,增加代碼復(fù)雜度和調(diào)試難度。

          單線程又什么好處?

          1. 不會(huì)因?yàn)榫€程創(chuàng)建導(dǎo)致的性能消耗;
          2. 避免上下文切換引起的 CPU 消耗,沒有多線程切換的開銷;
          3. 避免了線程之間的競(jìng)爭(zhēng)問題,比如添加鎖、釋放鎖、死鎖等,不需要考慮各種鎖問題。
          4. 代碼更清晰,處理邏輯簡(jiǎn)單。

          I/O 多路復(fù)用模型

          Redis 采用 I/O 多路復(fù)用技術(shù),并發(fā)處理連接。采用了 epoll + 自己實(shí)現(xiàn)的簡(jiǎn)單的事件框架。

          epoll 中的讀、寫、關(guān)閉、連接都轉(zhuǎn)化成了事件,然后利用 epoll 的多路復(fù)用特性,絕不在 IO 上浪費(fèi)一點(diǎn)時(shí)間。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          Redis 線程不會(huì)阻塞在某一個(gè)特定的監(jiān)聽或已連接套接字上,也就是說,不會(huì)阻塞在某一個(gè)特定的客戶端請(qǐng)求處理上。正因?yàn)榇?,Redis 可以同時(shí)和多個(gè)客戶端連接并處理請(qǐng)求,從而提升并發(fā)性。

          Redis 全局 hash 字典

          Redis 整體就是一個(gè) 哈希表來保存所有的鍵值對(duì),無論數(shù)據(jù)類型是 5 種的任意一種。哈希表,本質(zhì)就是一個(gè)數(shù)組,每個(gè)元素被叫做哈希桶,不管什么數(shù)據(jù)類型,每個(gè)桶里面的 entry 保存著實(shí)際具體值的指針。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          而哈希表的時(shí)間復(fù)雜度是 O(1),只需要計(jì)算每個(gè)鍵的哈希值,便知道對(duì)應(yīng)的哈希桶位置,定位桶里面的 entry 找到對(duì)應(yīng)數(shù)據(jù),這個(gè)也是 Redis 快的原因之一。

          Redis 使用對(duì)象(redisObject)來表示數(shù)據(jù)庫中的鍵值,當(dāng)我們?cè)?Redis 中創(chuàng)建一個(gè)鍵值對(duì)時(shí),至少創(chuàng)建兩個(gè)對(duì)象,一個(gè)對(duì)象是用做鍵值對(duì)的鍵對(duì)象,另一個(gè)是鍵值對(duì)的值對(duì)象。

          也就是每個(gè) entry 保存著 「鍵值對(duì)」的 redisObject 對(duì)象,通過 redisObject 的指針找到對(duì)應(yīng)數(shù)據(jù)。

          typedef struct redisObject{    //類型   unsigned type:4;   //編碼   unsigned encoding:4;   //指向底層數(shù)據(jù)結(jié)構(gòu)的指針   void *ptr;    //...  }robj;復(fù)制代碼

          Hash 沖突怎么辦?

          Redis 通過鏈?zhǔn)焦?/strong>解決沖突:也就是同一個(gè) 桶里面的元素使用鏈表保存。但是當(dāng)鏈表過長(zhǎng)就會(huì)導(dǎo)致查找性能變差可能,所以 Redis 為了追求快,使用了兩個(gè)全局哈希表。用于 rehash 操作,增加現(xiàn)有的哈希桶數(shù)量,減少哈希沖突。

          開始默認(rèn)使用 「hash 表 1 」保存鍵值對(duì)數(shù)據(jù),「hash 表 2」 此刻沒有分配空間。當(dāng)數(shù)據(jù)越來多觸發(fā) rehash 操作,則執(zhí)行以下操作:

          1. 給 「hash 表 2 」分配更大的空間;
          2. 將 「hash 表 1 」的數(shù)據(jù)重新映射拷貝到 「hash 表 2」 中;
          3. 釋放 hash 表 1 的空間。

          值得注意的是,將 hash 表 1 的數(shù)據(jù)重新映射到 hash 表 2 的過程中并不是一次性的,這樣會(huì)造成 Redis 阻塞,無法提供服務(wù)。

          而是采用了漸進(jìn)式 rehash,每次處理客戶端請(qǐng)求的時(shí)候,先從「 hash 表 1」 中第一個(gè)索引開始,將這個(gè)位置的 所有數(shù)據(jù)拷貝到 「hash 表 2」 中,就這樣將 rehash 分散到多次請(qǐng)求過程中,避免耗時(shí)阻塞。

          Redis 如何實(shí)現(xiàn)持久化?宕機(jī)后如何恢復(fù)數(shù)據(jù)?

          Redis 的數(shù)據(jù)持久化使用了「RDB 數(shù)據(jù)快照」的方式來實(shí)現(xiàn)宕機(jī)快速恢復(fù)。但是 過于頻繁的執(zhí)行全量數(shù)據(jù)快照,有兩個(gè)嚴(yán)重性能開銷:

          1. 頻繁生成 RDB 文件寫入磁盤,磁盤壓力過大。會(huì)出現(xiàn)上一個(gè) RDB 還未執(zhí)行完,下一個(gè)又開始生成,陷入死循環(huán)。
          2. fork 出 bgsave 子進(jìn)程會(huì)阻塞主線程,主線程的內(nèi)存越大,阻塞時(shí)間越長(zhǎng)。

          所以 Redis 還設(shè)計(jì)了 AOF 寫后日志記錄對(duì)內(nèi)存進(jìn)行修改的指令記錄。

          面試官:什么是 RDB 內(nèi)存快照?

          在 Redis 執(zhí)行「寫」指令過程中,內(nèi)存數(shù)據(jù)會(huì)一直變化。所謂的內(nèi)存快照,指的就是 Redis 內(nèi)存中的數(shù)據(jù)在某一刻的狀態(tài)數(shù)據(jù)。

          好比時(shí)間定格在某一刻,當(dāng)我們拍照的,通過照片就能把某一刻的瞬間畫面完全記錄下來。

          Redis 跟這個(gè)類似,就是把某一刻的數(shù)據(jù)以文件的形式拍下來,寫到磁盤上。這個(gè)快照文件叫做 RDB 文件,RDB 就是 Redis DataBase 的縮寫。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          在做數(shù)據(jù)恢復(fù)時(shí),直接將 RDB 文件讀入內(nèi)存完成恢復(fù)。

          面試官:在生成 RDB 期間,Redis 可以同時(shí)處理寫請(qǐng)求么?

          可以的,Redis 使用操作系統(tǒng)的多進(jìn)程寫時(shí)復(fù)制技術(shù) COW(Copy On Write) 來實(shí)現(xiàn)快照持久化,保證數(shù)據(jù)一致性。

          Redis 在持久化時(shí)會(huì)調(diào)用 glibc 的函數(shù)fork產(chǎn)生一個(gè)子進(jìn)程,快照持久化完全交給子進(jìn)程來處理,父進(jìn)程繼續(xù)處理客戶端請(qǐng)求。

          當(dāng)主線程執(zhí)行寫指令修改數(shù)據(jù)的時(shí)候,這個(gè)數(shù)據(jù)就會(huì)復(fù)制一份副本, bgsave 子進(jìn)程讀取這個(gè)副本數(shù)據(jù)寫到 RDB 文件。

          這既保證了快照的完整性,也允許主線程同時(shí)對(duì)數(shù)據(jù)進(jìn)行修改,避免了對(duì)正常業(yè)務(wù)的影響。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          面試官:那 AOF 又是什么?

          AOF 日志記錄了自 Redis 實(shí)例創(chuàng)建以來所有的修改性指令序列,那么就可以通過對(duì)一個(gè)空的 Redis 實(shí)例順序執(zhí)行所有的指令,也就是「重放」,來恢復(fù) Redis 當(dāng)前實(shí)例的內(nèi)存數(shù)據(jù)結(jié)構(gòu)的狀態(tài)。

          Redis 提供的 AOF 配置項(xiàng)appendfsync寫回策略直接決定 AOF 持久化功能的效率和安全性。

          • always:同步寫回,寫指令執(zhí)行完畢立馬將 aof_buf緩沖區(qū)中的內(nèi)容刷寫到 AOF 文件。
          • everysec:每秒寫回,寫指令執(zhí)行完,日志只會(huì)寫到 AOF 文件緩沖區(qū),每隔一秒就把緩沖區(qū)內(nèi)容同步到磁盤。
          • no: 操作系統(tǒng)控制,寫執(zhí)行執(zhí)行完畢,把日志寫到 AOF 文件內(nèi)存緩沖區(qū),由操作系統(tǒng)決定何時(shí)刷寫到磁盤。

          沒有兩全其美的策略,我們需要在性能和可靠性上做一個(gè)取舍。

          面試官:既然 RDB 有兩個(gè)性能問題,那為何不用 AOF 即可。

          AOF 寫前日志,記錄的是每個(gè)「寫」指令操作。不會(huì)像 RDB 全量快照導(dǎo)致性能損耗,但是執(zhí)行速度沒有 RDB 快,同時(shí)日志文件過大也會(huì)造成性能問題。

          所以,Redis 設(shè)計(jì)了一個(gè)殺手锏「AOF 重寫機(jī)制」,Redis 提供了 bgrewriteaof指令用于對(duì) AOF 日志進(jìn)行瘦身。

          其原理就是開辟一個(gè)子進(jìn)程對(duì)內(nèi)存進(jìn)行遍歷轉(zhuǎn)換成一系列 Redis 的操作指令,序列化到一個(gè)新的 AOF 日志文件中。序列化完畢后再將操作期間發(fā)生的增量 AOF 日志追加到這個(gè)新的 AOF 日志文件中,追加完畢后就立即替代舊的 AOF 日志文件了,瘦身工作就完成了。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          面試官:如何實(shí)現(xiàn) 數(shù)據(jù)盡可能少丟失又能兼顧性能呢?

          重啟 Redis 時(shí),我們很少使用 rdb 來恢復(fù)內(nèi)存狀態(tài),因?yàn)闀?huì)丟失大量數(shù)據(jù)。我們通常使用 AOF 日志重放,但是重放 AOF 日志性能相對(duì) rdb 來說要慢很多,這樣在 Redis 實(shí)例很大的情況下,啟動(dòng)需要花費(fèi)很長(zhǎng)的時(shí)間。

          Redis 4.0 為了解決這個(gè)問題,帶來了一個(gè)新的持久化選項(xiàng)——混合持久化。將 rdb 文件的內(nèi)容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是自持久化開始到持久化結(jié)束的這段時(shí)間發(fā)生的增量 AOF 日志,通常這部分 AOF 日志很小。

          于是在 Redis 重啟的時(shí)候,可以先加載 rdb 的內(nèi)容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升

          Redis 主從架構(gòu)數(shù)據(jù)同步

          Redis 提供了主從模式,通過主從復(fù)制,將數(shù)據(jù)冗余一份復(fù)制到其他 Redis 服務(wù)器。

          面試官:主從之間數(shù)據(jù)如何保證一致性?

          為了保證副本數(shù)據(jù)的一致性,主從架構(gòu)采用了讀寫分離的方式。

          • 讀操作:主、從庫都可以執(zhí)行;
          • 寫操作:主庫先執(zhí)行,之后將寫操作同步到從庫;

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          面試官:主從復(fù)制還有其他作用么?

          1. 故障恢復(fù):當(dāng)主節(jié)點(diǎn)宕機(jī),其他節(jié)點(diǎn)依然可以提供服務(wù);
          2. 負(fù)載均衡:Master 節(jié)點(diǎn)提供寫服務(wù),Slave 節(jié)點(diǎn)提供讀服務(wù),分擔(dān)壓力;
          3. 高可用基石:是哨兵和 cluster 實(shí)施的基礎(chǔ),是高可用的基石。

          面試官:主從復(fù)制如何實(shí)現(xiàn)的?

          同步分為三種情況:

          1. 第一次主從庫全量復(fù)制;
          2. 主從正常運(yùn)行期間的同步;
          3. 主從庫間網(wǎng)絡(luò)斷開重連同步。

          面試官:第一次同步怎么實(shí)現(xiàn)?

          主從庫第一次復(fù)制過程大體可以分為 3 個(gè)階段:連接建立階段(即準(zhǔn)備階段)、主庫同步數(shù)據(jù)到從庫階段、發(fā)送同步期間新寫命令到從庫階段;

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          1. 建立連接:從庫會(huì)和主庫建立連接,從庫執(zhí)行 replicaof 并發(fā)送 psync 命令并告訴主庫即將進(jìn)行同步,主庫確認(rèn)回復(fù)后,主從庫間就開始同步了。
          2. 主庫同步數(shù)據(jù)給從庫:master 執(zhí)行 bgsave命令生成 RDB 文件,并將文件發(fā)送給從庫,同時(shí)主庫為每一個(gè) slave 開辟一塊 replication buffer 緩沖區(qū)記錄從生成 RDB 文件開始收到的所有寫命令。從庫保存 RDB 并清空數(shù)據(jù)庫再加載 RDB 數(shù)據(jù)到內(nèi)存中。
          3. 發(fā)送 RDB 之后接收到的新寫命令到從庫:在生成 RDB 文件之后的寫操作并沒有記錄到剛剛的 RDB 文件中,為了保證主從庫數(shù)據(jù)的一致性,所以主庫會(huì)在內(nèi)存中使用一個(gè)叫 replication buffer 記錄 RDB 文件生成后的所有寫操作。并將里面的數(shù)據(jù)發(fā)送到 slave。

          面試官:主從庫間的網(wǎng)絡(luò)斷了咋辦?斷開后要重新全量復(fù)制么?

          在 Redis 2.8 之前,如果主從庫在命令傳播時(shí)出現(xiàn)了網(wǎng)絡(luò)閃斷,那么,從庫就會(huì)和主庫重新進(jìn)行一次全量復(fù)制,開銷非常大。

          從 Redis 2.8 開始,網(wǎng)絡(luò)斷了之后,主從庫會(huì)采用增量復(fù)制的方式繼續(xù)同步。

          增量復(fù)制:用于網(wǎng)絡(luò)中斷等情況后的復(fù)制,只將中斷期間主節(jié)點(diǎn)執(zhí)行的寫命令發(fā)送給從節(jié)點(diǎn),與全量復(fù)制相比更加高效

          斷開重連增量復(fù)制的實(shí)現(xiàn)奧秘就是 repl_backlog_buffer 緩沖區(qū),不管在什么時(shí)候 master 都會(huì)將寫指令操作記錄在 repl_backlog_buffer 中,因?yàn)閮?nèi)存有限, repl_backlog_buffer 是一個(gè)定長(zhǎng)的環(huán)形數(shù)組,如果數(shù)組內(nèi)容滿了,就會(huì)從頭開始覆蓋前面的內(nèi)容。

          master 使用 master_repl_offset記錄自己寫到的位置偏移量,slave 則使用 slave_repl_offset記錄已經(jīng)讀取到的偏移量。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          當(dāng)主從斷開重連后,slave 會(huì)先發(fā)送 psync 命令給 master,同時(shí)將自己的 runID,slave_repl_offset發(fā)送給 master。

          master 只需要把 master_repl_offsetslave_repl_offset之間的命令同步給從庫即可。

          增量復(fù)制執(zhí)行流程如下圖:

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          面試官:那完成全量同步后,正常運(yùn)行過程中如何同步數(shù)據(jù)呢?

          當(dāng)主從庫完成了全量復(fù)制,它們之間就會(huì)一直維護(hù)一個(gè)網(wǎng)絡(luò)連接,主庫會(huì)通過這個(gè)連接將后續(xù)陸續(xù)收到的命令操作再同步給從庫,這個(gè)過程也稱為基于長(zhǎng)連接的命令傳播,使用長(zhǎng)連接的目的就是避免頻繁建立連接導(dǎo)致的開銷。

          哨兵原理連環(huán)問

          面試官:可以呀,知道這么多,你知道 哨兵集群原理么?

          哨兵是 Redis 的一種運(yùn)行模式,它專注于對(duì) Redis 實(shí)例(主節(jié)點(diǎn)、從節(jié)點(diǎn))運(yùn)行狀態(tài)的監(jiān)控,并能夠在主節(jié)點(diǎn)發(fā)生故障時(shí)通過一系列的機(jī)制實(shí)現(xiàn)選主及主從切換,實(shí)現(xiàn)故障轉(zhuǎn)移,確保整個(gè) Redis 系統(tǒng)的可用性

          他的架構(gòu)圖如下:

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          Redis 哨兵具備的能力有如下幾個(gè):

          • 監(jiān)控:持續(xù)監(jiān)控 master 、slave 是否處于預(yù)期工作狀態(tài)。
          • 自動(dòng)切換主庫:當(dāng) Master 運(yùn)行故障,哨兵啟動(dòng)自動(dòng)故障恢復(fù)流程:從 slave 中選擇一臺(tái)作為新 master。
          • 通知:讓 slave 執(zhí)行 replicaof ,與新的 master 同步;并且通知客戶端與新 master 建立連接。

          面試官:哨兵之間是如何知道彼此的?

          哨兵與 master 建立通信,利用 master 提供發(fā)布/訂閱機(jī)制發(fā)布自己的信息,比如身高體重、是否單身、IP、端口……

          master 有一個(gè) __sentinel__:hello 的專用通道,用于哨兵之間發(fā)布和訂閱消息。這就好比是 __sentinel__:hello 微信群,哨兵利用 master 建立的微信群發(fā)布自己的消息,同時(shí)關(guān)注其他哨兵發(fā)布的消息

          面試官:哨兵之間雖然建立連接了,但是還需要和 slave 建立連接,不然沒法監(jiān)控他們呀,如何知道 slave 并監(jiān)控他們的?

          關(guān)鍵還是利用 master 來實(shí)現(xiàn),哨兵向 master 發(fā)送 INFO 命令, master 掌門自然是知道自己門下所有的 salve 小弟的。所以 master 接收到命令后,便將 slave 列表告訴哨兵。

          哨兵根據(jù) master 響應(yīng)的 slave 名單信息與每一個(gè) salve 建立連接,并且根據(jù)這個(gè)連接持續(xù)監(jiān)控哨兵。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          Cluster 集群連環(huán)炮

          面試官:除了哨兵以外,還有其他的高可用手段么?

          有 Cluster 集群實(shí)現(xiàn)高可用,哨兵集群監(jiān)控的 Redis 集群是主從架構(gòu),無法很想拓展。使用 Redis Cluster 集群,主要解決了大數(shù)據(jù)量存儲(chǔ)導(dǎo)致的各種慢問題,同時(shí)也便于橫向拓展。

          在面向百萬、千萬級(jí)別的用戶規(guī)模時(shí),橫向擴(kuò)展的 Redis 切片集群會(huì)是一個(gè)非常好的選擇。

          面試官:什么是 Cluster 集群?

          Redis 集群是一種分布式數(shù)據(jù)庫方案,集群通過分片(sharding)來進(jìn)行數(shù)據(jù)管理(「分治思想」的一種實(shí)踐),并提供復(fù)制和故障轉(zhuǎn)移功能。

          將數(shù)據(jù)劃分為 16384 的 slots,每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分槽位。槽位的信息存儲(chǔ)于每個(gè)節(jié)點(diǎn)中。

          它是去中心化的,如圖所示,該集群有三個(gè) Redis 節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)負(fù)責(zé)整個(gè)集群的一部分?jǐn)?shù)據(jù),每個(gè)節(jié)點(diǎn)負(fù)責(zé)的數(shù)據(jù)多少可能不一樣。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          三個(gè)節(jié)點(diǎn)相互連接組成一個(gè)對(duì)等的集群,它們之間通過 Gossip協(xié)議相互交互集群信息,最后每個(gè)節(jié)點(diǎn)都保存著其他節(jié)點(diǎn)的 slots 分配情況。

          面試官:哈希槽又是如何映射到 Redis 實(shí)例上呢?

          1. 根據(jù)鍵值對(duì)的 key,使用 CRC16 算法,計(jì)算出一個(gè) 16 bit 的值;
          2. 將 16 bit 的值對(duì) 16384 執(zhí)行取模,得到 0 ~ 16383 的數(shù)表示 key 對(duì)應(yīng)的哈希槽。
          3. 根據(jù)該槽信息定位到對(duì)應(yīng)的實(shí)例。

          鍵值對(duì)數(shù)據(jù)、哈希槽、Redis 實(shí)例之間的映射關(guān)系如下:

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          面試官:Cluster 如何實(shí)現(xiàn)故障轉(zhuǎn)移?

          Redis 集群節(jié)點(diǎn)采用 Gossip 協(xié)議來廣播自己的狀態(tài)以及自己對(duì)整個(gè)集群認(rèn)知的改變。比如一個(gè)節(jié)點(diǎn)發(fā)現(xiàn)某個(gè)節(jié)點(diǎn)失聯(lián)了 (PFail),它會(huì)將這條信息向整個(gè)集群廣播,其它節(jié)點(diǎn)也就可以收到這點(diǎn)失聯(lián)信息。

          如果一個(gè)節(jié)點(diǎn)收到了某個(gè)節(jié)點(diǎn)失聯(lián)的數(shù)量 (PFail Count) 已經(jīng)達(dá)到了集群的大多數(shù),就可以標(biāo)記該節(jié)點(diǎn)為確定下線狀態(tài) (Fail),然后向整個(gè)集群廣播,強(qiáng)迫其它節(jié)點(diǎn)也接收該節(jié)點(diǎn)已經(jīng)下線的事實(shí),并立即對(duì)該失聯(lián)節(jié)點(diǎn)進(jìn)行主從切換。

          面試官:客戶端又怎么確定訪問的數(shù)據(jù)到底分布在哪個(gè)實(shí)例上呢?

          Redis 實(shí)例會(huì)將自己的哈希槽信息通過 Gossip 協(xié)議發(fā)送給集群中其他的實(shí)例,實(shí)現(xiàn)了哈希槽分配信息的擴(kuò)散。

          這樣,集群中的每個(gè)實(shí)例都有所有哈希槽與實(shí)例之間的映射關(guān)系信息。

          當(dāng)客戶端連接任何一個(gè)實(shí)例,實(shí)例就將哈希槽與實(shí)例的映射關(guān)系響應(yīng)給客戶端,客戶端就會(huì)將哈希槽與實(shí)例映射信息緩存在本地。

          當(dāng)客戶端請(qǐng)求時(shí),會(huì)計(jì)算出鍵所對(duì)應(yīng)的哈希槽,再通過本地緩存的哈希槽實(shí)例映射信息定位到數(shù)據(jù)所在實(shí)例上,再將請(qǐng)求發(fā)送給對(duì)應(yīng)的實(shí)例。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          面試官:什么是 Redis 重定向機(jī)制?

          哈希槽與實(shí)例之間的映射關(guān)系由于新增實(shí)例或者負(fù)載均衡重新分配導(dǎo)致改變了,客戶端將請(qǐng)求發(fā)送到實(shí)例上,這個(gè)實(shí)例沒有相應(yīng)的數(shù)據(jù),該 Redis 實(shí)例會(huì)告訴客戶端將請(qǐng)求發(fā)送到其他的實(shí)例上。

          Redis 通過 MOVED 錯(cuò)誤和 ASK 錯(cuò)誤告訴客戶端。

          MOVED

          MOVED 錯(cuò)誤(負(fù)載均衡,數(shù)據(jù)已經(jīng)遷移到其他實(shí)例上):當(dāng)客戶端將一個(gè)鍵值對(duì)操作請(qǐng)求發(fā)送給某個(gè)實(shí)例,而這個(gè)鍵所在的槽并非由自己負(fù)責(zé)的時(shí)候,該實(shí)例會(huì)返回一個(gè) MOVED 錯(cuò)誤指引轉(zhuǎn)向正在負(fù)責(zé)該槽的節(jié)點(diǎn)。

          同時(shí),客戶端還會(huì)更新本地緩存,將該 slot 與 Redis 實(shí)例對(duì)應(yīng)關(guān)系更新正確

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          ASK

          如果某個(gè) slot 的數(shù)據(jù)比較多,部分遷移到新實(shí)例,還有一部分沒有遷移。

          如果請(qǐng)求的 key 在當(dāng)前節(jié)點(diǎn)找到就直接執(zhí)行命令,否則時(shí)候就需要 ASK 錯(cuò)誤響應(yīng)了。

          槽部分遷移未完成的情況下,如果需要訪問的 key 所在 Slot 正在從 實(shí)例 1 遷移到 實(shí)例 2(如果 key 已經(jīng)不在實(shí)例 1),實(shí)例 1 會(huì)返回客戶端一條 ASK 報(bào)錯(cuò)信息:客戶端請(qǐng)求的 key 所在的哈希槽正在遷移到實(shí)例 2 上,你先給實(shí)例 2 發(fā)送一個(gè) ASKING 命令,接著發(fā)發(fā)送操作命令。

          比如客戶端請(qǐng)求定位到 key = 「公眾號(hào):碼哥字節(jié)」的槽 16330 在實(shí)例 172.17.18.1 上,節(jié)點(diǎn) 1 如果找得到就直接執(zhí)行命令,否則響應(yīng) ASK 錯(cuò)誤信息,并指引客戶端轉(zhuǎn)向正在遷移的目標(biāo)節(jié)點(diǎn) 172.17.18.2。

          Redis高頻面試題分享,帶你橫掃核心知識(shí)點(diǎn)!

          注意:ASK 錯(cuò)誤指令并不會(huì)更新客戶端緩存的哈希槽分配信息。

          總結(jié)

          本篇主要將 Redis 核心內(nèi)容過了一遍,涉及到數(shù)據(jù)結(jié)構(gòu)、內(nèi)存模型、 IO 模型、持久化 RDB 和AOF 、主從復(fù)制原理、哨兵原理、cluster 原理。

          原文地址:https://juejin.cn/post/6976257378094481444

          作者:碼哥字節(jié)

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)