Redis設計

2023-07-21 18:03:31

過期鍵刪除策略

對於過期鍵值的刪除有三種常見的做法

  1. 定時刪除。為每一個過期的鍵建立一個定時器,過期立刻刪除。
    優點:及時刪除過期鍵值,釋放記憶體空間
    缺點:如果過期鍵值較多時,在刪除過期鍵值上佔用的CPU較多,而在記憶體充足的情況下,過期鍵值其實是不必急著刪除的,應該優先把CPU用在處理使用者端請求上
  2. 惰性刪除。當存取鍵的時候判斷這個鍵是否已過期,過期就刪除。
    優點:對CPU佔用最少,不會在刪除其他過期的鍵上花費時間
    缺點:不能及時刪除鍵,釋放記憶體。如果一個過期的鍵永遠沒有被存取,那麼這個鍵就不會被刪除,無用的垃圾資料會堆積在記憶體中,造成記憶體洩露
  3. 定期刪除。每隔一段時間遍歷一遍所有設定了過期時間的鍵,刪除已過期鍵。
    優點:相對定時刪除,佔用的CPU時間更少,相對惰性刪除,在執行頻率設定合理的情況下也能及時刪除過期鍵,釋放記憶體
    缺點:頻率很難設定,設定的過於頻繁會退化成定時刪除,設定的執行間隔太長,會導致過期鍵不能及時刪除

Redis採用了惰性刪除和定期刪除,可以在合理使用CPU時間和避免浪費記憶體空間上取得平衡。另外在定期刪除策略上也做了優化,每次執行定期刪除會有最大執行時間限制,如果達到時間上限還沒執行完會記錄當前位置並停止處理,下次從紀錄點繼續執行

持久化

Redis是記憶體資料庫,當Redis程序退出後,儲存在記憶體中的資料就會丟失。為了解決這個問題,Redis提供了RDB跟AOF兩種方式,將資料庫狀態進行持久化到磁碟中

RDB

RDB檔案儲存了Redis在某個時間點上的資料庫狀態,類似於快照,可以使用RDB檔案將Redis資料恢復。Redis提供了兩個命令生成RDB檔案,分別是SAVE跟BGSAVE,區別是SAVE是由伺服器程序直接執行,會阻塞伺服器,BGSAVE是由子執行緒執行儲存,不會阻塞伺服器。
Redis預設設定了多個儲存條件,900秒內對資料庫修改1次、300秒內對資料庫修改10次、60秒內對資料庫修改10000次,伺服器中有一個週期性執行的serverCron預設每隔100毫秒執行一次,在serverCron執行時檢查只要其中任何一個條件得到滿足就會執行BGSAVE命令自動儲存。

AOF

除了RDB外,Redis還提供了AOF持久化功能,區別於RDB是通過直接儲存資料庫鍵值,AOF是通過儲存伺服器執行的寫命令來記錄資料庫狀態。當伺服器的AOF功能開啟的時候,每當伺服器進行寫操作,都會有對應的寫命令記錄在AOF的緩衝區中,緩衝區中的資料會根據設定持久化到檔案中,目前AOF提供三種持久化的選項,分別是always(將緩衝區所有內容寫入並同步到檔案)、erverysec(每秒將緩衝區內容寫入到檔案)、no(由作業系統控制何時同步)

使用AOF還原資料庫狀態流程

AOF重寫

前面講到,AOF檔案是具體執行的寫命令記錄,隨著執行命令的次數越來越多,AOF檔案內容會越來越大,為了解決這個問題,Redis提供了AOF檔案重寫的功能。通過這個功能,Redis伺服器可以建立一個新的AOF檔案來替代現有的AOF檔案,新舊兩個AOF檔案所儲存的資料庫狀態相同,但新AOF檔案不會包含冗餘命令。實現原理是從資料庫中讀取鍵當前的值,然後用生成一條命令去代替之前記錄這個鍵值的多條命令。
例如:

實際執行命令
sadd animals "cat"
sadd animals "dog" "panda" "tiger"
srem animal "cat"
sadd animals "lion" "cat"

重寫後儲存命令
sadd animals "dog" "panda" "tiger" "lion" "cat"

