1. 程式人生 > 資料庫 >Redis Redis原理

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 Java物件形式展示Redis資料結構

Redis執行機制

Redis server初始化
  1. 例項化redisServer物件
  2. 根據使用者指定引數和配置檔案初始化redisServer物件屬性
  3. 初始化redisServer物件其他屬性
  4. 建立常量:“OK"字串和"1”-"10000"字串
  5. 為serverCron建立時間事件
  6. 載入持久化檔案(AOF或RDB)
  7. 開始執行時間事件

第六步載入持久化檔案流程圖

Created with Raphaël 2.2.0redis啟動是否開啟aof?是否存在aof檔案載入aof檔案啟動成功啟動失敗是否存在rdb檔案載入rdb檔案yesno
yesyesnoyesyesno
Redis client傳送請求
  1. 將操作命令根據RESP協議進行封裝
  2. 通過套接字傳送給Redis server
Redis server接收請求
  1. 通過套接字接收請求內容,儲存至redisClient.querybuf(輸入緩衝區)
  2. 解析請求內容,儲存至redisClient.argv(操作命令與操作命令引數陣列)redisClient.argc(argv長度)
  3. 呼叫操作命令執行器
Redis server處理請求
  1. 根據操作命令去命令表查詢操作命令對應的命令函式(redisCommand)
  2. 根據redisClient.argc和redisCommand.arity驗證操作命令引數個數是否正確
  3. 根據redisClient.authenticated驗證客戶端是否通過身份驗證
  4. 呼叫命令函式
  5. 將處理結果儲存至redisClient.buf(輸出緩衝區)
  6. 進行後續處理(慢查詢日誌 & redisCommand計數+1 & AOF & 同步)
  7. 將處理結果傳送給Redis client

Redis事件

檔案事件

套接字:socket
IO多路複用程式:Redis底層使用epoll
檔案事件分派器:事件執行者
事件處理器:連線應答處理器、命令請求處理器和命令回覆處理器
檔案事件處理流程

  1. 套接字準備好執行連線應答、寫入、讀取、關閉等操作時,會產生一個檔案事件
  2. IO多路複用程式監聽多個套接字,把產生事件的套接字放在一個佇列裡
  3. IO多路複用程式有序推送套接字給檔案事件分派器
  4. 檔案事件分派器根據事件型別,選擇事件處理器呼叫函式
時間事件serverCron函式
  1. 更新時間戳redisServer.unixtime和redisServer.mstime
  2. 更新LRU時鐘redisServer.lruclock
  3. 更新Redis server每秒執行命令次數redisServer.ops_sec_samples
  4. 更新Redis server記憶體峰值記錄redisServer.stat_peak_memory
  5. 處理SIGTERM訊號,接收到SIGTERM訊號把redisServer.shutdown_asap設定為1
  6. 檢查客戶端資源
  7. 檢查資料庫資源
  8. 檢查持久化操作執行狀態
  9. 如果開啟AOF持久化,將AOF緩衝區內容寫入AOF
  10. 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預設開啟混合持久化
混合持久化執行流程

  1. 手動/自動觸發bgrewriteaof命令
  2. 主程序fork子程序,fork過程中主程序阻塞
  3. 子程序將全量資料以RDB格式寫入AOF檔案,主程序將操作命令寫入AOF緩衝區和AOF重寫緩衝區
  4. 子程序通知主程序,主程序將AOF重寫緩衝區資料以AOF格式寫入AOF檔案
  5. 主程序將新AOF檔案替換原AOF檔案(AOF檔案前半段是RDB格式資料,後半段是AOF格式命令)

Redis高可用

主從複製模式

優點
讀寫分離:主節點寫,從節點讀
故障恢復:主節點宕機,將從節點升級為主節點
缺點
主/從節點故障恢復需要人工干預
寫操作無法負載均衡
連線建立階段

  1. 從節點masterhost記錄主節點ip,masterport記錄主節點port
  2. 從節點發送slaveof命令給主節點,主節點返回OK
  3. 從節點與主節點建立socket連線,從節點socket用於接收RDB檔案以及其他命令,主節點socket儲存在redisServer.clients
  4. 從節點發送ping命令給主節點,主節點返回pong
  5. 從節點發送auth命令給主節點進行身份驗證
  6. 從節點發送埠號給主節點,主節點後續會將資料傳送至該埠

資料同步階段

  • 從節點發送psync命令給主節點,主節點判斷全量複製或者部分複製

命令傳播階段

  • 主節點操作命令執行成功後,傳送操作命令給從節點
  • 主從節點心跳機制和REPLCONF ACK機制

repl-disable-tcp-nodelay yes:合併操作命令,40ms傳送一次
repl-disable-tcp-nodelay no:每次操作命令傳送一次
心跳機制:主節點每10秒傳送ping命令給從節點
REPLCONF ACK機制:從節點每秒傳送REPLCONF ACK命令給主節點,維護offset屬性

哨兵模式

優點
自動實現主節點故障恢復
缺點
從節點故障恢復仍需要人工干預
寫操作無法負載均衡
實現原理
每個哨兵節點維護了三個定時任務

  1. 向主節點發送info命令獲取最新主從結構
  2. 通過釋出訂閱獲取其他哨兵節點資訊
  3. 向其他節點發送ping命令進行心跳檢測

心跳檢測過程中,主節點沒有回覆,哨兵節點將主節點主觀下線,並通過sentinel is-master-down-by-addr命令詢問其他哨兵節點主節點狀態,如果判斷主觀下線的哨兵節點到達一定數量,則對該主節點進行客觀下線,開始進行選舉。
選擇領導者哨兵節點演算法:Raft演算法,先到先得。
選擇主節點演算法:

  1. 過濾掉不健康的從節點
  2. 選擇優先順序最高的從節點
  3. 若優先順序無法區分,選擇offset最大的從節點
  4. 若offset無法區分,選擇runid最小的從節點
叢集模式

優點
解決寫操作無法負載均衡的問題
實現原理
叢集模式將16384個槽分佈在各個主節點上,資料通過資料分割槽方案落在各個槽中。
資料分割槽方案

  • 雜湊取餘分割槽
  • 一致性雜湊分割槽
  • 虛擬節點一致性雜湊分割槽

叢集成員

  • 資料節點(主節點和從節點)
  • 哨兵節點
    主節點讀寫,從節點只負責備份資料
    每個節點維護2個埠
  • 普通埠:使用者提供服務
  • 叢集埠(普通埠+10000):用於叢集各個節點通訊

增加節點

  1. 啟動節點
  2. 節點握手
  3. 遷移槽
  4. 指定主從關係

減少節點

  1. 遷移槽
  2. 節點下線

故障轉移
哨兵節點識別主節點客觀下線,由其他主節點投票選一個從節點成為主節點