Redis主要通過控制內存上限和回收策略實現內存管理。
設置內存上限
config set maxmemory xx,maxmemory限制的是Redis真正使用的內存,也就是used_memory對應的內存,由於內存碎片的存在,實際消耗的內存可能會比maxmemory大,實際使用時要小心這部分內存溢出。
內存回收策略
Redis的內存回收機制主要體現在以下兩個方面:
- 刪除到達過期時間的鍵對象。
- 內存使用達到maxmemory上限時觸發內存溢出控制策略。
刪除過期鍵對象
Redis所有鍵都可以設置過期屬性,內部保存在過期字典中。由於有大量的鍵,維護每個鍵精準的過期刪除機制會導致消耗大量的CPU,對於單線程的Redis來說成本過高,因此Redis採用惰性刪除和定時任務刪除機制實現過期鍵的內存回收。
惰性刪除:當客戶端讀取帶有超時屬性的鍵時,如果已經超過鍵設置的過期時間,會執行刪除操作並返回空。但當過期鍵一直沒有訪問無法得到及時刪除,從而導致內存不能及時釋放。Redis還提供另一種定時任務刪除機制作爲補充。
定時任務刪除:Redis內存維護一個定時任務,默認每秒運行10次(通過hz控制)。定時任務中刪除過期鍵邏輯採用了自適應算法,根據鍵的過期比例,使用快慢速率模式回收鍵。
# Redis calls an internal function to perform many background tasks, like
# closing connections of clients in timeout, purging expired keys that are
# never requested, and so forth.
#
# Not all tasks are performed with the same frequency, but Redis checks for
# tasks to perform according to the specified "hz" value.
#
# By default "hz" is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10
內存溢出控制策略
當Redis所用內存達到maxmemory上限時會觸發相應的溢出控制策略,具體控制策略由maxmemory-policy參數控制。
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key according to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
# The default is:
# maxmemory-policy noeviction
Redis在使用LRU算法回收過期鍵時,並不是採用維護TTL鏈表,而是使用採樣的方式。採樣的多少由maxmemory-samples控制。
# LRU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or
# accuracy. For default Redis will check five keys and pick the one that was
# used less recently, you can change the sample size using the following
# configuration directive.
# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs a bit more CPU. 3 is very fast but not very accurate.
# maxmemory-samples 5
每次Redis執行命令時若設置了maxmemory參數,都會嘗試執行內存回收操作。當Redis一直工作在內存溢出(used_memory>maxmemory)狀態下,且設置非noeviction策略時,會頻繁觸發內存回收操作,主要包括查找可回收的鍵和刪除鍵的開銷,影響Redis的性能。如果當前Redis有從節點時,回收內存操作對應的刪除命令會同步到從節點,導致寫放大的問題。