Redis Redis原理
Redis原理
Redis記憶體模型
redisServer
public class redisServer { int dbnum;// 當前redis節點內資料庫數量,預設16 redisDb[] db;// 陣列,儲存資料庫資訊 redisClient clients;// 連結串列,儲存客戶端資訊 // serverCron函式維護的屬性 Date unixtime;// 秒級別時間戳 long mstime;// 毫秒級別時間戳 Date lruclock;// LRU時鐘,每十秒更新一次 long ops_sec_samples;// Redis server每秒執行命令次數 long stat_peak_memory;// Redis server記憶體峰值記錄 int shutdown_asap;// Redis server執行狀態 1關閉 0執行 int cronloops;// serverCron函式計數器 // 持久化相關 String rdb_child_pid;// 執行BGSAVE子程序ID,-1表示未執行 String aof_child_pid;// 執行BGREWRITEAOF子程序ID,-1表示未執行 long dirty;// 修改計數器 Date lastsave;// 上次BGSAVE時間 sdshdr aof_buf;// AOF緩衝區 // 慢查詢相關 long slowlog_entry_id;// 下一條慢查詢日誌ID Object slowlog;// 慢查詢日誌連結串列 long slowlog_log_slower_than;// 超出該屬性值則為慢查詢,單位微秒 long slowlog_max_len;// 慢查詢日誌儲存數量 }
redisDb
public class redisDb{
dict dict;// 儲存鍵值對
dict expires;// 儲存設定過期時間的鍵和過期時間
dict watched_keys;// 儲存被WATCH監視的鍵
}
redisClient
public class redisClient{ redisDb db;// 當前客戶端正在使用的資料庫 sdshdr querybuf;// 輸入緩衝區 String[] argv;// 命令與命令引數陣列 int argc;// argv長度 sdshdr buf;// 輸出緩衝區 int bufpos;// buf已使用長度 int authenticated;// 0未通過身份驗證 1通過身份驗證 }
Redis資料結構
Redis執行機制
Redis server初始化
- 例項化redisServer物件
- 根據使用者指定引數和配置檔案初始化redisServer物件屬性
- 初始化redisServer物件其他屬性
- 建立常量:“OK"字串和"1”-"10000"字串
- 為serverCron建立時間事件
- 載入持久化檔案(AOF或RDB)
- 開始執行時間事件
第六步載入持久化檔案流程圖
Redis client傳送請求
- 將操作命令根據RESP協議進行封裝
- 通過套接字傳送給Redis server
Redis server接收請求
- 通過套接字接收請求內容,儲存至redisClient.querybuf(輸入緩衝區)
- 解析請求內容,儲存至redisClient.argv(操作命令與操作命令引數陣列)redisClient.argc(argv長度)
- 呼叫操作命令執行器
Redis server處理請求
- 根據操作命令去命令表查詢操作命令對應的命令函式(redisCommand)
- 根據redisClient.argc和redisCommand.arity驗證操作命令引數個數是否正確
- 根據redisClient.authenticated驗證客戶端是否通過身份驗證
- 呼叫命令函式
- 將處理結果儲存至redisClient.buf(輸出緩衝區)
- 進行後續處理(慢查詢日誌 & redisCommand計數+1 & AOF & 同步)
- 將處理結果傳送給Redis client
Redis事件
檔案事件
套接字:socket
IO多路複用程式:Redis底層使用epoll
檔案事件分派器:事件執行者
事件處理器:連線應答處理器、命令請求處理器和命令回覆處理器
檔案事件處理流程
- 套接字準備好執行連線應答、寫入、讀取、關閉等操作時,會產生一個檔案事件
- IO多路複用程式監聽多個套接字,把產生事件的套接字放在一個佇列裡
- IO多路複用程式有序推送套接字給檔案事件分派器
- 檔案事件分派器根據事件型別,選擇事件處理器呼叫函式
時間事件serverCron函式
- 更新時間戳redisServer.unixtime和redisServer.mstime
- 更新LRU時鐘redisServer.lruclock
- 更新Redis server每秒執行命令次數redisServer.ops_sec_samples
- 更新Redis server記憶體峰值記錄redisServer.stat_peak_memory
- 處理SIGTERM訊號,接收到SIGTERM訊號把redisServer.shutdown_asap設定為1
- 檢查客戶端資源
- 檢查資料庫資源
- 檢查持久化操作執行狀態
- 如果開啟AOF持久化,將AOF緩衝區內容寫入AOF
- redisServer.cronloops+1
Redis持久化
RDB
觸發條件
手動觸發(save命令和bgsave命令)
自動觸發(save m n)(主從複製)(shutdown命令)
- save命令會阻塞Redis server,直至RDB檔案建立完成(基本棄用)
- bgsave命令會建立子程序來生成RDB檔案,建立子程序過程中父程序阻塞
- save m n指m秒發生n次操作,會自動觸發bgsave;根據redisServer.dirty和redisServer.lastsave屬性進行判斷,由時間事件serverCron負責觸發
- 主從複製場景下,從節點執行全量複製,主節點會執行bgsave命令,將RDB檔案傳送給從節點
- shutdown命令會自動生成RDB檔案,然後再結束程序
AOF
開啟AOF持久化:appendonly yes
AOF執行流程分為三步:命令追加+檔案寫入+檔案重寫
- 命令追加:將執行成功的修改操作寫入redisServer.aof_buf
- 檔案寫入:根據策略將緩衝區資料寫入磁碟
always:緩衝區有資料就寫入磁碟
no:等待作業系統通過write命令呼叫,通常為30秒一次
everysec:等待作業系統通過fsync命令呼叫,每秒一次(預設策略) - 檔案重寫:將Redis內的資料轉換成命令,寫入新的AOF檔案
檔案重寫觸發條件
手動觸發(bgrewriteaof命令)
自動觸發(AOF檔案超過64MB 並且 新AOF檔案大於原AOF檔案)
為什麼AOF最多可能丟失2秒的資料
Redis會記錄上次fsync命令成功的時間,如果不到2秒,不觸發fsync;如果超過2秒,則阻塞進行fsync。因此在觸發fsync之前突然宕機可能會丟失2秒資料。
Redis4.0混合持久化
開啟混合持久化:aof-use-rdb-preamble yes
Redis5.0預設開啟混合持久化
混合持久化執行流程
- 手動/自動觸發bgrewriteaof命令
- 主程序fork子程序,fork過程中主程序阻塞
- 子程序將全量資料以RDB格式寫入AOF檔案,主程序將操作命令寫入AOF緩衝區和AOF重寫緩衝區
- 子程序通知主程序,主程序將AOF重寫緩衝區資料以AOF格式寫入AOF檔案
- 主程序將新AOF檔案替換原AOF檔案(AOF檔案前半段是RDB格式資料,後半段是AOF格式命令)
Redis高可用
主從複製模式
優點
讀寫分離:主節點寫,從節點讀
故障恢復:主節點宕機,將從節點升級為主節點
缺點
主/從節點故障恢復需要人工干預
寫操作無法負載均衡
連線建立階段
- 從節點masterhost記錄主節點ip,masterport記錄主節點port
- 從節點發送slaveof命令給主節點,主節點返回OK
- 從節點與主節點建立socket連線,從節點socket用於接收RDB檔案以及其他命令,主節點socket儲存在redisServer.clients
- 從節點發送ping命令給主節點,主節點返回pong
- 從節點發送auth命令給主節點進行身份驗證
- 從節點發送埠號給主節點,主節點後續會將資料傳送至該埠
資料同步階段
- 從節點發送psync命令給主節點,主節點判斷全量複製或者部分複製
命令傳播階段
- 主節點操作命令執行成功後,傳送操作命令給從節點
- 主從節點心跳機制和REPLCONF ACK機制
repl-disable-tcp-nodelay yes:合併操作命令,40ms傳送一次
repl-disable-tcp-nodelay no:每次操作命令傳送一次
心跳機制:主節點每10秒傳送ping命令給從節點
REPLCONF ACK機制:從節點每秒傳送REPLCONF ACK命令給主節點,維護offset屬性
哨兵模式
優點
自動實現主節點故障恢復
缺點
從節點故障恢復仍需要人工干預
寫操作無法負載均衡
實現原理
每個哨兵節點維護了三個定時任務
- 向主節點發送info命令獲取最新主從結構
- 通過釋出訂閱獲取其他哨兵節點資訊
- 向其他節點發送ping命令進行心跳檢測
心跳檢測過程中,主節點沒有回覆,哨兵節點將主節點主觀下線,並通過sentinel is-master-down-by-addr命令詢問其他哨兵節點主節點狀態,如果判斷主觀下線的哨兵節點到達一定數量,則對該主節點進行客觀下線,開始進行選舉。
選擇領導者哨兵節點演算法:Raft演算法,先到先得。
選擇主節點演算法:
- 過濾掉不健康的從節點
- 選擇優先順序最高的從節點
- 若優先順序無法區分,選擇offset最大的從節點
- 若offset無法區分,選擇runid最小的從節點
叢集模式
優點
解決寫操作無法負載均衡的問題
實現原理
叢集模式將16384個槽分佈在各個主節點上,資料通過資料分割槽方案落在各個槽中。
資料分割槽方案
- 雜湊取餘分割槽
- 一致性雜湊分割槽
- 虛擬節點一致性雜湊分割槽
叢集成員
- 資料節點(主節點和從節點)
- 哨兵節點
主節點讀寫,從節點只負責備份資料
每個節點維護2個埠 - 普通埠:使用者提供服務
- 叢集埠(普通埠+10000):用於叢集各個節點通訊
增加節點
- 啟動節點
- 節點握手
- 遷移槽
- 指定主從關係
減少節點
- 遷移槽
- 節點下線
故障轉移
哨兵節點識別主節點客觀下線,由其他主節點投票選一個從節點成為主節點