Redis叢集高頻問答,連夜肝出來了

桑小榆的堅定學說發表於2022-05-19

Redis叢集高頻問答,連夜肝出來了

Redis 叢集方案

Redis叢集方案應該怎麼做?都有哪些方案? 

 

使用codis方案:目前用的多的叢集方案,基本和twemproxy一致的效果,但它支援在節點數量改變情況下,舊節點資料可恢復到新hash節點。

 

Redis cluster3.0自帶的叢集:特點在於他的分散式演算法不是一致性hash,而是hash槽的概念,以及自身支援節點設定從節點。

 

在業務程式碼層實現:起幾個毫無關聯的Redis例項,在程式碼層對key進行hash計算,然後去對應的Redis例項運算元據。這種方式對hash層程式碼要求比較高,考慮部分包括,節點失效後的替代演算法方案,資料震盪後的自動指令碼恢復,例項的監控等等。

 

Redis叢集方案什麼情況下會導致整個叢集不可用? 

 

如有A,B,C三個節點的叢集,在沒有複製模型的情況下,如果節點B失敗了,那麼整個叢集就會以為缺少 5501-11000這個範圍的槽而不可用。

 

Redis叢集高頻問答,連夜肝出來了

 

Redis叢集的主從複製模型是怎樣的? 

 

為了使在部分節點失敗或者大部分節點無法通訊的情況下叢集仍然可用,所以叢集使用了主從複製模型, 每個節點都會有N-1個複製品。

 

Redis叢集會有寫操作丟失嗎?為什麼? 

 

Redis並不能保證資料的強一致性,這意味這在實際中叢集在特定的條件下可能會丟失寫操作。

 

Redis叢集之間是如何複製的? 

 

Redis叢集之間是通過非同步複製。 

 

Redis叢集最大節點個數是多少? 

 

16384個。

 

Redis叢集如何選擇資料庫? 

 

Redis叢集目前無法做資料庫選擇,預設在0資料庫。

Redis 持久化方案

Redis的持久化是什麼? 

 

RDB持久化:該機制可以在指定的時間間隔內生成資料集的時間點快照(point-in-time snapshot)。

 

AOF持久化:記錄伺服器執行的所有寫操作命令,並在伺服器啟動時,通過重新執行這些命令來還原資料集。

 

AOF檔案中的命令全部以Redis協議的格式來儲存,新命令會被追加到檔案的末尾。Redis還可以 在後臺對AOF檔案進行重寫(rewrite),使得AOF檔案的體積不會超出儲存資料集狀態所需的實際大小。

 

AOF和RDB的同時應用:當Redis重啟時,它會優先使用AOF檔案來還原資料集,因為AOF檔案儲存的數 據集通常比RDB檔案所儲存的資料集更完整。

 

RDB的優缺點? 

 

優點:RDB是一個非常緊湊(compact)的檔案,它儲存了Redis在某個時間點上的資料集。這種檔案非常適合用於進行備份:比如說你可以在近的24小時內,每小時備份一次RDB檔案,並且在每個月的每一天,也備份一個RDB檔案。這樣的話,即使遇上問題也可以隨時將資料集還原到不同的版本。

 

RDB非常適用於災難恢復(disaster recovery):它只有一個檔案,並且內容都非常緊湊,可以(在加密後)將它傳送到別的資料中心,或者亞馬遜S3中。

 

RDB可以最大化Redis的效能:父程式在儲存RDB檔案時唯一要做的就是fork出一個子程式,然後這個子程式就會處理接下來的所有儲存工作,父程式無須執行任何磁碟I/O操作。RDB在恢復大資料集時的速度比AOF的恢復速度要快。

 

缺點:如果你需要儘量避免在伺服器故障時丟失資料,那麼RDB不適合你。雖然Redis允許你設定不同的儲存點(save point)來控制儲存RDB檔案的頻率,但是,因為RDB檔案需要儲存整個資料集的狀態,所以它並不是一個輕鬆的操作。因此你可能會至少5分鐘才儲存一次RDB檔案。

 

在這種情況下,一旦發生故障停機,你就可能會丟失好幾分鐘的資料。每次儲存RDB的時候,Redis都要 fork()出一個子程式,並由子程式來進行實際的持久化工作。

 

在資料集比較龐大時,fork()可能會非常耗時,造成伺服器在某某毫秒內停止處理客戶端;如果資料集非常巨大,並且CPU時間非常緊張 的話,那麼這種停止時間甚至可能會長達整整一秒。

 

AOF的優缺點?

 

