原文地址:https://yq.aliyun.com/articles/257459#
背景
Redis作為一個高效能的記憶體NoSQL資料庫,其容量受到最大記憶體限制的限制。
使用者在使用Redis時,除了對效能,穩定性有很高的要求外,對記憶體佔用也比較敏感。在使用過程中,有些使用者會覺得自己的線上例項記憶體佔用比自己預想的要大。
事實上,例項中的記憶體除了儲存原始的鍵值對所需的開銷外,還有一些執行時產生的額外記憶體,包括:
- 垃圾資料和過期Key所佔空間
- 字典漸進式Rehash導致未及時刪除的空間
- Redis管理資料,包括底層資料結構開銷,客戶端資訊,讀寫緩衝區等
- 主從複製,bgsave時的額外開銷
- 其它
本文主要分析第一項Redis過期策略對記憶體的影響。
設定過期時間
- expire key time(以秒為單位)--這是最常用的方式
- setex(String key, int seconds, String value)–字串獨有的方式
注意:
- 除了字串自己獨有設定過期時間的方法外,其他方法都需要依靠expire方法來設定時間
- 如果沒有設定時間,那快取就是永不過期
- 如果設定了過期時間,之後又想讓快取永不過期,使用persist key
Redis三種Key過期策略
1、被動刪除(惰性刪除)
當讀/寫一個已經過期的key時,會觸發惰性刪除策略,直接刪除掉這個過期key。
只有key被操作時(如GET),REDIS才會被動檢查該key是否過期,如果過期則刪除之並且返回NIL。
a.這種刪除策略對CPU是友好的,刪除操作只有在不得不的情況下才會進行,不會其他的expire key上浪費無謂的CPU時間。
b.但是這種策略對記憶體不友好,一個key已經過期,但是在它被操作之前不會被刪除,仍然佔據記憶體空間。如果有大量的過期鍵存在但是又很少被訪問到,那會造成大量的記憶體空間浪費。expireIfNeeded(redisDb *db, robj *key)函式位於src/db.c。
但僅是這樣是不夠的,因為可能存在一些key永遠不會被再次訪問到,這些設定了過期時間的key也是需要在過期後被刪除的,我們甚至可以將這種情況看作是一種記憶體洩露----無用的垃圾資料佔用了大量的記憶體,而伺服器卻不會自己去釋放它們,這對於執行狀態非常依賴於記憶體的Redis伺服器來說,肯定不是一個好訊息
優點:刪除操作只發生在從資料庫取出key的時候發生,而且只刪除當前key,所以對CPU時間的佔用是比較少的,而且此時的刪除是已經到了非做不可的地步(如果此時還不刪除的話,我們就會獲取到了已經過期的key了)
缺點:若大量的key在超出超時時間後,很久一段時間內,都沒有被獲取過,那麼可能發生記憶體洩露(無用的垃圾佔用了大量的記憶體)
2、主動刪除
由於惰性刪除策略無法保證冷資料被及時刪掉,所以Redis會定期主動淘汰一批已過期的key。
先說一下時間事件,對於持續執行的伺服器來說, 伺服器需要定期對自身的資源和狀態進行必要的檢查和整理, 從而讓伺服器維持在一個健康穩定的狀態, 這類操作被統稱為常規操作(cron job)。在 Redis 中, 常規操作由 redis.c/serverCron
實現。
優點:通過限制刪除操作的時長和頻率,來減少刪除操作對CPU時間的佔用–處理"定時刪除"的缺點,定期刪除過期key–處理"惰性刪除"的缺點。
缺點:在記憶體友好方面,不如"定時刪除",在CPU時間友好方面,不如"惰性刪除"。
難點:合理設定刪除操作的執行時長(每次刪除執行多長時間)和執行頻率(每隔多長時間做一次刪除)(這個要根據伺服器執行情況來定了)
3、maxmemory
當前已用記憶體超過maxmemory限定時,觸發主動清理策略。
- 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(驅逐):(預設)禁止驅逐資料
相關最佳實踐優化配置
- 不要放垃圾資料,及時清理無用資料
實驗性的資料和下線的業務資料及時刪除; - key儘量都設定過期時間
對具有時效性的key設定過期時間,通過redis自身的過期key清理策略來降低過期key對於記憶體的佔用,同時也能夠減少業務的麻煩,不需要定期手動清理了. - 單Key不要過大
給使用者排查問題時遇到過單個string的value有43M的,也有一個list 100多萬個大成員佔了1G多記憶體的。這種key在get的時候網路傳輸延遲會比較大,需要分配的輸出緩衝區也比較大,在定期清理的時候也容易造成比較高的延遲. 最好能通過業務拆分,資料壓縮等方式避免這種過大的key的產生。 - 不同業務如果公用一個業務的話,最好使用不同的邏輯db分開
從上面的分析可以看出,Redis的過期Key清理策略和強制淘汰策略都會遍歷各個db。將key分佈在不同的db有助於過期Key的及時清理。另外不同業務使用不同db也有助於問題排查和無用資料的及時下線.
- 不要放垃圾資料,及時清理無用資料