1. 程式人生 > 其它 >Redis設計與實現 第 9 章 資料庫

Redis設計與實現 第 9 章 資料庫

第 9 章 資料庫

9.1 伺服器中的資料庫

redis.h/redisServer 結構的 db 陣列中,每個元素都是 redis.h/redisDb 結構,代表一個數據庫

初始化伺服器時會根據伺服器狀態的 dbnum 屬性來決定建立多少個數據庫

dbnum 由伺服器配置的 database 選項決定,預設為 16,所以會預設建立 16 個數據庫

9.2 切換資料庫

Redis 客戶端的目標資料庫為 0 號資料庫,可以通過 SELECT 命令來切換目標資料庫

127.0.0.1:6379> set 32 "hello you"
OK
127.0.0.1:6379> get 32
"hello you"
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> get 32
(nil)
127.0.0.1:6379[2]> set 32 "you hello"
OK
127.0.0.1:6379[2]> get 32
"you hello"

在伺服器內部,客戶端狀態的 redisClient 結構的 db 屬性記錄了客戶端當前的目標資料,是一個指向 redisDb 結構的指標

redisClint.db 指標指向 redisServer.db 陣列的一個元素,被指向的元素則是客戶端的目標資料庫

9.3 資料庫鍵空間

redis 是鍵值對資料庫伺服器,每個資料庫都是 redis.h/redisDb 結構表示,dict 字典儲存了資料庫中的鍵值對,這個字典為鍵空間

鍵空間與資料庫是直接對應的

  • 鍵空間的鍵是資料庫的鍵,也是一個字串物件
  • 鍵空間的值是資料庫的值,每個值可以是任意一種 Redis 物件

所有針對資料庫的操作,實際上都是通過對鍵空間字典進行操作實現的

新增新鍵

新增新鍵值對到資料庫,實際上就是將新鍵值對新增到鍵空間中,鍵為字串物件,值為任意一種 Redis 物件

刪除鍵、更新鍵、對鍵取值

同理新增新鍵

其他命令

FLUSHDB、RANDOMKEY、DESIZE 同樣也是通過對鍵空間進行操作的

9.3.6

不僅只有指定的讀寫操作,還有一些額外的維護操作,包括

  • 伺服器會根據鍵是否存在來更新鍵空間命中次數和不命中次數,使用 INFO status 命令的 keyspace_hits 和 keyspace_misses 屬性檢視

  • 伺服器會更新鍵的 lru (最後一次使用)時間,可以計算鍵的閒置時間, OBJECT idletime key 檢視

  • 讀取一個鍵時發現已經過期了,則會先刪除再進行餘下的操作

  • 如果客戶端使用 watch 命令監視了某個鍵,則伺服器修改後,會將其標記為髒,從而讓事務程式注意到鍵已經修改

  • 每修改一個鍵後,髒鍵計數器的值 + 1,會觸發伺服器的持久化以及複製操作

  • 如果伺服器開啟資料庫通知功能,在對鍵進行修改之後,伺服器會按配置傳送相應的資料庫通知

9.4 設定鍵的生存時間或過期時間

9.4.1 設定過期時間

所有命令底層還是由 PEXPIRE 命令轉換而來的

9.4.2 儲存過期時間

redisDb 結構的 expires 字典儲存了資料庫中所有鍵的過期時間,即過期字典:

  • 鍵為指標,指向鍵空間的某個鍵物件

  • 值為 long long 型別,儲存了鍵指向的鍵空間鍵物件的過期時間,毫秒精度的 UNIX 時間戳

鍵空間和過期字典的鍵都指向同一個,此處為明顯展示而列舉兩個

9.4.3 移除過期命令

PERSTST 命令移除鍵的過期時間:

在過期字典中查詢給定的鍵,並解除鍵和值在過期字典中的關聯

9.4.4 計算並返回剩餘生存時間

TTL 秒為單位,PTTL 毫秒為單位,通過計算鍵的過期時間與當前時間之差

9.4.5 過期鍵的判定

  1. 檢查給定鍵是否存在於過期字典;存在則取得過期時間
  2. 檢查當前 UNIX 時間戳是否大於鍵的過期時間;是則鍵已經過期,否則未過期

或者使用 TTL 或 PTTL 命令,但實際中是使用 is_expired 函式

9.5 過期鍵刪除策略

  • 定時刪除:主動
    • 設定過期時間的同時建立定時器,過期時間到達立即刪除
  • 惰性刪除:被動
    • 當從鍵空間獲取鍵時,檢查是否過期,過期則刪除,沒有則返回
  • 定期刪除:主動
    • 每隔一段時間,對資料庫檢查,刪除多少,檢查多少資料庫則由演算法決定

9.5.1 定時刪除

  • 記憶體友好

  • CPU 最不友好

  • 需要使用 Redis 的時間事件,時間事件實現方式為無序連結串列,則查詢一個事件的時間複雜度為 O(N)

9.5.2 惰性刪除

  • 對 CPU 最友好

  • 對記憶體最不友好

  • 浪費大量記憶體

9.5.3 定期刪除

