Redis 為什么這么快?

溫馨提示:本篇文章適合有一定基礎(chǔ)的童鞋哦

到底有多快璃谨?

我們可以先說到底有多快,根據(jù)官方數(shù)據(jù)鲤妥,Redis 的 QPS 可以達(dá)到約 100000(每秒請求數(shù))


基準(zhǔn)測試

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

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

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

Redis 是基于內(nèi)存的數(shù)據(jù)庫贡耽,跟磁盤數(shù)據(jù)庫相比衷模,完全吊打磁盤的速度鹊汛。
不論讀寫操作都是在內(nèi)存上完成的,我們分別對比下內(nèi)存操作與磁盤操作的差異阱冶。
磁盤調(diào)用

圖片

內(nèi)存操作
內(nèi)存直接由 CPU 控制刁憋,也就是 CPU 內(nèi)部集成的內(nèi)存控制器,所以說內(nèi)存是直接與 CPU 對接木蹬,享受與 CPU 通信的最優(yōu)帶寬至耻。
最后以一張圖量化系統(tǒng)的各種延時時間
圖片

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

學(xué)習(xí) MySQL 的時候我知道為了提高檢索速度使用了 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)來支撐敛腌,目的就是為了追求更快的速度痰催。

圖片

1. SDS 簡單動態(tài)字符串優(yōu)勢

C 語言字符串與 SDS
  1. SDS 中 len 保存這字符串的長度,O(1) 時間復(fù)雜度查詢字符串長度信息迎瞧。
  2. 空間預(yù)分配:SDS 被修改后夸溶,程序不僅會為 SDS 分配所需要的必須空間,還會分配額外的未使用空間凶硅。
  3. 惰性空間釋放:當(dāng)對 SDS 進(jìn)行縮短操作時缝裁,程序并不會回收多余的內(nèi)存空間,而是使用 free 字段將這些字節(jié)數(shù)量記錄下來不釋放足绅,后面如果需要 append 操作捷绑,則直接使用 free 中未使用的空間,減少了內(nèi)存的分配氢妈。

2. zipList 壓縮列表

壓縮列表是 List 粹污、hash、 sorted Set 三種數(shù)據(jù)類型底層實現(xiàn)之一首量。
當(dāng)一個列表只有少量數(shù)據(jù)的時候壮吩,并且每個列表項要么就是小整數(shù)值,要么就是長度比較短的字符串加缘,那么 Redis 就會使用壓縮列表來做列表鍵的底層實現(xiàn)鸭叙。

ziplist

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

3. quicklist

后續(xù)版本對列表數(shù)據(jù)結(jié)構(gòu)進(jìn)行了改造沈贝,使用 quicklist 代替了 ziplist 和 linkedlist。
** quicklist 是 ziplist 和 linkedlist 的混合體勋乾,它將 linkedlist 按段切分宋下,每一段使用 ziplist 來緊湊存儲嗡善,多個 ziplist 之間使用雙向指針串接起來。**


圖片

4. skipList 跳躍表

sorted set 類型的排序功能便是通過「跳躍列表」數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)学歧。
跳躍表(skiplist)是一種有序數(shù)據(jù)結(jié)構(gòu)罩引,它通過在每個節(jié)點中維持多個指向其他節(jié)點的指針,從而達(dá)到快速訪問節(jié)點的目的撩满。
跳表在鏈表的基礎(chǔ)上蜒程,增加了多層級索引,通過索引位置的幾個跳轉(zhuǎn)伺帘,實現(xiàn)數(shù)據(jù)的快速定位昭躺,如下圖所示:


跳躍表

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

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

單線程模型

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

