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