redis總結(jié)

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(每秒請求數(shù))徘铝,有興趣的可以參考官方的基準(zhǔn)程序測試《How fast is Redis耳胎?》惯吕,地址:https://redis.io/topics/benchmarks

基準(zhǔn)測試

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

?

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

基于內(nèi)存實(shí)現(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í)時(shí)間(部分?jǐn)?shù)據(jù)引用 Brendan Gregg)

高效的數(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)就能讓人眼前一亮。

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

C 語言字符串與 SDS

SDS 中 len 保存這字符串的長度方咆,O(1) 時(shí)間復(fù)雜度查詢字符串長度信息月腋。

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

惰性空間釋放:當(dāng)對 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ù)值,要么就是長度比較短的字符串操禀,那么 Redis 就會(huì)使用壓縮列表來做列表鍵的底層實(shí)現(xiàn)褂策。

ziplist

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

quicklist

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

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

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ǔ)上,增加了多層級索引护戳,通過索引位置的幾個(gè)跳轉(zhuǎn)翎冲,實(shí)現(xiàn)數(shù)據(jù)的快速定位,如下圖所示:

跳躍表

整數(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 使用多線程)以及鍵值對指令讀寫是由一個(gè)線程來執(zhí)行的。對于 Redis 的持久化鱼炒、集群數(shù)據(jù)同步衔沼、異步刪除等都是其他線程執(zhí)行。

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

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

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

官方答案:因?yàn)?Redis 是基于內(nèi)存的操作,CPU 不是 Redis 的瓶頸酬荞,Redis 的瓶頸最有可能是機(jī)器內(nèi)存的大小或者網(wǎng)絡(luò)帶寬搓劫。既然單線程容易實(shí)現(xiàn)瞧哟,而且 CPU 不會(huì)成為瓶頸,那就順理成章地采用單線程的方案了枪向。原文地址:https://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)試難度。

?

單線程有什么好處被辑?

不會(huì)因?yàn)榫€程創(chuàng)建導(dǎo)致的性能消耗燎悍;

避免上下文切換引起的 CPU 消耗,沒有多線程切換的開銷盼理;

避免了線程之間的競爭問題谈山,比如添加鎖、釋放鎖宏怔、死鎖等奏路,不需要考慮各種鎖問題。

代碼更清晰臊诊,處理邏輯簡單鸽粉。

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

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

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

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

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

Redis 全局 hash 字典

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

Redis 全局哈希表

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

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

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

typedefstructredisObject{

//類型

unsignedtype:4;

//編碼

unsignedencoding:4;

//指向底層數(shù)據(jù)結(jié)構(gòu)的指針

void*ptr;

//...

}robj;

Hash 沖突怎么辦?

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

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

給 「hash 表 2 」分配更大的空間窥突;

將 「hash 表 1 」的數(shù)據(jù)重新映射拷貝到 「hash 表 2」 中;

釋放 「hash 表 1」 的空間硫嘶。

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

而是采用了漸進(jìn)式 rehash第队,每次處理客戶端請求的時(shí)候,先從「 hash 表 1」 中第一個(gè)索引開始刨秆,將這個(gè)位置的 所有數(shù)據(jù)拷貝到 「hash 表 2」 中凳谦,就這樣將 rehash 分散到多次請求過程中,避免耗時(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)重性能開銷:

頻繁生成 RDB 文件寫入磁盤,磁盤壓力過大送粱。會(huì)出現(xiàn)上一個(gè) RDB 還未執(zhí)行完褪贵,下一個(gè)又開始生成,陷入死循環(huán)抗俄。

fork 出 bgsave 子進(jìn)程會(huì)阻塞主線程竭鞍,主線程的內(nèi)存越大,阻塞時(shí)間越長橄镜。

所以 Redis 還設(shè)計(jì)了 AOF 寫后日志記錄對內(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 的縮寫无切。

RDB內(nèi)存快照

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

?

面試官:在生成 RDB 期間哆键,Redis 可以同時(shí)處理寫請求么掘托?

可以的,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ù)處理客戶端請求泪掀。

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

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

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