千萬別說 Redis 就只有一個線程。
單線程指的是 Redis 鍵值對讀寫指令的執(zhí)行是單線程龙助。
先說官方答案砰奕,讓人覺得足夠嚴(yán)謹(jǐn),而不是人云亦云去背誦一些博客提鸟。
官方答案: 因為 Redis 是基于內(nèi)存的操作军援,CPU 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是機(jī)器內(nèi)存的大小或者網(wǎng)絡(luò)帶寬 称勋。既然單線程容易實現(xiàn)胸哥,而且 CPU 不會成為瓶頸,那就順理成章地采用單線程的方案了赡鲜。原文地址:https://redis.io/topics/faq空厌。

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

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

切換上下文時盗棵,我們需要完成一系列工作壮韭,這是非常消耗資源的操作北发。

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

單線程有什么好處?

  1. 不會因為線程創(chuàng)建導(dǎo)致的性能消耗屯曹;
  2. 避免上下文切換引起的 CPU 消耗狱庇,沒有多線程切換的開銷;
  3. 避免了線程之間的競爭問題恶耽,比如添加鎖密任、釋放鎖、死鎖等偷俭,不需要考慮各種鎖問題浪讳。
  4. 代碼更清晰,處理邏輯簡單涌萤。

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

Redis 采用 I/O 多路復(fù)用技術(shù)淹遵,并發(fā)處理連接。采用了 epoll + 自己實現(xiàn)的簡單的事件框架负溪。
epoll 中的讀透揣、寫、關(guān)閉川抡、連接都轉(zhuǎn)化成了事件辐真,然后利用 epoll 的多路復(fù)用特性,絕不在 IO 上浪費(fèi)一點時間猖腕。


高性能 IO 多路復(fù)用

Redis 線程不會阻塞在某一個特定的監(jiān)聽或已連接套接字上拆祈,也就是說,不會阻塞在某一個特定的客戶端請求處理上倘感。正因為此放坏,Redis 可以同時和多個客戶端連接并處理請求,從而提升并發(fā)性老玛。

Redis 全局 hash 字典

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

Redis 全局哈希表

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

Redis 使用對象(redisObject)來表示數(shù)據(jù)庫中的鍵值梗摇,當(dāng)我們在 Redis 中創(chuàng)建一個鍵值對時拓哟,至少創(chuàng)建兩個對象,一個對象是用做鍵值對的鍵對象伶授,另一個是鍵值對的值對象断序。

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

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

Hash 沖突怎么辦违诗?

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