優點:使用AOF持久化會讓Redis變得非常耐久(much more durable):你可以設定不同的fsync策略,比如無fsync,每秒鐘一次fsync,或者每次執行寫入命令時fsync。

 

AOF的預設策略為每秒鐘fsync一次,在這種配置下,Redis仍然可以保持良好的效能,並且就算髮生故障停機,也多隻會丟失一秒鐘的資料(fsync會在後臺執行緒執行,所以主執行緒可以繼續努力地處理命令請求)。

 

AOF檔案是一個只進行追加 操作的日誌檔案(append onlylog),因此對AOF檔案的寫入不需要進行seek,即使日誌因為某些原因 而包含了未寫入完整的命令(比如寫入時磁碟已滿,寫入中途停機等等),redis-check-aof工具也可以輕易地修復這種問題。

 

Redis可以在AOF檔案體積變得過大時,自動地在後臺對AOF進行重寫:重寫後的新AOF檔案包含了恢復當前資料集所需的小命令集合。

 

整個重寫操作是絕對安全的,因為Redis在建立新AOF檔案的過程 中,會繼續將命令追加到現有的AOF檔案裡面,即使重寫過程中發生停機,現有的AOF檔案也不會丟 失。

 

而一旦新AOF檔案建立完畢,Redis就會從舊AOF檔案切換到新AOF檔案,並開始對新AOF檔案進行追加操作。

 

缺點:對於相同的資料集來說,AOF檔案的體積通常要大於RDB檔案的體積。根據所使用的fsync策略,AOF的速度可能會慢於RDB。

 

在一般情況下,每秒fsync的效能依然非常高,而關閉fsync可以讓AOF的速度和RDB一樣快,即使在高負荷之下也是如此。不過在處理巨大的寫入載入時,RDB可以提供更有保證的最大延遲時間(latency)。

 

AOF在過去曾經發生過這樣的bug:因為個別命令的原因,導致AOF檔案在重新載入時,無法將資料集 恢復成儲存時的原樣。

 

Redis叢集高頻問答,連夜肝出來了

 

如何選擇合適的持久化方式? 

 

(1)一般來說,如果想達到足以媲美PostgreSQL的資料安全性,你應該同時使用兩種持久化功能。

 

在這種情況下,當 Redis 重啟的時候會優先載入AOF檔案來恢復原始的資料,因為在通常情況下AOF檔案保 存的資料集要比RDB檔案儲存的資料集要完整。

 

(2)如果你非常關心你的資料, 但仍然可以承受數分鐘以內的資料丟失,那麼你可以只使用RDB持久化。

 

(3)有很多使用者都只使用AOF持久化,但並不推薦這種方式,因為定時生成RDB快照(snapshot)非常 便於進行資料庫備份, 並且 RDB 恢復資料集的速度也要比AOF恢復的速度要快,除此之外,使用RDB 還可以避免AOF程式的bug。

 

(4)如果你只希望你的資料在伺服器執行的時候存在,你也可以不使用任何持久化方式。

 

Redis持久化資料和快取怎麼做擴容? 

 

(1)如果Redis被當做快取使用,使用一致性雜湊實現動態擴容縮容。

 

(2)如果Redis被當做一個持久化儲存使用,必須使用固定的keys-to-nodes對映關係,節點的數量一旦確定不能變化。


否則的話(即Redis節點需要動態變化的情況),必須使用可以在執行時進行資料再平衡的一套系統,而當前只有Redis叢集可以做到這樣。

 

Redis的記憶體淘汰策略有哪些? 

 

Redis的記憶體淘汰策略是指在Redis的用於快取的記憶體不足時,怎麼處理需要新寫入且需要申請額外空間 的資料。

 

 

全域性的鍵空間選擇性移除 
noeviction:當記憶體不足以容納新寫入資料時,新寫入操作會報錯。

allkeys-lru:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近少使用的key。

allkeys-random:當記憶體不足以容納新寫入資料時,在鍵空間中,隨機移除某個key。


設定過期時間的鍵空間選擇性移除 
volatile-lru:當記憶體不足以容納新寫入資料時,且設定了過期時間的鍵空間中,移除最近少使用 的key。

volatile-random:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,隨機移除某 個key。

volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的 key優先移除。

 

簡單描述下Redis執行緒模型 

 

Redis基於Reactor模式開發了網路事件處理器,這個處理器被稱為檔案事件處理器(file event handler)。

 

它的組成結構為4部分:多個套接字、IO多路複用程式、檔案事件分派器、事件處理器