?

面試官:那 AOF 又是什么贴妻?

AOF 日志記錄了自 Redis 實(shí)例創(chuàng)建以來所有的修改性指令序列,那么就可以通過對一個(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指令用于對 AOF 日志進(jìn)行瘦身交汤。

其原理就是開辟一個(gè)子進(jìn)程對內(nèi)存進(jìn)行遍歷轉(zhuǎn)換成一系列

Redis 的操作指令,序列化到一個(gè)新的 AOF 日志文件中。序列化完畢后再將操作期間發(fā)生的增量 AOF 日志追加到這個(gè)新的 AOF

日志文件中芙扎,追加完畢后就立即替代舊的 AOF 日志文件了星岗,瘦身工作就完成了。

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

?

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

重啟 Redis 時(shí)俏橘,我們很少使用 rdb 來恢復(fù)內(nèi)存狀態(tài),因?yàn)闀?huì)丟失大量數(shù)據(jù)圈浇。我們通常使用 AOF 日志重放寥掐,但是重放 AOF 日志性能相對 rdb 來說要慢很多,這樣在 Redis 實(shí)例很大的情況下磷蜀,啟動(dòng)需要花費(fèi)很長的時(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 讀寫分離

?

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

故障恢復(fù):當(dāng)主節(jié)點(diǎn)宕機(jī)煎源,其他節(jié)點(diǎn)依然可以提供服務(wù)色迂;

負(fù)載均衡:Master 節(jié)點(diǎn)提供寫服務(wù),Slave 節(jié)點(diǎn)提供讀服務(wù)手销,分擔(dān)壓力歇僧;

高可用基石:是哨兵和 cluster 實(shí)施的基礎(chǔ),是高可用的基石。

?

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

同步分為三種情況:

第一次主從庫全量復(fù)制祸轮;

主從正常運(yùn)行期間的同步;

主從庫間網(wǎng)絡(luò)斷開重連同步侥钳。

?

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

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

Redis全量同步

建立連接:從庫會(huì)和主庫建立連接,從庫執(zhí)行 replicaof 并發(fā)送 psync 命令并告訴主庫即將進(jìn)行同步给猾,主庫確認(rèn)回復(fù)后疫萤,主從庫間就開始同步了

主庫同步數(shù)據(jù)給從庫:master 執(zhí)行bgsave命令生成 RDB 文件耙册,并將文件發(fā)送給從庫给僵,同時(shí)主庫為每一個(gè) slave 開辟一塊 replication buffer 緩沖區(qū)記錄從生成 RDB 文件開始收到的所有寫命令。從庫保存 RDB 并清空數(shù)據(jù)庫再加載 RDB 數(shù)據(jù)到內(nèi)存中详拙。

發(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è)定長的環(huán)形數(shù)組匕得,如果數(shù)組內(nèi)容滿了继榆,就會(huì)從頭開始覆蓋前面的內(nèi)容

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

repl_backlog_buffer

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

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

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

Redis增量復(fù)制

?

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

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

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

?

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

哨兵是 Redis 的一種運(yùn)行模式云头,它專注于對 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哨兵集群

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 中選擇一臺作為新 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)控哨兵。

INFO命令獲取slave信息

Cluster 集群連環(huán)炮

?

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

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

