服務器中的數據庫
Redis 服務器將絕大部分的信息都保存在 server.h/redisServer
。redis 的數據是保存在 redisServer
中的 redisDb
結構中。
struct redisServer {
// ...
redisDb *db; // 數據庫列表
// ...
int dbnum; // 數據庫數量
// ...
}
db
中每個redisDb結構代表一個數據庫。- 在初始化服務器時,程序會根據服務器狀態的
dbnum
屬性來決定應該創建多少個數據庫。 dbnum
屬性的值由服務器配置的database
選項決定,默認情況下,該選項的值為16,所以Redis服務器默認會創建16個數據庫。
數據庫鍵空間
Redis 是一個鍵值對數據庫服務器,服務器中的每個數據庫都由一個 server.h/redisDb
結構表示.
其中,redisDb
的 dict
字典屬性保存了數據庫中的所有鍵值對,我們將這個字典稱為鍵空間(key space):
typedef struct redisDb {
dict *dict;
// ...
} redisDb;
dict 中的數據跟我們平常操作的鍵值對是一一對應的:
- dict 的 key 就是數據庫中的 key,字符串類型
- dict 的 值 就是數據庫中的 值,這個值可以是
string
、hash
、zset
、set
、list
中的任何一種
示例
如果我們在數據庫中,執行以下命令:
redis > SET str_key str_value
OK
redis > RPUSH list_key a b c
(integer) 3
新添加的兩個 key 的結構如下圖所示:
從上面的示例圖可以很清晰地知道 Redis 數據是如何組織的,增刪改查也就是對 dict 的操作而已,此處就不詳細說了。
Key 的過期時間
1. 數據結構
redisDb 中的 expires
屬性保存了所有 key
的過期時間,我們姑且就稱它為過期字典吧。
- 過期字典中的鍵,是一個指針,指向了真實數據的
key
,不會浪費空間多保存一次 - 過期字典中的值,存的是具體的過期時間點,精確到毫秒的時間戳
typedef struct redisDb {
// ...
// 保存了所有 key 的過期時間
dict *expires;
// ...
} redisDb;
命令TTL
、PTTL
都是去查這個過期字典的過期時間,然后減去當前時間,得到的就是剩余的時間啦。
2. 過期 key 的刪除策略
一個 key 過期時間到了之后,是如何進行刪除的呢?Redis 使用了一下兩種策略:惰性刪除、定期刪除
惰性刪除
惰性刪除策略指的是:key 在過期之后,沒有立即刪除,而是在讀寫 key 的時候,才對過期的 key 進行刪除。
代碼實現在 db.c/expireIfNeeded
方法中。所有 key 的讀寫之前,都會先調用 expireIfNeeded
對 key 進行檢查,如果已過期,則刪除。
定期刪除
定期刪除策略指的是:Redis 每隔一段時間,隨機從數據庫中取出一定量的 key 進行檢查,如果已過期,則進行刪除。
代碼實現在 expire.c/activeExpireCycle
方法中。
刪除的步驟:
- 從過期字典中隨機 20 個 key
- 刪除這 20 個 key 中已經過期的 key
- 如果過期的 key 比率超過 1/4,那就重復步驟 1
為什么只是隨機挑 一些 key 呢?因為如果把所有 key 都遍歷一遍,那這個性能肯定是不能接受的!所以還需要配合 惰性刪除。
本文的分析沒有特殊說明都是基於 Redis 6.0 版本源碼
redis 6.0 源碼:https://github.com/redis/redis/tree/6.0