設置鍵的生存時間或過期時間
通過EXPIRE 命令或者PEXPIRE 命令,客戶端可以以秒或者毫秒精度為數據庫中的某個鍵設置生存時間( Time To Live , TTL) ,在經過指定的秒數或者毫秒數之后,服務器就會自動刪除生存時間為0的鍵:
redis> SET key value OK redis> EXP 工RE key 5 (integer) 1 redis> GET key // 5 秒之內 "value" redis> GET key // 5 秒之后 (nil)
注意
SETEX 命令可以在設直一個字符串鍵的同時為鍵設直過期時間,因為這個命令是一個類型限定的命令(只能用於字符串鍵),但SETEX 命令設置過期時間的原理和EXPIRE命令設置過期時間的原理是完全一樣的。
與EXPlRE 命令和PEXPIRE 命令類似,客戶端可以通過EXPlREAT 命令或PEXPlREAT命令,以秒或者毫秒精度給數據庫中的某個鍵設置過期時間(expire time)。
過期時間是一個UNIX時間戳,當鍵的過期時間來臨時,服務器就會自動從數據庫中刪除這個鍵:
redis> SET key value OK redis> EXPIREAT key 1377257300 (integer) 1 redis> TIME 1)"1377257296" 2)"296543" redis> GET key // 1377257300 之前 "value" redis> TIME 1)"1377257303" 2)"230656 " redis> GET key // 1377257300 之后 (nil)
TTL 命令和PTTL 命令接受一個帶有生存時間或者過期時間的鍵,返回這個鍵的剩余生存時間,也就是,返回距離這個鍵被服務器自動刪除還有多長時間:
redis> SET key va1ue OK redis> EXPlRE key 1000 (integer) 1 redis> TTL key (integer) 997 redis> SET another_key another_value OK redis> TIME 1) "1377333070 " 2) "761687 " redis> EXPlREAT another key 1377333100 (integer ) 1 redis> TTL another key (integer) 10
設置過期時間
Redis 有四個不同的命令可以用於設置鍵的生存時間(鍵可以存在多久)或過期時間(鍵什么時候會被刪除) :
EXPlRE <key> <ttl> 命令用於將鍵key 的生存時間設置為ttl 秒。
PEXPIRE <key> <ttl> 命令用於將鍵key 的生存時間設置為ttl 毫秒。
EXPIREAT <key> < timestamp> 命令用於將鍵key 的過期時間設置為timestamp所指定的秒數時間戳。
PEXPIREAT <key> < timestamp > 命令用於將鍵key 的過期時間設置為timestamp所指定的毫秒數時間戳。
雖然有多種不同單位和不同形式的設置命令,但實際上EXPlRE、PEXPlRE 、EXPIREAT三個命令都是使用PEXPlREAT 命令來實現的:無論客戶端執行的是以上四個命令中的哪一個, 經過轉換之后,最終的執行效果都和執行PEXPlREAT 命令一樣。
首先, EXPIRE 命令可以轉換成PEXPlRE 命令:
def EXPIRE(key,ttl_in_sec): #將TTL 從秒轉換成毫秒 ttl_in_ms = sec_to_ms(ttl_in_sec ) PEXPlRE(key, ttl_in_ms)
接着,PEXPlRE 命令又可以轉換成PEXPlREAT 命令:
def PEXPIRE(key,ttl_in_ms) : #獲取以毫秒計算的當前UNIX 時間戳 now_ms = get_current_unix_timestamp_in_ms() #當前時間加上TTL,得出毫秒格式的鍵過期時間 PEXPlREAT(key,now_ms+ttl_in_ms)
並且,EXPlREAT命令也可以轉換成PEXPlREAT命令:
def EXPIREAT (key,expire_time_in_ sec): #將過期時間從秒轉換為毫秒 expire_time_ in_ms = sec_to_ms (expire_time_in_sec) PEXPlREAT(key, expire_time_in_ms)
保存過期時間
redisDB結構的expires字典保存了數據庫中所有鍵的過期時間,我們稱這個字典為過期字典:
過期字典的鍵是一個指針,這個指針指向鍵空間中的某個鍵對象( 也即是某個數據庫鍵)。
過期字典的值是一個long long 類型的整數,這個整數保存了鍵所指向的數據庫鍵的過期時間:一個毫秒精度的UNIX 時間戳。
typedef struct redisDb { //... // 過期字典,保存着鍵的過期時間 dict *expires ; //... } redisDb;
如下圖展示了一個帶有過期字典的數據庫例子,在這個例子中,鍵空間保存了數據庫中的所有鍵值對,而過期字典則保存了數據庫鍵的過期時間。
過期字典將新增一個鍵值對, 其中鍵為message 鍵對象,而值則為1391234400000(2014年2月1日零時) ,如圖9-13 所示。
圖9-12 中的過期字典保存了兩個鍵值對:
第一個鍵值對的鍵為alphabet 鍵對象,值為1385877600000 ,這表示數據庫鍵alphabet 的過期時間為1385877600000 (2013年12月1日零時)。
第二個鍵值對的鍵為book鍵對象,值為1388556000000,這表示數據庫鍵book的過期時間為1388556000000(2014年1月1日零時)。
當客戶端執行PEXPIREAT 命令(或者其他三個會轉換成PEXPIREAT命令的命令)為一個數據庫鍵設置過期時間時,服務器會在數據庫的過期字典中關聯給定的數據庫鍵和過期時間。
舉個例子, 如果數據庫當前的狀態如圖9-12 所示, 那么在服務器執行以下命令之后:
redis> PEXPIREAT message 1 391234400000 (integer) 1
過期字典將新增一個鍵值對,其中鍵為message鍵對象,而值則為1391234400000(2014年2月1日零時),如圖9-13 所示。
以下是PEXPlREAT 命令的偽代碼定義:
def PEXPIREAT(key,expire_time_in_ms) : #如果給定的鍵不存在於鍵空間,那么不能設置過期時間 if key not in redisDB.dict : return 0 #在過期字典中關聯鍵和過期時間 redisDB.expires[key]=expire_tirne_in_ms #過期時間設置成功 return 1
移除過期時間
PERSIST命令可以移除一個鍵的過期時間:
redis>PEXPlREAT message 1391234400000 (integer) 1 redis>TTL message (integer) 13893281 redis> PERSIST message (integer) 1 redis> TTL message (integer) - 1
PERSIST命令就是PEXPIREAT命令的反操作: PERSIST 命令在過期字典中查找給定的鍵,並解除鍵和值(過期時間)在過期字典中的關聯。
舉個例子,如果數據庫當前的狀態如圖9-12所示,那么當服務器執行以下命令之后:
redis> PERSIST book (integer) 1
數據庫將更新成圖9-14 所示的狀態。
可以看到,當PERSIST命令執行之后,過期字典中原來的book鍵值對消失了,這代表數據庫鍵book的過期時間已經被移除。
以下是PERSIST 命令的偽代碼定義:
def PERSIST(key) : #如果鍵不存在,或者鍵沒有設置過期時間,那么直接返回 if key not in redisDB.expires: return 0 #移除過期字典中給定鍵的鍵值對關聯 redisDB.expires.remove(key) #鍵的過期時間移除成功 return 1
計算並返回剩余生存時間
TTL 命令以秒為單位返回鍵的剩余生存時間, 而P TTL 命令則以毫秒為單位返回鍵的剩余生存時間:
redis> PEXPIREAT alphabet 1385877600000 (integer) 1 redis> TTL alphabet (integer) 85 49007 redis> PTTL alphabet (integer) 8549001011
TTL 和PTTL 兩個命令都是通過計算鍵的過期時間和當前時間之間的差來實現的,以下
是這兩個命令的偽代碼實現:
def PTTL (key) : #鍵不存在於數據庫 if key not in redisDb.dict: return -2 #嘗試取得鍵的過期時間 #如果鍵沒有設置過期時間,那么expire_time_in_ms將為None expire_time_in_ms = redisDB.expires.get(key) #鍵沒有設置過期時間 if expire time in ms is None: return -1 #獲得當前時間 now_ms = get_current_unix _timestamp_in_ms () #過期時間減去當前時間, 得出的差就是鍵的剩余生存時間 return(expire_time_in_ms - now_ms) def TTL(key): #獲取以毫秒為單位的剩余生存時間 ttl_in_ms = PTTL (key ) if ttl_in_ms < 0: #處理返回值為2 和斗的情況 return ttl_in_ms else: #將毫秒轉換為秒 return ms_to_sec(ttl_in_ms)
舉個例子,對於一個過期時間為1385877600000(2013年12月1日零時)的鍵alphabet來說:
如果當前時間為1383282000000 (2013年11月1日零時),那么對鍵alphabet執行PTTL命令將返回2595600000 ,這個值是通過用a1phabet 鍵的過期時間減去當前時間計算得出的: 1385877600000-1383282000000=25956000000
另一方面,如果當前時間為1383282000000 ( 2013年11月1日零時),那么對鍵alphabet執行TTL命令將返回2595600,這個值是通過計算alphabet 鍵的過期時間減去當前時間的差,然后將差值從毫秒轉換為秒之后得出的。
過期鍵的判定
通過過期字典,程序可以用以下步驟檢查一個給定鍵是否過期:
1 )檢查給定鍵是否存在於過期字典:如果存在,那么取得鍵的過期時間。
2 )檢查當前UNIX 時間戳是否大於鍵的過期時間: 如果是的話,那么鍵已經過期;否則的話,鍵未過期。
可以用偽代碼來描述這一過程:
def is expired(key) : #取得鍵的過期時間 expire_time_in_ms = redisDB.expires.get(key) #鍵沒有設置過期時間 if expire_time_in_ms is none: return false #取得當前時間的UNIX 時間戳 now_ms = get_current_unix_timestamp_in_ms() #檢查當前時間是否大於鍵的過期時間 if now_ms > expire time_in_ms : #是,鍵已經過期 return true else : # 否,鍵未過期 return false
舉個例子,對於一個過期時間為1385877600000 (2013年12月1日零時)的鍵alphabet來說:
如果當前時間為1383282000000 (2013 年II 月1 日零時),那么調用is_expired ( alphabet ) 將返回False ,因為當前時間小於alphabet 鍵的過期時間。
另一方面,如果當前時間為1385964000000 (2013年12月2日零時),那么調用is_expired(alphabet) 將返回True. 因為當前時間大於alphabet 鍵的過期時間。
注意
實現過期鍵判定的另一種方法是使用TTL 命令或者PTTL 命令,比如說,如采對某個鍵執行TTL 命令,並且命令返回的位大於等於0 ,那么說明該鍵未過期。在實際中, Redis檢查鍵是否過期的方法和is_expired函數所描述的方法一致,因為直接訪問字典比執行一個命令稍微快一些。