在面向百萬、千萬級別的用戶規(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é)一部分槽位砸西。槽位的信息存儲于每個(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 集群架構(gòu)

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

?

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

根據(jù)鍵值對的 key,使用 CRC16 算法翁逞,計(jì)算出一個(gè) 16 bit 的值肋杖;

將 16 bit 的值對 16384 執(zhí)行取模,得到 0 ~ 16383 的數(shù)表示 key 對應(yīng)的哈希槽挖函。

根據(jù)該槽信息定位到對應(yīng)的實(shí)例状植。

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

數(shù)據(jù)浅萧、Slot與實(shí)例的映射

?

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

Redis 集群節(jié)點(diǎn)采用Gossip協(xié)議來廣播自己的狀態(tài)以及自己對整個(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í)丧肴,并立即對該失聯(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)客戶端請求時(shí),會(huì)計(jì)算出鍵所對應(yīng)的哈希槽竖伯,再通過本地緩存的哈希槽實(shí)例映射信息定位到數(shù)據(jù)所在實(shí)例上存哲,再將請求發(fā)送給對應(yīng)的實(shí)例。

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

?

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

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

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

MOVED

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

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

MOVED 指令

ASK

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

如果請求的 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ò)信息:客戶端請求的 key 所在的哈希槽正在遷移到實(shí)例 2 上裂逐,你先給實(shí)例 2 發(fā)送一個(gè) ASKING 命令,接著發(fā)發(fā)送操作命令泣栈。

比如客戶端請求定位到 key = 「公眾號:碼哥字節(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掺涛。

ASK 錯(cuò)誤

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疼进,一起剝皮案震驚了整個(gè)濱河市薪缆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伞广,老刑警劉巖拣帽,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赔癌,居然都是意外死亡诞外,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門灾票,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峡谊,“玉大人,你說我怎么就攤上這事刊苍〖让牵” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵正什,是天一觀的道長啥纸。 經(jīng)常有香客問我,道長婴氮,這世上最難降的妖魔是什么斯棒? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任盾致,我火速辦了婚禮,結(jié)果婚禮上荣暮,老公的妹妹穿的比我還像新娘庭惜。我一直安慰自己,他們只是感情好穗酥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布护赊。 她就那樣靜靜地躺著,像睡著了一般砾跃。 火紅的嫁衣襯著肌膚如雪骏啰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天抽高,我揣著相機(jī)與錄音判耕,去河邊找鬼。 笑死翘骂,一個(gè)胖子當(dāng)著我的面吹牛祈秕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雏胃,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼请毛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瞭亮?” 一聲冷哼從身側(cè)響起方仿,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎统翩,沒想到半個(gè)月后仙蚜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厂汗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年委粉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娶桦。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贾节,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衷畦,到底是詐尸還是另有隱情栗涂,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布祈争,位于F島的核電站斤程,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏菩混。R本人自食惡果不足惜忿墅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一扁藕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疚脐,春花似錦纹磺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秘症。三九已至照卦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乡摹,已是汗流浹背役耕。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留聪廉,地道東北人瞬痘。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像板熊,于是被迫代替她去往敵國和親框全。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

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

  • [TOC] 一干签、Redis 基礎(chǔ)常問 1.1津辩、Redis 有哪些數(shù)據(jù)結(jié)構(gòu) 基礎(chǔ):字符串String、字典Hash容劳、...
    w1992wishes閱讀 637評論 0 1
  • 目錄 五種數(shù)據(jù)結(jié)構(gòu) 簡單動(dòng)態(tài)字符串sds 這種數(shù)據(jù)結(jié)構(gòu)的應(yīng)用舉例,設(shè)置過期時(shí)間喘沿,發(fā)短信的時(shí)候,設(shè)置驗(yàn)證碼的過期時(shí)間...
    后來丶_a24d閱讀 263評論 0 7
  • Redis數(shù)據(jù)結(jié)構(gòu) String字符串 Hash存儲對象 List微博關(guān)注列表竭贩、消息列表雙向鏈表蚜印,支持反向查找和遍...
    幽游不想吃飯閱讀 381評論 0 0
  • Redis簡介 Redis 是一個(gè)使用 C 語言編寫的,開源的(BSD許可)高性能非關(guān)系型(NoSQL)的鍵值對數(shù)...
    JourWon閱讀 454評論 0 2
  • Redis為什么速度快 1留量、完全基于內(nèi)存窄赋,絕大部分請求是純粹的內(nèi)存操作,非陈ハǎ快速寝凌。數(shù)據(jù)存在內(nèi)存中,類似于HashM...
    亖狼何需裝羴閱讀 726評論 0 0