每隔一段時間執行一次刪除過期鍵操作,並通過限制刪除操作執行的時長和頻率來減少刪除操作對 CPU 時間的影響,有效減少了過期鍵的記憶體浪費

時長和頻率難以確定:

  • 執行頻繁、時間長,會退化成定時刪除

  • 執行少、時間短,退化為惰性刪除

9.6 Redis 的過期鍵刪除策略

9.6.1 惰性刪除策略的實現

由 db.c/expireIfNeeded 函式實現,所有讀寫資料庫的 Redis 命令在執行之前都會呼叫此函式對輸入鍵進行檢查

  • 過期,此函式將鍵從資料庫中刪除
  • 未過期,此函式不做動作

命令執行時也需要按照兩種情況執行

9.6.2 定期刪除策略的實現

由 redis.c/activeExpireCycle 函式實現,Redis 週期性操作 redis.c/serverCron 函式執行時,activeExpireCycle 函式會呼叫,在規定時間內,分多次遍歷各個資料庫,從 expires 字典中隨機檢查一部分鍵的過期時間,並刪除過期鍵

activeExpireCycle:

  1. 執行時,從一定數量的資料庫取出一定數量的隨機鍵進行檢查,刪除過期鍵
  2. current_db 會記錄當前函式檢查的進度,並在下一次呼叫此函式時按上一次進度進行處理
  3. 函式不斷執行,所有資料庫都會被檢查一遍,current_db 置為 0 ,開始新一輪的檢查

9.7 AOF、ROB 和複製功能對過期鍵的處理

9.7.1 生成 RDB 檔案

在執行 save 命令或者 bgsave 命令建立新的 RDB 檔案時,對鍵會進行檢查,已經過期的鍵不會被儲存到新建立的 RDB 檔案中

9.7.2 載入 RDB 檔案

對 RDB 檔案進行載入:

  • 以主伺服器模式執行,在載入時會對檔案中儲存的鍵進行檢查,未過期的鍵會被載入到資料庫中,已過期的會被忽略

  • 以從伺服器模式執行,檔案中所有鍵都會被載入資料庫,但是主從伺服器進行資料同步時,從伺服器的資料庫就會被清空

9.7.3 AOF 檔案寫入

伺服器以 AOF 持久化模式執行時,如果某個鍵過期,但沒有被惰性刪除或者定期刪除,AOF 檔案不會因這個過期鍵產生任何影響

當過期鍵被刪除後,會向 AOF 檔案追加一條 DEL 命令顯示記錄該鍵已被刪除

9.7.4 AOF 重寫

類似生成 RDB 檔案,對鍵檢查,已過期的不會被儲存

9.7.5 複製

當伺服器在複製模式下,從伺服器的過期刪除動作由主伺服器控制:

  • 主伺服器刪除一個過期鍵,顯示向所有從伺服器傳送一個 DEL 命令,告知從伺服器刪除這個過期鍵
  • 從伺服器在執行客戶端傳送的讀命令時,遇到過期鍵也不會將過期鍵刪除,當成未過期鍵處理
  • 從伺服器只有在接到豬伺服器發來的 DEL 命令之後才會刪除過期鍵

通過主伺服器控制從伺服器統一刪除過期鍵,來保證主從伺服器的一致性

9.8 資料庫通知

讓客戶端通過訂閱給定的頻道或者模式,來獲知資料庫中鍵的變化,以及命令的執行情況

  • 鍵空間通知:
    • 某個鍵執行了什麼命令
  • 鍵事件通知
    • 某個命令被什麼鍵執行了

notify-keyspace-events 決定傳送通知的型別

  • 傳送所有型別的鍵空間通知和鍵事件通知:AKE

  • 傳送所有型別的鍵空間通知:AK

  • 傳送所有型別的鍵事件通知:AE

  • 傳送和字串鍵相關的鍵空間通知:K$

  • 傳送和列表鍵相關的鍵事件通知:EL

9.8.1 傳送通知

傳送資料庫通知功能由 notify.c/notifyKeyspaceEvent 實現:

void notifyKeyspaceEvent(int type, char *event, robj  *key, int dbid);

type 為當前想要傳送的通知的型別,根據這個值來判斷通知是否就是伺服器配置 notify-keyspace-event 選項所選定的通知型別來決定是否傳送通知

event 事件名稱

keys 產生事件的鍵

dbid 產生事件的資料庫號

根據這些引數來構建事件通知的內容、以及接受通知的頻道名

REDIS_NOTIFY_SET:集合鍵通知

REDIS_NOTIFY_GENERIC:通用型別通知

notifyKeyspaceEvent:

  1. server.notify_keyspace_events 為 notify-keyspace-events 選項設定的值,如果給定的通知型別 type 不是伺服器允許傳送的通知型別,函式直接返回
  2. 是伺服器允許傳送的通知型別,再檢測是否允許傳送鍵空間通知,允許則構建併發送事件通知
  3. 最後再檢測是否允許傳送鍵事件通知,允許中午構建併發送事件通知

pubsubPublishMessage 是PUBLISH 命令的實現函式