回覆列表
  • 1 # 使用者9923191605008

    我們資料平臺中有使用Redis來給線上提供低延時(20毫秒以內)的高併發讀寫請求,其中最大的Redis使用了阿里雲的Redis叢集(256G),儲存的記錄超過10億,Key的有效期設定為15天,每天寫入的記錄大概5000萬左右,QPS大概在6萬左右。由於過期Key的產生速度大於Redis自動清理的速度,因此在Redis中會有大量過期Key未被及時清理。

    被動刪除(惰性刪除):當讀/寫一個已經過期的Key時,會觸發惰性刪除策略,直接刪除掉這個Key;主動刪除(定期刪除):Redis會定期巡檢,來清理過期Key;當記憶體達到maxmemory配置時候,會觸發Key的刪除操作;

    另外,還有一種基於觸發器的刪除策略,因為對Redis壓力太大,一般沒人使用。

    在 Redis 中,常規操作由 redis.c/serverCron 實現,它主要執行以下操作:

    更新伺服器的各類統計資訊,比如時間、記憶體佔用、資料庫佔用情況等。清理資料庫中的過期鍵值對。對不合理的資料庫進行大小調整。關閉和清理連線失效的客戶端。嘗試進行 AOF 或 RDB 持久化操作。如果伺服器是主節點的話,對附屬節點進行定期同步。如果處於叢集模式的話,對叢集進行定期同步和連線測試。

    Redis 將 serverCron 作為時間事件來執行,從而確保它每隔一段時間就會自動執行一次, 又因為 serverCron 需要在 Redis 伺服器執行期間一直定期執行, 所以它是一個迴圈時間事件:serverCron 會一直定期執行,直到伺服器關閉為止。

    在 Redis 2.6 版本中, 程式規定 serverCron 每秒執行 10 次, 平均每 100 毫秒執行一次。 從 Redis 2.8 開始, 使用者可以透過修改 hz選項來調整 serverCron 的每秒執行次數, 具體資訊請參考 redis.conf 檔案中關於 hz 選項的說明。

    serverCron是由redis的事件框架驅動的定位任務,這個定時任務中會呼叫activeExpireCycle函式,針對每個db在限制的時間REDIS_EXPIRELOOKUPS_TIME_LIMIT內遲可能多的刪除過期key,之所以要限制時間是為了防止過長時間 的阻塞影響redis的正常執行。這種主動刪除策略彌補了被動刪除策略在記憶體上的不友好。

    隨機測試100個設定了過期時間的key刪除所有發現的已過期的key若刪除的key超過25個則重複步驟1

    這是一個基於機率的簡單演算法,基本的假設是抽出的樣本能夠代表整個key空間,redis持續清理過期的資料直至將要過期的key的百分比降到了25%以下。這也意味著在任何給定的時刻已經過期但仍佔據著記憶體空間的key的量最多為每秒的寫操作量除以4.

    Redis-3.0.0中的預設值是10,代表每秒鐘呼叫10次後臺任務。

    除了主動淘汰的頻率外,Redis對每次淘汰任務執行的最大時長也有一個限定,這樣保證了每次主動淘汰不會過多阻塞應用請求,以下是這個限定計算公式:

    #define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* CPU max % for keys collection */

    timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;

    hz調大將會提高Redis主動淘汰的頻率,如果你的Redis儲存中包含很多冷資料佔用記憶體過大的話,可以考慮將這個值調大,但Redis作者建議這個值不要超過100。我們實際線上將這個值調大到100,觀察到CPU會增加2%左右,但對冷資料的記憶體釋放速度確實有明顯的提高(透過觀察keyspace個數和used_memory大小)。

    可以看出timelimit和server.hz是一個倒數的關係,也就是說hz配置越大,timelimit就越小。換句話說是每秒鐘期望的主動淘汰頻率越高,則每次淘汰最長佔用時間就越短。這裡每秒鐘的最長淘汰佔用時間是固定的250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰頻率和每次淘汰的最長時間是透過hz引數控制的。

    從以上的分析看,當redis中的過期key比率沒有超過25%之前,提高hz可以明顯提高掃描key的最小個數。假設hz為10,則一秒內最少掃描200個key(一秒呼叫10次*每次最少隨機取出20個key),如果hz改為100,則一秒內最少掃描2000個key;另一方面,如果過期key比率超過25%,則掃描key的個數無上限,但是cpu時間每秒鐘最多佔用250ms。

    maxmemory

    當前已用記憶體超過maxmemory限定時,觸發主動清理策略,這些策略可以配置(引數maxmemory-policy),包括以下幾個:

    volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰

    volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰

    volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰

    allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰

    allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰

    no-enviction(驅逐):禁止驅逐資料

    當mem_used記憶體已經超過maxmemory的設定,對於所有的讀寫請求,都會觸發redis.c/freeMemoryIfNeeded(void)函式以清理超出的記憶體。注意這個清理過程是阻塞的,直到清理出足夠的記憶體空間。所以如果在達到maxmemory並且呼叫方還在不斷寫入的情況下,可能會反覆觸發主動清理策略,導致請求會有一定的延遲。

    清理時會根據使用者配置的maxmemory-policy來做適當的清理(一般是LRU或TTL),這裡的LRU或TTL策略並不是針對redis的所有key,而是以配置檔案中的maxmemory-samples個key作為樣本池進行抽樣清理。

    總結與備忘

    如果Redis中每天過期大量Key(比如幾千萬),那麼必須得考慮過期Key的清理:

    增加Redis主動清理的頻率(透過調大hz引數);

    dbsize命令返回的Key數量,包含了過期Key;

    randomkey命令返回的Key,不包含過期Key;

    scan命令返回的Key,包含過期Key;

    info命令返回的# Keyspace:

    db6:keys=1034937352,expires=994731489,avg_ttl=507838502

    keys對應的Key數量等同於dbsize;

    expires指的是設定了過期時間的Key數量;

    avg_ttl指設定了過期時間的Key的平均過期時間(單位:毫秒);

  • 中秋節和大豐收的關聯?
  • 有人說古代宮女出宮時年輕漂亮又有錢,為何卻無人敢娶?