Redis有哪些部署方案?了解哨兵機(jī)制嗎?
Redis有哪些部署方案?
- 單機(jī)版:?jiǎn)螜C(jī)部署,單機(jī)Redis能夠承載的 QPS 大概就在上萬(wàn)到幾萬(wàn)不等。這種部署方式很少使用。存在的問(wèn)題:1、內(nèi)存容量有限 2、處理能力有限 3、無(wú)法高可用。
- 主從模式:一主多從,主負(fù)責(zé)寫,并且將數(shù)據(jù)復(fù)制到其它的 slave 節(jié)點(diǎn),從節(jié)點(diǎn)負(fù)責(zé)讀。所有的讀請(qǐng)求全部走從節(jié)點(diǎn)。這樣也可以很輕松實(shí)現(xiàn)水平擴(kuò)容,支撐讀高并發(fā)。master 節(jié)點(diǎn)掛掉后,需要手動(dòng)指定新的 master,可用性不高,基本不用。
- 哨兵模式:主從復(fù)制存在不能自動(dòng)故障轉(zhuǎn)移、達(dá)不到高可用的問(wèn)題。哨兵模式解決了這些問(wèn)題。通過(guò)哨兵機(jī)制可以自動(dòng)切換主從節(jié)點(diǎn)。master 節(jié)點(diǎn)掛掉后,哨兵進(jìn)程會(huì)主動(dòng)選舉新的 master,可用性高,但是每個(gè)節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)是一樣的,浪費(fèi)內(nèi)存空間。數(shù)據(jù)量不是很多,集群規(guī)模不是很大,需要自動(dòng)容錯(cuò)容災(zāi)的時(shí)候使用。
- Redis cluster:服務(wù)端分片技術(shù),3.0版本開始正式提供。Redis Cluster并沒(méi)有使用一致性hash,而是采用slot(槽)的概念,一共分成16384個(gè)槽。將請(qǐng)求發(fā)送到任意節(jié)點(diǎn),接收到請(qǐng)求的節(jié)點(diǎn)會(huì)將查詢請(qǐng)求發(fā)送到正確的節(jié)點(diǎn)上執(zhí)行。主要是針對(duì)海量數(shù)據(jù)+高并發(fā)+高可用的場(chǎng)景,如果是海量數(shù)據(jù),如果你的數(shù)據(jù)量很大,那么建議就用Redis cluster,所有主節(jié)點(diǎn)的容量總和就是Redis cluster可緩存的數(shù)據(jù)容量。
主從架構(gòu)
單機(jī)的 Redis,能夠承載的 QPS 大概就在上萬(wàn)到幾萬(wàn)不等。對(duì)于緩存來(lái)說(shuō),一般都是用來(lái)支撐讀高并發(fā)的。因此架構(gòu)做成主從(master-slave)架構(gòu),一主多從,主負(fù)責(zé)寫,并且將數(shù)據(jù)復(fù)制到其它的 slave 節(jié)點(diǎn),從節(jié)點(diǎn)負(fù)責(zé)讀。所有的讀請(qǐng)求全部走從節(jié)點(diǎn)。這樣也可以很輕松實(shí)現(xiàn)水平擴(kuò)容,支撐讀高并發(fā)。
Redis的復(fù)制功能是支持多個(gè)數(shù)據(jù)庫(kù)之間的數(shù)據(jù)同步。主數(shù)據(jù)庫(kù)可以進(jìn)行讀寫操作,當(dāng)主數(shù)據(jù)庫(kù)的數(shù)據(jù)發(fā)生變化時(shí)會(huì)自動(dòng)將數(shù)據(jù)同步到從數(shù)據(jù)庫(kù)。從數(shù)據(jù)庫(kù)一般是只讀的,它會(huì)接收主數(shù)據(jù)庫(kù)同步過(guò)來(lái)的數(shù)據(jù)。一個(gè)主數(shù)據(jù)庫(kù)可以有多個(gè)從數(shù)據(jù)庫(kù),而一個(gè)從數(shù)據(jù)庫(kù)只能有一個(gè)主數(shù)據(jù)庫(kù)。
主從復(fù)制的原理?
Redis 的主從復(fù)制是指一個(gè) Redis 實(shí)例(主節(jié)點(diǎn))可以將數(shù)據(jù)復(fù)制到一個(gè)或多個(gè)從節(jié)點(diǎn)(從節(jié)點(diǎn)),從節(jié)點(diǎn)從主節(jié)點(diǎn)獲取數(shù)據(jù)并保持同步。
- 開始同步:從節(jié)點(diǎn)通過(guò)向主節(jié)點(diǎn)發(fā)送PSNC命令發(fā)起同步,
- 全量復(fù)制:如果是第一次連接或之前的連接失效,從節(jié)點(diǎn)會(huì)請(qǐng)求全量復(fù)制,主節(jié)點(diǎn)將當(dāng)前數(shù)據(jù)快照(RDB文件)發(fā)送給從節(jié)點(diǎn)。
- 增量復(fù)制:全量復(fù)制完畢后,主從之間會(huì)保持一個(gè)長(zhǎng)連接,主節(jié)點(diǎn)會(huì)通過(guò)這個(gè)連接將后續(xù)的寫操作傳遞給從節(jié)點(diǎn)執(zhí)行,來(lái)保證數(shù)據(jù)的一致。
詳細(xì)流程如下:
- 當(dāng)啟動(dòng)一個(gè)從節(jié)點(diǎn)時(shí),它會(huì)發(fā)送一個(gè)
PSYNC命令給主節(jié)點(diǎn); - 全量復(fù)制:如果是從節(jié)點(diǎn)初次連接到主節(jié)點(diǎn),那么會(huì)觸發(fā)一次全量復(fù)制。此時(shí)主節(jié)點(diǎn)會(huì)啟動(dòng)一個(gè)后臺(tái)線程,開始生成一份
RDB快照文件; - 同時(shí)還會(huì)將從客戶端 client 新收到的所有寫命令緩存在內(nèi)存中。
RDB文件生成完畢后, 主節(jié)點(diǎn)會(huì)將RDB文件發(fā)送給從節(jié)點(diǎn),從節(jié)點(diǎn)會(huì)先將RDB文件寫入本地磁盤,然后再?gòu)谋镜卮疟P加載到內(nèi)存中; - 接著主節(jié)點(diǎn)會(huì)將內(nèi)存中緩存的寫命令發(fā)送到從節(jié)點(diǎn),從節(jié)點(diǎn)同步這些數(shù)據(jù);
- 增量同步:如果從節(jié)點(diǎn)跟主節(jié)點(diǎn)之間網(wǎng)絡(luò)出現(xiàn)故障,連接斷開了,會(huì)自動(dòng)重連,連接之后主節(jié)點(diǎn)僅會(huì)將部分缺失的數(shù)據(jù)同步給從節(jié)點(diǎn)。
主從復(fù)制存在的問(wèn)題
Redis的主從模式重點(diǎn)在于解決整體的承壓能力,利用從節(jié)點(diǎn)分擔(dān)讀取操作的壓力。但是其在容錯(cuò)恢復(fù)等可靠性層面欠缺明顯,不具備自動(dòng)的故障轉(zhuǎn)移與恢復(fù)能力:
- 如果slave從節(jié)點(diǎn)宕機(jī),整個(gè)redis依舊可以正常提供服務(wù),待slave節(jié)點(diǎn)重新啟動(dòng)后,可以恢復(fù)從master節(jié)點(diǎn)的數(shù)據(jù)同步、然后繼續(xù)提供服務(wù)。
- 如果master主節(jié)點(diǎn)宕機(jī),則redis功能受損,無(wú)法繼續(xù)提供寫服務(wù),直到手動(dòng)修復(fù)master節(jié)點(diǎn)方可恢復(fù)。
當(dāng)然,master節(jié)點(diǎn)故障后,也可以手動(dòng)將其中一個(gè)從節(jié)點(diǎn)切換為新的master節(jié)點(diǎn)來(lái)恢復(fù)故障。而原先的master節(jié)點(diǎn)恢復(fù)后,需要手動(dòng)將其降級(jí)為slave節(jié)點(diǎn),對(duì)外提供只讀服務(wù)。
實(shí)際使用的時(shí)候,手動(dòng)故障恢復(fù)的時(shí)效無(wú)法得到保證,為了支持自動(dòng)的故障轉(zhuǎn)移與恢復(fù)能力,Redis在主從模式的基礎(chǔ)上進(jìn)行優(yōu)化增強(qiáng),提供了哨兵(Sentinel)架構(gòu)模式。
那么就需要有一個(gè)機(jī)制,能夠監(jiān)測(cè)主節(jié)點(diǎn)是否存活,如果發(fā)現(xiàn)主節(jié)點(diǎn)掛了,就選舉一個(gè)從節(jié)點(diǎn)切換為主節(jié)點(diǎn),并且把新主節(jié)點(diǎn)的相關(guān)信息通知給從節(jié)點(diǎn)和客戶端。這就是哨兵機(jī)制。
圖片
Redis 復(fù)制延遲的常見(jiàn)原因有哪些?
Redis 的復(fù)制延遲是指從節(jié)點(diǎn)同步主節(jié)點(diǎn)數(shù)據(jù)時(shí)可能出現(xiàn)時(shí)間延遲。在讀寫分離場(chǎng)景,這個(gè)延遲會(huì)導(dǎo)致明明寫入了數(shù)據(jù),但是去從節(jié)點(diǎn)查的時(shí)候沒(méi)查到。
可能原因如下:
- 網(wǎng)絡(luò)原因:可能是帶寬不足,或者網(wǎng)絡(luò)抖動(dòng)導(dǎo)致同步的延遲,不過(guò)一般內(nèi)網(wǎng)情況下不會(huì)產(chǎn)生這個(gè)問(wèn)題。
- 主節(jié)點(diǎn)負(fù)載過(guò)高主節(jié)點(diǎn)接收到大量的寫操作,在處理客戶端請(qǐng)求的同時(shí),還需向從節(jié)點(diǎn)發(fā)送復(fù)制數(shù)據(jù)。如果主節(jié)點(diǎn)負(fù)載較高時(shí),來(lái)不及處理從服務(wù)的復(fù)制請(qǐng)求,就會(huì)導(dǎo)致復(fù)制延遲。大量寫操作無(wú)法避免。但是我們可優(yōu)化下寫入的結(jié)構(gòu),精簡(jiǎn)數(shù)據(jù),降低單條數(shù)據(jù)的大小。
- 復(fù)制緩存區(qū)溢出:復(fù)制緩存區(qū)暫存當(dāng)前主節(jié)點(diǎn)接收到的寫命令,待傳輸給從節(jié)點(diǎn)。如果從節(jié)點(diǎn)處理過(guò)慢,寫入的命令又過(guò)多,則會(huì)導(dǎo)致復(fù)制緩沖區(qū)溢出,此時(shí)從節(jié)點(diǎn)就需要重新執(zhí)行全量復(fù)制,導(dǎo)致延遲??赏ㄟ^(guò) client-output-buffer-limit間接控制緩沖區(qū)大小
- 主節(jié)點(diǎn)持久化,無(wú)法及時(shí)響應(yīng)復(fù)制請(qǐng)求:生成 RDB 快照或 AOF 文件重寫都會(huì)占用大量的 CPU 和 I/O 資源,可能會(huì)影響復(fù)制的速度。避免在高峰期觸發(fā)持久化動(dòng)作。
- 從節(jié)點(diǎn)配置太差:因?yàn)閺墓?jié)點(diǎn)需要接收、處理和存儲(chǔ)主節(jié)點(diǎn)發(fā)送的數(shù)據(jù)。如果從節(jié)點(diǎn)性能較低,處理數(shù)據(jù)的速度會(huì)慢,從而導(dǎo)致延遲。此時(shí)需要升配。
Redis 的哨兵機(jī)制是什么?
Redis 的哨兵機(jī)制(Sentinel)是一種高可用性解決方案,用于監(jiān)控 Redis 主從集群,自動(dòng)完成主從切換,以實(shí)現(xiàn)故障自動(dòng)恢復(fù)和通知。主要功能包括:
- 監(jiān)控:哨兵不斷監(jiān)控 Redis 主節(jié)點(diǎn)和從節(jié)點(diǎn)的運(yùn)行狀態(tài),定期發(fā)送 PING 請(qǐng)求檢查節(jié)點(diǎn)是否正常。
- 自動(dòng)故障轉(zhuǎn)移:當(dāng)主節(jié)點(diǎn)發(fā)生故障時(shí),哨兵會(huì)選舉一個(gè)從節(jié)點(diǎn)提升為新的主節(jié)點(diǎn),并通知客戶端更新主節(jié)點(diǎn)的地址,從而實(shí)現(xiàn)高可用。
- 通知:哨兵可以向系統(tǒng)管理員或其他服務(wù)發(fā)送通知,以便快速處理 Redis 實(shí)例的狀態(tài)變化。
哨兵Sentinel工作原理?
- 每個(gè)
Sentinel以每秒鐘一次的頻率向它所知道的Master,Slave以及其他Sentinel實(shí)例發(fā)送一個(gè)PING命令。 - 如果一個(gè)實(shí)例距離最后一次有效回復(fù)
PING命令的時(shí)間超過(guò)指定值, 則這個(gè)實(shí)例會(huì)被Sentine標(biāo)記為主觀下線。 - 如果一個(gè)
Master被標(biāo)記為主觀下線,則正在監(jiān)視這個(gè)Master的所有Sentinel要以每秒一次的頻率確認(rèn)Master是否真正進(jìn)入主觀下線狀態(tài)。 - 當(dāng)有足夠數(shù)量的
Sentinel(大于等于配置文件指定值)在指定的時(shí)間范圍內(nèi)確認(rèn)Master的確進(jìn)入了主觀下線狀態(tài), 則Master會(huì)被標(biāo)記為客觀下線 。若沒(méi)有足夠數(shù)量的Sentinel同意Master已經(jīng)下線,Master的客觀下線狀態(tài)就會(huì)被解除。 若Master重新向Sentinel的PING命令返回有效回復(fù),Master的主觀下線狀態(tài)就會(huì)被移除。 - 哨兵節(jié)點(diǎn)會(huì)選舉出哨兵 leader,負(fù)責(zé)故障轉(zhuǎn)移的工作。
- 哨兵 leader 會(huì)推選出某個(gè)表現(xiàn)良好的從節(jié)點(diǎn)成為新的主節(jié)點(diǎn),然后通知其他從節(jié)點(diǎn)更新主節(jié)點(diǎn)信息。
Redis cluster實(shí)現(xiàn)原理?
哨兵模式解決了主從復(fù)制不能自動(dòng)故障轉(zhuǎn)移、達(dá)不到高可用的問(wèn)題,但還是存在主節(jié)點(diǎn)的寫能力、容量受限于單機(jī)配置的問(wèn)題。而cluster模式實(shí)現(xiàn)了Redis的分布式存儲(chǔ),每個(gè)節(jié)點(diǎn)存儲(chǔ)不同的內(nèi)容,解決主節(jié)點(diǎn)的寫能力、容量受限于單機(jī)配置的問(wèn)題。
Redis cluster集群節(jié)點(diǎn)最小配置6個(gè)節(jié)點(diǎn)以上(3主3從),其中主節(jié)點(diǎn)提供讀寫操作,從節(jié)點(diǎn)作為備用節(jié)點(diǎn),不提供請(qǐng)求,只作為故障轉(zhuǎn)移使用。
Redis cluster采用虛擬槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0~16383個(gè)整數(shù)槽內(nèi),每個(gè)節(jié)點(diǎn)負(fù)責(zé)維護(hù)一部分槽以及槽所映射的鍵值數(shù)據(jù)。
圖片
工作原理:
- 通過(guò)哈希的方式,將數(shù)據(jù)分片,每個(gè)節(jié)點(diǎn)均分存儲(chǔ)一定哈希槽(哈希值)區(qū)間的數(shù)據(jù),默認(rèn)分配了16384 個(gè)槽位
- 每份數(shù)據(jù)分片會(huì)存儲(chǔ)在多個(gè)互為主從的多節(jié)點(diǎn)上
- 數(shù)據(jù)寫入先寫主節(jié)點(diǎn),再同步到從節(jié)點(diǎn)(支持配置為阻塞同步)
- 同一分片多個(gè)節(jié)點(diǎn)間的數(shù)據(jù)不保持一致性
- 讀取數(shù)據(jù)時(shí),當(dāng)客戶端操作的key沒(méi)有分配在該節(jié)點(diǎn)上時(shí),redis會(huì)返回轉(zhuǎn)向指令,指向正確的節(jié)點(diǎn)
- 擴(kuò)容時(shí)時(shí)需要需要把舊節(jié)點(diǎn)的數(shù)據(jù)遷移一部分到新節(jié)點(diǎn)
在 Redis cluster 架構(gòu)下,每個(gè) redis 要放開兩個(gè)端口號(hào),比如一個(gè)是 6379,另外一個(gè)就是 加1w 的端口號(hào),比如 16379。
16379 端口號(hào)是用來(lái)進(jìn)行節(jié)點(diǎn)間通信的,也就是 cluster bus 的東西,cluster bus 的通信,用來(lái)進(jìn)行故障檢測(cè)、配置更新、故障轉(zhuǎn)移授權(quán)。cluster bus 用了另外一種二進(jìn)制的協(xié)議,gossip 協(xié)議,用于節(jié)點(diǎn)間進(jìn)行高效的數(shù)據(jù)交換,占用更少的網(wǎng)絡(luò)帶寬和處理時(shí)間。
優(yōu)點(diǎn):
- 無(wú)中心架構(gòu),支持動(dòng)態(tài)擴(kuò)容。
- 數(shù)據(jù)按照
slot存儲(chǔ)分布在多個(gè)節(jié)點(diǎn),節(jié)點(diǎn)間數(shù)據(jù)共享,可動(dòng)態(tài)調(diào)整數(shù)據(jù)分布。 - 高可用性。部分節(jié)點(diǎn)不可用時(shí),集群仍可用。集群模式能夠?qū)崿F(xiàn)自動(dòng)故障轉(zhuǎn)移(failover),節(jié)點(diǎn)之間通過(guò)
gossip協(xié)議交換狀態(tài)信息,用投票機(jī)制完成Slave到Master的角色轉(zhuǎn)換。
缺點(diǎn):
- 不支持批量操作(pipeline)。
- 數(shù)據(jù)通過(guò)異步復(fù)制,不保證數(shù)據(jù)的強(qiáng)一致性。
- 事務(wù)操作支持有限,只支持多
key在同一節(jié)點(diǎn)上的事務(wù)操作,當(dāng)多個(gè)key分布于不同的節(jié)點(diǎn)上時(shí)無(wú)法使用事務(wù)功能。 key作為數(shù)據(jù)分區(qū)的最小粒度,不能將一個(gè)很大的鍵值對(duì)象如hash、list等映射到不同的節(jié)點(diǎn)。- 不支持多數(shù)據(jù)庫(kù)空間,單機(jī)下的Redis可以支持到16個(gè)數(shù)據(jù)庫(kù),集群模式下只能使用1個(gè)數(shù)據(jù)庫(kù)空間。
- 只能使用0號(hào)數(shù)據(jù)庫(kù)。
Redis Cluster 模式與 Sentinel 模式的區(qū)別是什么?
- Redis Cluster 是 Redis 集群,提供自動(dòng)分片功能,將數(shù)據(jù)自動(dòng)分布在多個(gè)節(jié)點(diǎn)上,支持自動(dòng)故障轉(zhuǎn)移。如果一個(gè)節(jié)點(diǎn)失敗,集群會(huì)自動(dòng)重新配置和平衡,不需要外部介入,因?yàn)樗鼉?nèi)置了哨兵邏輯。
- Sentinel是哨兵,主要用于管理多個(gè) Redis 服務(wù)器實(shí)例來(lái)提高數(shù)據(jù)的高可用性。當(dāng)主節(jié)點(diǎn)宕機(jī),哨兵會(huì)將從節(jié)點(diǎn)提升為主節(jié)點(diǎn),它并不提供數(shù)據(jù)分片功能。如果需要處理大量數(shù)據(jù)并進(jìn)行數(shù)據(jù)分片,應(yīng)選擇 Redis Cluster,它支持水平擴(kuò)展,適用于大規(guī)模數(shù)據(jù)、高吞吐量場(chǎng)景。
如果只是為了提高 Redis 實(shí)例的可用性,并不需要數(shù)據(jù)分片,應(yīng)選擇 主從+Sentinel,它主要關(guān)注故障轉(zhuǎn)移和實(shí)例高可用,適用于高可用性、讀寫分離場(chǎng)景。
Redis 集群會(huì)出現(xiàn)腦裂問(wèn)題嗎?
Redis 集群存在腦裂問(wèn)題的風(fēng)險(xiǎn),特別是在網(wǎng)絡(luò)分區(qū)的情況下,可能會(huì)導(dǎo)致同一集群內(nèi)出現(xiàn)多個(gè)主節(jié)點(diǎn),導(dǎo)致數(shù)據(jù)不一致。
Redis 中如何避免腦裂問(wèn)題的發(fā)生呢?
這里需要了解兩個(gè)參數(shù):
- min-slaves-to-write:設(shè)置主節(jié)點(diǎn)在至少有指定數(shù)量的從節(jié)點(diǎn)確認(rèn)寫操作的情況下才執(zhí)行寫操作。
- min-salves-max-lag:設(shè)置從節(jié)點(diǎn)的最大延遲(以秒為單位),如果從節(jié)點(diǎn)的延遲超過(guò)這個(gè)值,則該從節(jié)點(diǎn)不會(huì)被計(jì)入 min-slaves-to-write 的計(jì)數(shù)中 舉個(gè)例子:當(dāng) min-slaves-to-write設(shè)置為2,min-slaves-max-lag設(shè)置為 10 秒時(shí),主節(jié)點(diǎn)只有在至少有2 個(gè)從節(jié)點(diǎn)延遲不超過(guò) 10 秒的情況下才會(huì)接受寫操作,這兩個(gè)參數(shù)就使得發(fā)生腦裂的時(shí)候,如果某個(gè)主節(jié)點(diǎn)跟隨的從節(jié)點(diǎn)數(shù)量不夠或延遲較大,就無(wú)法被寫入,這樣就能避免腦裂導(dǎo)致的數(shù)據(jù)不一致。建議集群部署奇數(shù)個(gè)節(jié)點(diǎn),例如集群數(shù)為5,那么可以設(shè)置 min-slaves-to-write為3,min-slaves-max-lag為 5-10 秒。
腦裂問(wèn)題能完全避免嗎?
并不能。即使配置了以上兩個(gè)參數(shù)也可能會(huì)因?yàn)槟X裂導(dǎo)致數(shù)據(jù)不一致。
舉個(gè)例子,假設(shè)某個(gè)主節(jié)點(diǎn)臨時(shí)出了問(wèn)題,哨兵判斷它主觀下線,然后開始發(fā)起選舉。在選舉進(jìn)行的時(shí)候,主節(jié)點(diǎn)恢復(fù)了,此時(shí)它還是跟著很多從節(jié)點(diǎn),假設(shè) min-slaves-max-log 配置了10s,可能此時(shí)從節(jié)點(diǎn)和主節(jié)點(diǎn)延遲的時(shí)間才 6s,因此此時(shí)主節(jié)點(diǎn)還是可以被寫入。而等選舉完畢了,選出新的主節(jié)點(diǎn),舊的主節(jié)點(diǎn)被哨兵操作需要 salveof 新主,此時(shí)選舉時(shí)間內(nèi)寫入的數(shù)據(jù)會(huì)被覆蓋,因此就導(dǎo)致了數(shù)據(jù)不一致的問(wèn)題
哈希分區(qū)算法有哪些?
- 節(jié)點(diǎn)取余分區(qū)。 使用特定的數(shù)據(jù),如Redis的鍵或用戶ID,對(duì)節(jié)點(diǎn)數(shù)量N取余:hash(key)%N計(jì)算出哈希值,用來(lái)決定數(shù)據(jù)映射到哪一個(gè)節(jié)點(diǎn)上。
優(yōu)點(diǎn)是簡(jiǎn)單性。擴(kuò)容時(shí)通常采用翻倍擴(kuò)容,避免數(shù)據(jù)映射全部被打亂導(dǎo)致全量遷移的情況。
- 一致性哈希分區(qū)。
為系統(tǒng)中每個(gè)節(jié)點(diǎn)分配一個(gè)token,范圍一般在0~232,這些token構(gòu)成一個(gè)哈希環(huán)。數(shù)據(jù)讀寫執(zhí)行節(jié)點(diǎn)查找操作時(shí),先根據(jù)key計(jì)算hash值,然后順時(shí)針找到第一個(gè)大于等于該哈希值的token節(jié)點(diǎn)。
這種方式相比節(jié)點(diǎn)取余最大的好處在于加入和刪除節(jié)點(diǎn)只影響哈希環(huán)中相鄰的節(jié)點(diǎn),對(duì)其他節(jié)點(diǎn)無(wú)影響。
- 虛擬Hash槽分區(qū)
所有的鍵根據(jù)哈希函數(shù)映射到0~16383整數(shù)槽內(nèi),計(jì)算公式:slot=CRC16(key)&16383。每一個(gè)節(jié)點(diǎn)負(fù)責(zé)維護(hù)一部分槽以及槽所映射的鍵值數(shù)據(jù)。Redis Cluser采用虛擬槽分區(qū)算法。
為什么Redis集群采用“hash槽”來(lái)解決數(shù)據(jù)分配問(wèn)題,而不采用“一致性hash”算法呢?
- 一致性哈希的節(jié)點(diǎn)分布基于圓環(huán),無(wú)法很好的手動(dòng)控制數(shù)據(jù)分布,比如有些節(jié)點(diǎn)的硬件差,希望少存一點(diǎn)數(shù)據(jù),這種很難操作(還得通過(guò)虛擬節(jié)點(diǎn)映射,總之較繁瑣)。
- 而redis集群的槽位空間是可以用戶手動(dòng)自定義分配的,類似于 windows 盤分區(qū)的概念,可以手動(dòng)控制大小。
- 其實(shí),無(wú)論是 “一致性哈希” 還是 “hash槽” 的方式,在增減節(jié)點(diǎn)的時(shí)候,都會(huì)對(duì)一部分?jǐn)?shù)據(jù)產(chǎn)生影響,都需要我們遷移數(shù)據(jù)。當(dāng)然,redis集群也提供了相關(guān)手動(dòng)遷移槽數(shù)據(jù)的命令。
為什么 Redis 集群的最大槽數(shù)是 16384 個(gè)?
Redis Cluster 采用數(shù)據(jù)分片機(jī)制,定義了 16384個(gè) Slot槽位,集群中的每個(gè)Redis 實(shí)例負(fù)責(zé)維護(hù)一部分槽以及槽所映射的鍵值數(shù)據(jù)。
Redis每個(gè)節(jié)點(diǎn)之間會(huì)定期發(fā)送ping/pong消息(心跳包包含了其他節(jié)點(diǎn)的數(shù)據(jù)),用于交換數(shù)據(jù)信息。
Redis集群的節(jié)點(diǎn)會(huì)按照以下規(guī)則發(fā)ping消息:
- 每秒會(huì)隨機(jī)選取5個(gè)節(jié)點(diǎn),找出最久沒(méi)有通信的節(jié)點(diǎn)發(fā)送ping消息。
- 每100毫秒都會(huì)掃描本地節(jié)點(diǎn)列表,如果發(fā)現(xiàn)節(jié)點(diǎn)最近一次接受pong消息的時(shí)間大于cluster-node-timeout/2 則立刻發(fā)送ping消息。
心跳包的消息頭里面有個(gè)myslots的char數(shù)組,是一個(gè)bitmap,每一個(gè)位代表一個(gè)槽,如果該位為1,表示這個(gè)槽是屬于這個(gè)節(jié)點(diǎn)的。
接下來(lái),解答為什么 Redis 集群的最大槽數(shù)是 16384 個(gè),而不是65536 個(gè)。
- 如果采用 16384 個(gè)插槽,那么心跳包的消息頭占用空間 2KB (16384/8);如果采用 65536 個(gè)插槽,那么心跳包的消息頭占用空間 8KB (65536/8)。可見(jiàn)采用 65536 個(gè)插槽,發(fā)送心跳信息的消息頭達(dá)8k,比較浪費(fèi)帶寬。
- 一般情況下一個(gè)Redis集群不會(huì)有超過(guò)1000個(gè)master節(jié)點(diǎn),太多可能導(dǎo)致網(wǎng)絡(luò)擁堵。
- 哈希槽是通過(guò)一張bitmap的形式來(lái)保存的,在傳輸過(guò)程中,會(huì)對(duì)bitmap進(jìn)行壓縮。bitmap的填充率越低,壓縮率越高。其中bitmap 填充率 = slots / N (N表示節(jié)點(diǎn)數(shù))。所以,插槽數(shù)越低, 填充率會(huì)降低,壓縮率會(huì)提高。
在 Redis 集群中,如何根據(jù)鍵定位到對(duì)應(yīng)的節(jié)點(diǎn)?
Redis 集群將數(shù)據(jù)分布到 16384 個(gè)哈希槽(sots),每個(gè)鍵通過(guò)哈希函數(shù)計(jì)算出一個(gè)槽位編號(hào),然后根據(jù)槽位編號(hào)定位到縣體的節(jié)點(diǎn),具體是使用 CRC16 哈希函數(shù)計(jì)算鍵的哈希值,然后對(duì) 16384 取模, 得到哈希槽編號(hào)(范圍是0到16383)。
Redis集群會(huì)有寫操作丟失嗎?為什么?
在Redis集群中,由于采用了主從復(fù)制模型的異步復(fù)制機(jī)制,寫操作有一定的丟失風(fēng)險(xiǎn)。
當(dāng)客戶端向主節(jié)點(diǎn)發(fā)送寫操作時(shí),主節(jié)點(diǎn)會(huì)立即返回成功響應(yīng),而不等待所有從節(jié)點(diǎn)執(zhí)行復(fù)制。如果主節(jié)點(diǎn)在執(zhí)行完寫操作后出現(xiàn)故障或網(wǎng)絡(luò)問(wèn)題,導(dǎo)致從節(jié)點(diǎn)無(wú)法及時(shí)接收到復(fù)制操作,那么這些未復(fù)制的寫操作將會(huì)丟失。
為了減少寫操作丟失的可能性,可以采取以下措施:
- 定期監(jiān)測(cè)集群狀態(tài),確保主從節(jié)點(diǎn)之間的復(fù)制正常進(jìn)行;
- 設(shè)置合理的持久化策略,將數(shù)據(jù)寫入磁盤或使用AOF模式以便數(shù)據(jù)恢復(fù);
- 在應(yīng)用程序?qū)訉?shí)施數(shù)據(jù)確認(rèn)機(jī)制,檢查寫操作是否成功。
Redis 中如何保證緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性?
- 先更新緩存,再更新數(shù)據(jù)庫(kù)
- 先更新數(shù)據(jù)庫(kù)存,再更新緩存
- 先刪除緩存,再更新數(shù)據(jù)庫(kù),后續(xù)等查詢把數(shù)據(jù)庫(kù)的數(shù)據(jù)回種到緩存中
- 先更新數(shù)據(jù)庫(kù),再刪除緩存,后續(xù)等查詢把數(shù)據(jù)庫(kù)的數(shù)據(jù)回種到緩存中
- 緩存雙刪策略。更新數(shù)據(jù)庫(kù)之前,刪除一次緩存;更新完數(shù)據(jù)庫(kù)后,再進(jìn)行一次延遲刪除
- 使用 Binlog 異步更新緩存,監(jiān)聽數(shù)據(jù)庫(kù)的 Binlog 變化,通過(guò)異步方式更新 Redis 緩存
以上就是實(shí)現(xiàn)數(shù)據(jù)庫(kù)與緩存一致性的六種方式,這里前面三種都不太推薦使用,后面三種需要根據(jù)實(shí)際場(chǎng)景選擇:
- 如果是要考慮實(shí)時(shí)一致性的話,先寫 MySQL,再刪除 Redis 應(yīng)該是較為優(yōu)的方案,雖然短期內(nèi)數(shù)據(jù)可能不一致,不過(guò)其能盡量保證數(shù)據(jù)的一致性。
- 如果考慮最終一致性的話,推薦的是使用 binlog + 消息隊(duì)列的方式,這個(gè)方案其有重試和順序消費(fèi),能夠最大限度地保證緩存與數(shù)據(jù)庫(kù)的最終一致性:。


































