當遇到美女面試官之如何理解Redis的Expire Key(過時鍵)

  在面試中遇到美女面試官時,咱們覺得面試會比較容易過,也能好好表現本身技術的時候了。然而卻出現如下這一幕,當美女面試官據說你使用過Redis時,那麼問題來了。面試

👩面試官Q1,你知道Redis設置key過時時間的命令嗎?redis

👧你:你堅決果斷的巴拉巴拉說了一堆命令,以及用法,好比expire 等等命令算法

(🎈這時候你想問得那麼簡單?但真的那麼簡單嗎?美女面試官停頓了一下,接着問)數據庫

👩面試官Q2,那你說說Redis是怎麼實現過時時間設置呢?以及怎麼判斷鍵過時的呢?
👧你:(這時候想這還難不倒我),而後又巴拉巴拉的說一通,Redis的數據庫服務器中redisDb數據結構以及過時時間的斷定服務器

(🎈你又在想應該不會問了吧,換個Redis的話題了吧,那你就錯了)數據結構

👩面試官:(擡頭笑着看了看你)Q3,那你說說過時鍵的刪除策略以及Redis過時鍵的刪除策略以及實現?
🤦‍️你:這時你回答的就不那麼流暢了,有時頭腦還阻塞了。dom

(🎈這是你可能就有點蒙了,或者只知道一些過時鍵的刪除策略,但具體怎麼實現不知道呀,你覺得面試官的提問這樣就完了嗎?)函數

👩面試官Q4,那你再說說其餘環節中是怎麼處理過時鍵的呢(好比AOF、RDB)?
🤦🤦你:...........學習

(🎈這更加尷尬了,知道的不全,也可能不知道,原本想好好表現,也想着面試比較簡單,沒想到會經歷這些)this

爲了不這尷尬的場景出現,那如今須要你記錄下如下的內容,這樣就能夠在美女面試官面前好好表現了。

1. Redis Expire Key基礎

redis數據庫在數據庫服務器中使用了redisDb數據結構,結構以下:

typedef struct redisDb {
     dict *dict;     /* 鍵空間 key space */
     dict *expires;    /* 過時字典 */
     dict *blocking_keys;  /* Keys with clients waiting for data (BLPOP) */
     dict *ready_keys;   /* Blocked keys that received a PUSH */
     dict *watched_keys;   /* WATCHED keys for MULTI/EXEC CAS */
     struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */
     int id;      /* Database ID */
     long long avg_ttl;   /* Average TTL, just for stats */
} redisDb;
複製代碼

其中,

  • 鍵空間(key space):dict字典用來保存數據庫中的全部鍵值對
  • 過時字典(expires):保存數據庫中全部鍵的過時時間,過時時間用UNIX時間戳表示,且值爲long long整數

1.1 設置過時時間命令

  • EXPIRE \<key> \<ttl>:命令用於將鍵key的過時時間設置爲ttl秒以後
  • PEXPIRE \<key> \<ttl>:命令用於將鍵key的過時時間設置爲ttl毫秒以後
  • EXPIREAT \<key> \<timesramp>:命令用於將key的過時時間設置爲timrestamp所指定的秒數時間戳
  • PEXPIREAT \<key> \<timesramp>:命令用於將key的過時時間設置爲timrestamp所指定的毫秒數時間戳

設置過時時間:

redis> set Ccww   5 2 0  
ok  
redis> expire Ccww 5  
ok  
複製代碼

使用redisDb結構存儲數據圖表示:

1.2過時時間保存以及斷定

過時鍵的斷定,其實經過過時字典進行斷定,步驟:

  1. 檢查給定鍵是否存在於過時字典,若是存在,取出鍵的過時時間
  2. 經過判斷當前UNIX時間戳是否大於鍵的過時時間,是的話,鍵已過時,相反則鍵未過時。

2. 過時鍵刪除策略