因為檔案事件分派器佇列的消費是單執行緒的,所以Redis才叫單執行緒模型

 

檔案事件處理器使用 I/O 多路複用(multiplexing)程式來同時監聽多個套接字, 並根據套接字目 前執行的任務來為套接字關聯不同的事件處理器。

 

當被監聽的套接字準備好執行連線應答(accept)、讀取(read)、寫入(write)、關閉 (close)等操作時, 與操作相對應的檔案事件就會產生, 這時檔案事件處理器就會呼叫套接字之前關聯好的事件處理器來處理這些事件。

 

雖然檔案事件處理器以單執行緒方式執行, 但通過使用 I/O 多路複用程式來監聽多個套接字,檔案事件處理器既實現了高效能的網路通訊模型,又可以很好地與Redis伺服器中其他同樣以單執行緒方式執行的模組進行對接,這保持了Redis內部單執行緒設計的簡單性。

 

Redis事務其他實現方式?

 

(1)基於Lua指令碼,Redis可以保證指令碼內的命令一次性、按順序地執行, 其同時也不提供事務執行錯誤的回滾,執行過程中如果部分命令執行錯誤,剩下的命令還是會繼續執行完。

 

(2)基於中間標記變數,通過另外的標記變數來標識事務是否執行完成,讀取資料時先讀取該標記變數 判斷是否事務執行完成。但這樣會需要額外寫程式碼實現,比較繁瑣 。

 

Redis叢集高頻問答,連夜肝出來了

 

Redis 快取雪崩與快取擊穿

簡單說說快取雪崩及解決方法 

 

快取雪崩我們可以簡單的理解為:由於原有快取失效,新快取未到期間 (例如:我們設定快取時採用了相同的過期時間,在同一時刻出現大面積的快取過期),所有原本應該訪 問快取的請求都去查詢資料庫了,而對資料庫CPU和記憶體造成巨大壓力,嚴重的會造成資料庫當機。從而形成一系列連鎖反應,造成整個系統崩潰。)

 

解決辦法:大多數系統設計者考慮用加鎖( 使用多的解決方案)或者佇列的方式保證來保證不會有大量的執行緒對資料庫一次性進行讀寫,從而避免失效時大量的併發請求落到底層儲存系統上。還有一個簡單方案就是設定快取失效時間分散開。

 

快取穿透怎麼導致的? 

 

在高併發下查詢key不存在的資料,會穿過緩去存查詢資料庫。導致資料庫壓力過大而當機。

 

解決方法:

 

1.對查詢結果為空的情況也進行快取,快取時間(ttl)設定短一點,或者該key對應的資料insert了之後清理快取。缺點:快取太多空值佔用了更多的空間。

 

2.使用布隆過濾器。在快取之前在加一層布隆過濾器,在查詢的時候先去布隆過濾器查詢 key 是否存在,如果不存在就直接返回,存在再查快取和DB。

 

布隆過濾器原理:當一個元素被加入集合時,將這個元素通過n次Hash函式結果對映成一個陣列中的n個點,把它們置為1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了,如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。總之布隆過濾器是一個很大二進位制的位陣列,陣列裡面只存0和1。

 

專案中有出現過快取擊穿,簡單說說怎麼回事? 

 

快取在某個時間點過期的時候,恰好在這個時間點對這個Key有大量的併發請求過來,這些請求發現快取過期一般會從資料庫中載入資料並回設到快取,這個時候大併發的請求可能會瞬間把後端DB壓垮。

 

解決方案:

 

1.用分散式鎖控制訪問的執行緒,使用redis的setnx互斥鎖先進行判斷,這樣其他執行緒就處於等待狀態,保證不會有大併發操作去運算元據庫。

 

2. 不設超時時間,採用volatile-lru淘汰策略 缺點:會造成寫一致問題,當資料庫資料發生更新時,快取中的資料不會及時更新,這樣會造成資料庫中的資料與快取中的資料的不一致,應用會從快取中讀取到髒資料。可採用延時雙刪策略處理。

 

Redis 快取一致性與競爭

遇到快取一致性問題,你怎麼解決的? 

 

由於快取和資料庫不屬於同一個資料來源,本質上非原子操作,所以是無法保證強一致性的,只能去實現終一致性。

 

解決方案:

 

延時雙刪:先更新資料庫同時刪除快取,等2秒後再刪除一次快取,等到讀的時候再回寫到快取。

 

利用工具(canal)將資料庫的binlog日誌採集傳送到MQ中,然後通過ACK機制確認處理刪除快取 。

 

為什麼要用 Redis 而不用 map/guava 做快取?

 

