1、說明
生存時間: (Time To Live, TTL),經過指定的秒/毫秒之后,服務器自動刪除TTL為0的key
過期時間: (expire time),時間戳,表示一個具體時間點,到這個時間點后,服務器會刪除key
2、指令
設置生存時間:
EXPIRE key ttl #設置ttl,s
PEXPIRE key ttl #設置ttl,ms
設置過期時間:
EXPIREAT key timestamp #設置expire time,s
PEXPIREAT key timestamp #設置exprie time,ms
以上四種命令雖然各有不同,但是其底層都是使用 PEXPIREAT 實現的
2.1、刪除和更新
PERSIST key #移除生存時間
DLE 命令可以刪除key,也會刪除其生存時間
SET 和 GETSET 命令也可以覆寫生存時間
3、過期時間的保存
redisDb結果的expires字典中保存了數據庫中的所有key的過期時間,redisDb的聲明如下:
/* Redis database representation. There are multiple databases identified
* by integers from 0 (the default database) up to the max configured
* database. The database number is the 'id' field in the structure. */
//每個數據庫都是一個redisDb,id為數據庫編號
typedef struct redisDb {
dict *dict; //鍵空間,保存了數據中所有鍵值對
dict *expires; //過期字典,保存了數據庫中所有鍵的過期時間
dict *blocking_keys;
dict *ready_keys;
dict *watched_keys;
struct evictionPoolEntry *eviction_pool;
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
expires 的鍵是一個指針,指向某個鍵對象,值是一個 long long 類型整數,保存了過期時間,是一個毫秒精度的UNIX時間戳
可見,過期時間的保存是使用key來作為關聯的,所以操作用,修改key均可以修改過期時間,而只修改key的value,是不是改變其過期時間的
4、計算剩余生存時間
TTL key #計算key的剩余生存時間,s
PTTL key #計算key的剩余生存時間,ms
底層的處理方式也很簡單,獲取key的生存時間戳,減去當前時間戳即可
如果鍵不存在,則返回-2
如果鍵沒有設置過期時間,則返回-1
同樣可以使用此方法判斷key是否過期,TTL/PTTL 結果小於0,則表示過去,大於0,則表示未過期
5、過期鍵的刪除策略
策略有三種:
- 定時刪除:設置鍵的過期時間的同時,設置一個定時器,來刪除鍵
- 惰性刪除:放任過期鍵不管,每次從鍵空間取值時,檢查是否過期,以決定是否刪除;
- 定期刪除:每隔一段時間,進行一次數據庫檢查,刪除里面的過期鍵,至於,要刪除多少過期鍵,以及要檢查多少數據庫,由算法決定;
5.1、定時刪除
對內存友好,保證鍵盡可能在失效時立即刪除;
對CPU不友好,過期鍵比較多時,刪除可能會占用一部分CPU時間,影響,服務器的響應時間和吞吐量;
Redis 中創建定時器需要使用Redis的時間事件(實現方式是無序鏈表,查找的時間復雜度為O(N)),大量事件處理時效率太低
5.2、惰性刪除
對CPU最友好,只有在取鍵的值時才處理;
對內存不友好,過期鍵如果長時間沒有被操作,則過期鍵仍然會占用內存,大量的無用數據占用內存,可以認為是一種內存泄漏;
Redis 的惰性刪除策略由 db.c/expireIfNeeded 函數實現,所有讀寫Redis的命令在執行之前都會調用expireIfNeeded 函數
5.3、定期刪除
定期刪除是定時刪除和惰性刪除的一種折中方式,其難點是確定刪除操作執行的時長和頻率
Redis 的定期刪除策略由 db.c/activeExpireCycle 函數實現,每當 Reids 周期性操作 redis.c/serverCron 函數執行時,activeExpireCycle 函數就會被調用,在規定時間內,多次遍歷服務器中的各個數據庫,隨機檢查一部分鍵的過期時間,刪除過期鍵