2.1 三種不一樣刪除策略

  1. 定時刪除:在設置鍵的過時時間的同時,建立一個定時任務,當鍵達到過時時間時,當即執行對鍵的刪除操做
  2. 惰性刪除:聽任鍵過時無論,但在每次從鍵空間獲取鍵時,都檢查取得的鍵是否過時,若是過時的話,就刪除該鍵,若是沒有過時,就返回該鍵
  3. 按期刪除:每隔一點時間,程序就對數據庫進行一次檢查,刪除裏面的過時鍵,至於要刪除多少過時鍵,以及要檢查多少個數據庫,則由算法決定。

2.2 三種刪除策略的優缺點

2.2.1 定時刪除

  • 優勢: 對內存友好,定時刪除策略能夠保證過時鍵會盡量快地被刪除,並釋放國期間所佔用的內存
  • 缺點: 對cpu時間不友好,在過時鍵比較多時,刪除任務會佔用很大一部分cpu時間,在內存不緊張但cpu時間緊張的狀況下,將cpu時間用在刪除和當前任務無關的過時鍵上,影響服務器的響應時間和吞吐量

2.2.2 惰性刪除

  • 優勢: 對cpu時間友好,在每次從鍵空間獲取鍵時進行過時鍵檢查並是否刪除,刪除目標也僅限當前處理的鍵,這個策略不會在其餘無關的刪除任務上花費任何cpu時間。
  • 缺點: 對內存不友好,過時鍵過時也可能不會被刪除,致使所佔的內存也不會釋放。甚至可能會出現內存泄露的現象,當存在不少過時鍵,而這些過時鍵又沒有被訪問到,這會可能致使它們會一直保存在內存中,形成內存泄露。

2.2.4 按期刪除

  因爲定時刪除會佔用太多cpu時間,影響服務器的響應時間和吞吐量以及惰性刪除浪費太多內存,有內存泄露的危險,因此出現一種整合和折中這兩種策略的按期刪除策略。

  1. 按期刪除策略每隔一段時間執行一次刪除過時鍵操做,並經過限制刪除操做執行的時長和頻率來減小刪除操做對CPU時間的影響。
  2. 定時刪除策略有效地減小了由於過時鍵帶來的內存浪費。

定時刪除策略難點就是肯定刪除操做執行的時長和頻率:

  刪除操做執行得太頻繁。或者執行時間太長,按期刪除策略就會退化成爲定時刪除策略,以致於將cpu時間過多地消耗在刪除過時鍵上。相反,則惰性刪除策略同樣,出現浪費內存的狀況。 因此使用按期刪除策略,須要根據服務器的狀況合理地設置刪除操做的執行時長和執行頻率。

3. Redis的過時鍵刪除策略

  Redis服務器結合惰性刪除和按期刪除兩種策略一塊兒使用,經過這兩種策略之間的配合使用,使得服務器能夠在合理使用CPU時間和浪費內存空間取得平衡點。

3.1 惰性刪除策略的實現

  Redis在執行任何讀寫命令時都會先找到這個key,惰性刪除就做爲一個切入點放在查找key以前,若是key過時了就刪除這個key。

robj *lookupKeyRead(redisDb *db, robj *key) {
          robj *val;
	 expireIfNeeded(db,key); // 切入點
	 val = lookupKey(db,key);
	 if (val == NULL)
	  server.stat_keyspace_misses++;
	 else
	  server.stat_keyspace_hits++;
	 return val;
}
複製代碼

經過expireIfNeeded函數對輸入鍵進行檢查是否刪除

int expireIfNeeded(redisDb *db, robj *key) {
     /* 取出鍵的過時時間 */
    mstime_t when = getExpire(db,key);
    mstime_t now;
    
     /* 沒有過時時間返回0*/
    if (when < 0) return 0; /* No expire for this key */

    /* 服務器loading時*/
    if (server.loading) return 0;

    /* 根據必定規則獲取當前時間*/
    now = server.lua_caller ? server.lua_time_start : mstime();
    /* 若是當前的是從(Slave)服務器
     * 0 認爲key爲無效
     * 1 if we think the key is expired at this time. 
     * */
    if (server.masterhost != NULL) return now > when;

     /* key未過時,返回 0 */
    if (now <= when) return 0;

     /* 刪除鍵 */
    server.stat_expiredkeys++;
    propagateExpire(db,key,server.lazyfree_lazy_expire);
    notifyKeyspaceEvent(NOTIFY_EXPIRED,
        "expired",key,db->id);
    return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
                                         dbSyncDelete(db,key);
}
複製代碼

