一個Redis服務器實例在單機運行時可以添加多個數據庫來保存鍵值對,Redis在實現中通過一個redisDb結構體來描述數據庫,該結構體中有一個字典類型的字段來保存數據庫中所有的鍵值對,redisServer結構體來描述服務器實例,該結構體中有一個dbnum字段來保存數據庫數量,一個redisDb數組字段來保存服務器中的所有數據庫。
struct redisServer{
...
redisDb *db;
int dbnum;
...
};
typedef struct redisDb{
...
dict *dict;
...
} redisDb;
當客戶端調用SET、RPUSH等命令之後會把命令中內容作爲鍵值對添加到redisDb的dict字典字段中,調用DEL等刪除操作時會刪除dict字段中相應的數據。
客戶端可以通過調用select <數據庫編號> 來切換數據庫。在服務器內部,通過redisClient結構體保存客戶端狀態,該結構體中有一個db字段來記錄目標數據庫,切換數據庫之後,redisClient結構體中的db屬性指向目標的數據庫結實例。typedef struct redisClient{
...
redisDb *db;
...
}redisClient;
鍵的過期時間:
redis提供了四種命令設置鍵的過期時間:
- EXPIRE <key> <ttl> 設置key的生存時間爲ttl秒。
- PEXPIRE <key> <ttl> 設置key的生存時間爲ttl毫秒。
- EXPIREAT <key> <timestamp> 設置key的過期時間爲timestamp所指定秒數時間戳。
- PEXPIREAT <key> <timestamp>設置key的過期時間爲timestamp所指定的毫秒數時間戳。
在redisDb結構體中有一個expires字段,用來保存數據庫中所有鍵的過期時間,這是一個字典字段,鍵是一個指向鍵對象的指針,值是一個長整型數保存過期時間(精確的UNIX毫秒時間戳),服務器通過該字段來檢查鍵是否已過期。
此外可以通過TTL<key>和PTTL<key>查詢鍵的剩餘生存時間,通過PERSIST <key>移除鍵的過期時間。
當鍵過期時有三種刪除策略:
- 定時刪除,創建一個定時器,在定時任務中刪除過期的鍵。這種策略節約內存但是耗CPU資源。
- 惰性刪除,不主動刪除過期鍵,每次訪問該鍵時如果該鍵過期刪除該鍵。這種策略對CPU消耗比較小,但是浪費內存,而且如果過期的鍵一致不被訪問的話有內存泄露的風險。
- 定期刪除,每隔一段時間,程序對數據庫做一次檢查,刪除裏面的過期鍵。但是不全部刪除,刪除多少由算法定,可以根據刪除鍵花費的時間或者一次刪除鍵的數量來控制。這種策略是1和2的折中,在內存和CPU上平衡,事實上很多性能調優的場景都是在內存和CPU之間犧牲哪一個而糾結。
關於持久化和複製功能對過期鍵的處理:
- 在生成RDB文件時會過濾掉過期的鍵。
- 在生成AOF文件時如果鍵過期會增加一條DEL命令到AOF文件中,AOF重寫時也會忽略過期的鍵。
- 當主服務器刪除一個過期的鍵後它會向所有從服務器發送一條DEL命令,顯示地刪除過期鍵。從服務器不會主動刪除過期的鍵,而是由主服務器統一通知,這種統一、中心化的過期鍵刪除策略可以保證主從服務器數據的一致性