開始默認(rèn)使用 「hash 表 1 」保存鍵值對數(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 的過程中并不是一次性的润努,這樣會造成 Redis 阻塞,無法提供服務(wù)示括。

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

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

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

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

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

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

在 Redis 執(zhí)行「寫」指令過程中评疗,內(nèi)存數(shù)據(jù)會一直變化测砂。所謂的內(nèi)存快照,指的就是 Redis 內(nèi)存中的數(shù)據(jù)在某一刻的狀態(tài)數(shù)據(jù)百匆。
好比時間定格在某一刻砌些,當(dāng)我們拍照的,通過照片就能把某一刻的瞬間畫面完全記錄下來加匈。
Redis 跟這個類似存璃,就是把某一刻的數(shù)據(jù)以文件的形式拍下來,寫到磁盤上雕拼。這個快照文件叫做 RDB 文件纵东,RDB 就是 Redis DataBase 的縮寫。

RDB內(nèi)存快照

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

面試官:在生成 RDB 期間,Redis 可以同時處理寫請求么辑甜?

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

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

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

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

圖片

寫時復(fù)制技術(shù)保證快照期間數(shù)據(jù)可修改

面試官:那 AOF 又是什么归薛?

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

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

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

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

面試官:既然 RDB 有兩個性能問題官研,那為何不用 AOF 即可秽澳。

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

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

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

圖片

AOF重寫機(jī)制(3條變一條)

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

重啟 Redis 時讹俊,我們很少使用 rdb 來恢復(fù)內(nèi)存狀態(tài),因為會丟失大量數(shù)據(jù)煌抒。我們通常使用 AOF 日志重放仍劈,但是重放 AOF 日志性能相對 rdb 來說要慢很多,這樣在 Redis 實例很大的情況下寡壮,啟動需要花費(fèi)很長的時間贩疙。

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

于是在 Redis 重啟的時候臭胜,可以先加載 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 讀寫分離

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

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

面試官:主從復(fù)制如何實現(xiàn)的碍论?
同步分為三種情況:

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

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

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

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

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

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

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

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

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

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

repl_backlog_buffer

當(dāng)主從斷開重連后卖擅,slave 會先發(fā)送 psync 命令給 master,同時將自己的 runID墨技,slave_repl_offset發(fā)送給 master惩阶。

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

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

Redis增量復(fù)制

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

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

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

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

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

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

Redis哨兵集群

Redis 哨兵具備的能力有如下幾個:

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

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

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

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

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

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

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

圖片

INFO命令獲取slave信息

Cluster 集群連環(huán)炮

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

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

在面向百萬蜂厅、千萬級別的用戶規(guī)模時,橫向擴(kuò)展的 Redis 切片集群會是一個非常好的選擇膊畴。

面試官:什么是 Cluster 集群掘猿?

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

將數(shù)據(jù)劃分為 16384 的 slots,每個節(jié)點負(fù)責(zé)一部分槽位买猖。槽位的信息存儲于每個節(jié)點中改橘。

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

Redis 集群架構(gòu)

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

面試官:哈希槽又是如何映射到 Redis 實例上呢筏餐?

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

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

數(shù)據(jù)趟畏、Slot與實例的映射

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

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

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

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

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

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

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

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

Redis 客戶端定位數(shù)據(jù)所在節(jié)點

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

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

Redis 通過 MOVED 錯誤和 ASK 錯誤告訴客戶端。

MOVED

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

同時姨裸,客戶端還會更新本地緩存,將該 slot 與 Redis 實例對應(yīng)關(guān)系更新正確 怨酝。

MOVED 指令

ASK

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

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

槽部分遷移未完成的情況下斤葱,如果需要訪問的 key 所在 Slot 正在從 實例 1 遷移到 實例 2(如果 key 已經(jīng)不在實例 1)慷垮,實例 1 會返回客戶端一條 ASK 報錯信息:客戶端請求的 key 所在的哈希槽正在遷移到實例 2 上揖闸,你先給實例 2 發(fā)送一個 ASKING 命令,接著發(fā)發(fā)送操作命令 料身。

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

圖片

注意:ASK 錯誤指令并不會更新客戶端緩存的哈希槽分配信息

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末幔烛,一起剝皮案震驚了整個濱河市啃擦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌说贝,老刑警劉巖议惰,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乡恕,居然都是意外死亡言询,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門傲宜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來运杭,“玉大人,你說我怎么就攤上這事函卒×俱荆” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵报嵌,是天一觀的道長虱咧。 經(jīng)常有香客問我,道長锚国,這世上最難降的妖魔是什么腕巡? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮血筑,結(jié)果婚禮上绘沉,老公的妹妹穿的比我還像新娘。我一直安慰自己豺总,他們只是感情好车伞,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著喻喳,像睡著了一般另玖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天日矫,我揣著相機(jī)與錄音赂弓,去河邊找鬼。 笑死哪轿,一個胖子當(dāng)著我的面吹牛盈魁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窃诉,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼杨耙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了飘痛?” 一聲冷哼從身側(cè)響起珊膜,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宣脉,沒想到半個月后车柠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡塑猖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年竹祷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羊苟。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡塑陵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜡励,到底是詐尸還是另有隱情令花,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布凉倚,位于F島的核電站兼都,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏稽寒。R本人自食惡果不足惜俯抖,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓦胎。 院中可真熱鬧,春花似錦尤揣、人聲如沸搔啊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽负芋。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旧蛾,已是汗流浹背莽龟。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留锨天,地道東北人毯盈。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像病袄,于是被迫代替她去往敵國和親搂赋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容