3.2 按期刪除策略的實現

  key的按期刪除會在Redis的週期性執行任務(serverCron,默認每100ms執行一次)中進行,並且是發生Redis的master節點,由於slave節點會經過主節點的DEL命令同步過來達到刪除key的目的。

for (j = 0; j < dbs_per_call; j++) {
 int expired;
 redisDb *db = server.db+(current_db % server.dbnum);
 
 current_db++;
 
 /* 超過25%的key已過時,則繼續. */
 do {
  unsigned long num, slots;
  long long now, ttl_sum;
  int ttl_samples;
 
  /* 若是該db沒有設置過時key,則繼續看下個db*/
  if ((num = dictSize(db->expires)) == 0) {
   db->avg_ttl = 0;
   break;
  }
  slots = dictSlots(db->expires);
  now = mstime();
 
  /*但少於1%時,須要調整字典大小*/
  if (num && slots > DICT_HT_INITIAL_SIZE &&
   (num*100/slots < 1)) break;
 
  expired = 0;
  ttl_sum = 0;
  ttl_samples = 0;
 
  if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
   num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;// 20
 
  while (num--) {
   dictEntry *de;
   long long ttl;
 
   if ((de = dictGetRandomKey(db->expires)) == NULL) break;
   ttl = dictGetSignedIntegerVal(de)-now;
   if (activeExpireCycleTryExpire(db,de,now)) expired++;
   if (ttl > 0) {
    /* We want the average TTL of keys yet not expired. */
    ttl_sum += ttl;
    ttl_samples++;
   }
  }
 
  /* Update the average TTL stats for this database. */
  if (ttl_samples) {
   long long avg_ttl = ttl_sum/ttl_samples;
 
   /樣本獲取移動平均值 */
   if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;
   db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);
  }
  iteration++;
  if ((iteration & 0xf) == 0) { /* 每迭代16次檢查一次 */
   long long elapsed = ustime()-start;
 
   latencyAddSampleIfNeeded("expire-cycle",elapsed/1000);
   if (elapsed > timelimit) timelimit_exit = 1;
  }
 /* 超過期間限制則退出*/
  if (timelimit_exit) return;
  /* 在當前db中,若是少於25%的key過時,則中止繼續刪除過時key */
 } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
}
複製代碼

  依次遍歷每一個db(默認配置數是16),針對每一個db,每次循環隨機選擇20個(ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)key判斷是否過時,若是一輪所選的key少於25%過時,則終止迭次,此外在迭代過程當中若是超過了必定的時間限制則終止過時刪除這一過程。

4. AOF、RDB和複製功能對過時鍵的處理

4.1 RDB

生成RDB文件
 程序會數據庫中的鍵進行檢查,已過時的鍵不會保存到新建立的RDB文件中

載入RDB文件

  1. 主服務載入RDB文件,會對文件中保存的鍵進行檢查會忽略過時鍵加載未過時鍵
  2. 從服務器載入RDB文件,會加載文件所保存的全部鍵(過時和未過時的),但從主服務器同步數據同時會清空從服務器的數據庫。

4.2 AOF

  • AOF文件寫入:當過時鍵被刪除後,會在AOF文件增長一條DEL命令,來顯式地記錄該鍵已被刪除。
  • AOF重寫:已過時的鍵不會保存到重寫的AOF文件中

4.3 複製

 當服務器運行在複製模式下時,從服務器的過時鍵刪除動做由主服務器控制的,這樣的好處主要爲了保持主從服務器數據一致性:

  1. 主服務器在刪除一個過時鍵以後,會顯式地向全部的從服務器發送一個DEL命令,告知從服務器刪除這個過時鍵
  2. 從服務器在執行客戶端發送的讀取命令時,即便碰到過時鍵也不會將過時鍵刪除,不做任何處理。
  3. 只有接收到主服務器 DEL命令後,從服務器進行刪除處理。

最後可關注公衆號【Ccww筆記】,一塊兒學習,天天會分享乾貨,還有學習視頻乾貨領取!

相關文章
相關標籤/搜索