另外AOF重寫一般是使用子執行緒在後臺執行,在重寫期間伺服器仍然繼續處理使用者端的請求。在重寫期間,如果伺服器端處理的請求導致現有的資料庫內容被修改,就會導致伺服器當前的資料庫狀態與重寫後的AOF檔案儲存的資料庫狀態不一致

為了解決這個問題,Redis伺服器中設定了AOF重寫緩衝區,會將AOF重寫期間執行的寫命令追加到AOF重寫緩衝區中,等子執行緒重寫完成後,再將緩衝區內容追加到AOF檔案末尾,保持資料庫狀態一致

主從複製

Redis的複製功能分為同步和命令傳播兩個操作:

  • 同步操作用於將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態
  • 命令傳播操作則用於在主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態出現不一致時,讓主從伺服器的資料庫重新回到一致狀態

這個複製功能對於初次複製來講能夠很好地完成任務,但是對於斷線後重複製來說,有可能從伺服器跟主伺服器資料只發生了幾次變化甚至完全一致,但這個複製卻要重新全量的覆蓋一遍從伺服器,效率非常低。為此Redis在2.8版本後開始,改進了複製功能,適用PSYNC命令代替SYNC命令。

完整重同步和部分重同步

PSYNC命令分為完整重同步和部分重同步兩種操作

  • 完整重同步:完整重同步的執行步驟和SYNC命令的執行步驟基本一樣,它們都是通過讓主伺服器建立並行送RDB檔案,以及向從伺服器傳送儲存在緩衝區裡面的寫命令來進行同步
  • 部分重同步:用於處理斷線後重複製情況:當從伺服器在斷線後重新連線主伺服器時,主伺服器將一部分最近傳播的寫命令儲存在複製積壓緩衝區中,如果從伺服器的複製偏移量之後的資料仍然存在複製積壓緩衝區中,主伺服器可以將主從伺服器連線斷開期間執行的寫命令傳送給從伺服器,從伺服器只要接收並執行這些寫命令,就可以將資料庫更新至主伺服器當前所處的狀態


哨兵Sentinel

哨兵對redis伺服器叢集的監聽


主觀下線:Sentinel對redis服務節點預設每秒傳送心跳檢測節點是否線上,但Redis範例連續向Sentinel返回無效回覆,該Sentinel節點會將範例標記為主觀下線狀態。
客觀下線:當Sentinel將一個主伺服器判斷為主觀下線之後,為了確認這個主伺服器是否真的下線了,它會向同樣監視這一主伺服器的其它Sentinel進行詢問,看它們是否也認為主伺服器已經進入了下線狀態(可以是主觀下線或者客觀下線)。當Sentinel從其他Sentinel那裡接收到足夠數量的已下線判斷後,Sentinel就會將主服務判定為客觀下線,並對主服務執行故障轉移。

執行者選舉

Sentinel確認主服務已經客觀下線後,會要求其他Sentinel節點將自己設為執行故障轉移的節點,其他節點採用搶佔的方式,只接受第一個到達的搶佔請求,並返回當前節點認可的執行節點ID,通過統計,可以得到最終過半數勝出的領頭Sentinel執行故障轉移

故障轉移

在選舉產生出領頭Sentinel之後,領頭Sentinel將對已下線的主伺服器執行故障轉移操作,該操作包含以下三個步驟:

  1. 在已下線主伺服器屬下的所有從伺服器中,挑選出一個從伺服器,並將其轉換為主伺服器
  2. 讓已下線主伺服器屬下的所有從伺服器改為複製新的主伺服器
  3. 將已下線主伺服器設定為新的主伺服器的從伺服器,當這個舊的主伺服器重新上線時,它就會成為新的主伺服器的從伺服器

選擇新的主伺服器流程

  1. 篩選出線上的從伺服器
  2. 根據從伺服器的優先順序,對從伺服器進行排序,選擇優先順序最高從伺服器
  3. 同最高優先順序的從伺服器,選擇複製偏移量最大的從伺服器(儲存著最新資料)
  4. 以上都相同時,選擇伺服器執行ID最小的從伺服器