快取分為本地快取和分散式快取:以 Java 為例,使用自帶的 map 或者 guava 實現的是本地快取, 主要的特點是輕量以及快速,生命週期隨著 jvm 的銷燬而結束,並且在多例項的情況下,每個例項都需要各自儲存一份快取,快取不具有一致性。

 

使用 Redis 或 MemoryCache之類的稱為分散式快取,在多例項的情況下,各例項共用一份快取資料,快取具有一致性。

 

缺點是需要保持 redis 或memcached服務的高可用,整個程式架構上較為複雜。

 

如何解決 Redis 的併發競爭Key問題? 

 

所謂 Redis 的併發競爭 Key 的問題也就是多個系統同時對一個 key 進行操作,但是後執行的順序和我們期望的順序不同,這樣也就導致了結果的不同。

 

推薦一種方案:分散式鎖(zookeeper 和 redis 都可以實現分散式鎖)。如果不存在 Redis 的併發競 爭 Key 問題,不要使用分散式鎖,這樣會影響效能。

 

基於zookeeper臨時有序節點可以實現的分散式鎖。

 

大致思想為:每個客戶端對某個方法加鎖時,在 zookeeper上的與該方法對應的指定節點的目錄下,生成一個唯一的瞬時有序節點。

 

判斷是否獲取鎖的方式很簡單,只需要判斷有序節點中序號小的一個。當釋放鎖的時候,只需將這個瞬時節點刪除即可。同時,其可以避免服務當機導致的鎖無法釋放,而產生的死鎖問題。完成業務流程後,刪除對應的子節點釋放鎖。

 

在實踐中,當然是從以可靠性為主,所以首推Zookeeper。

 

什麼是 RedLock? 

 

Redis 官方站提出了一種權威的基於 Redis 實現分散式鎖的方式名叫 Redlock,此種方式比原先的單節 點的方法更安全。


它可以保證以下特性:

 

安全特性:互斥訪問,即永遠只有一個 client 能拿到鎖 

避免死鎖:最終 client 都可能拿到鎖,不會出現死鎖的情況,即使原本鎖住某資源的 client crash 了或者出現了網路分割槽 

容錯性:只要大部分 Redis 節點存活就可以正常提供服務 

 

什麼時候需要快取降級? 

 

當訪問量劇增、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的效能時,仍然 需要保證服務還是可用的,即使是有損服務。

 

系統可以根據一些關鍵資料進行自動降級,也可以配置開 關實現人工降級。

快取降級的終目的是保證核心服務可用,即使是有損的。

 

而且有些服務是無法降級的(如加入購物 車、結算)。

 

在進行降級之前要對系統進行梳理,看看系統是不是可以丟卒保帥;從而梳理出哪些必須誓死保護,哪些可降級;比如可以參考日誌級別設定預案:

 

一般:比如有些服務偶爾因為網路抖動或者服務正在上線而超時,可以自動降級;

警告:有些服務在一段時間內成功率有波動(如在95~100%之間),可以自動降級或人工降級, 併傳送告警;

錯誤:比如可用率低於90%,或者資料庫連線池被打爆了,或者訪問量突然猛增到系統能承受的 大閥值,此時可以根據情況自動降級或者人工降級;

嚴重錯誤:比如因為特殊原因資料錯誤了,此時需要緊急人工降級。服務降級的目的,是為了防止Redis服務故障,導致資料庫跟著一起發生雪崩問題。
因此,對於不重要 的快取資料,可以採取服務降級策略,例如一個比較常見的做法就是,Redis出現問題,不去資料庫查 詢,而是直接返回預設值給使用者。

 

如何保證快取與資料庫雙寫時的資料一致性? 

 

你只要用快取,就可能會涉及到快取與資料庫雙儲存雙寫,你只要是雙寫,就一定會有資料一致性的問題,那麼你如何解決一致性問題?

 

一般來說,就是如果你的系統不是嚴格要求快取+資料庫必須一致性的話,快取可以稍微的跟資料庫偶爾有不一致的情況,好不要做這個方案,讀請求和寫請求序列化,串到一個記憶體佇列裡去,這樣就可以保證一定不會出現不一致的情況。

 

序列化之後,就會導致系統的吞吐量會大幅度的降低,用比正常情況下多幾倍的機器去支撐線上的一個請求。

 

還有一種方式就是可能會暫時產生不一致的情況,但是發生的機率特別小,就是先更新資料庫,然後再 刪除快取。

 

肝完啦,感謝友友們的支援!

相關文章