Redis Cache Eviction Strategies: Handling Full Memory Conditions
Redis utilizes memory to store data, enabling applications to bypass backend database access and improve response times. However, caching all data in Redis is not cost-effective. For instance, caching a 1TB MySQL dataset in Redis would require approximately 35,000 yuan worth of memory, whereas 1TB of disk storage costs only around 1,000 yuan. Moreover, data access follows the principle of locality, often referred to as the 80/20 rule, where 80% of requests access only 20% of the data. Therefore, allocating memory equivalent to the entire dataset size is unnecessary.
Given the high cost of memory, cache capacity should be smaller than the total backend database size. As cached data volume increases, limited cache space will inevitably become full. This necessitates implementing cache eviction mechanisms to remove less important data and make room for new entries.
Cache eviction involves two primary steps: identifying data that is less critical for application access and removing this data to free space. This article explores Redis cache replacement strategies and helps select appropriate configurations to improve cache hit rates and application performance.
Determining Appropriate Cache Capacity
The efficiency of cache usage depends on proper capacity setting. The goal is to maximize performance benefits while minimizing resource costs. Data access patterns exhibit locality characteristics, where a small portion of data receives most access requests.
When analyzing data access distribution, we observe that approximately 20% of data typically accounts for 80% of requests (the 80/20 principle). However, this ratio varies based on specific business scenarios. For example:
- During product promotions, 5% of popular products might handle 90% of requests, making it efficient to cache only this 5%.
- In applications requiring comprehensive data queries, caching 20% of data may not suffice if 80% still requires database access.
Recent analysis of internet applications reveals changing access patterns. User personalization has led to heavier-tailed distributions where 20% of data may contribute less than 80% of requests. This "heavy-tail effect" occurs when diverse user interests result in more evenly distributed data access.
Cache capacity planning requires balancing access patterns with cost considerations. While there's no one-size-fits-all solution, a practical approach is to allocate cache space between 15-30% of the total dataset size. This range typically balances performance benefits with memory costs.
For Redis, you can set the maximum cache size using:
CONFIG SET maxmemory 4gb
Regardless of capacity planning, cache exhaustion is inevitable. When the cache reaches its limit, the system must decide which data to evict and how to handle evicted items.
Redis Cache Eviction Strategies
Redis 4.0 introduced 6 eviction strategies, with 2 additional strategies added in later versions. These can be categorized based on whether they perform eviction and their candidate data scope:
- No eviction strategy: Only noeviction, which rejects write requests when memory limit is reached.
- Eviction strategies: Seven strategies that remove data when memory is full, further divided by candidate scope:
- Evicting from keys with expiration times: volatile-random, volatile-ttl, volatile-lru, volatile-lfu (Redis 4.0+)
- Evicting from all keys: allkeys-lru, allkeys-random, allkeys-lfu (Redis 4.0+)
Eviction Strategy Details
The noeviction strategy is Redis's default. When memory is exhausted, new write operations return errors rather than evicting data. This approach is unsuitable for typical caching scenarios where new data constantly needs to be added.
The volatile group of strategies only considers keys with expiration times for eviction:
- volatile-ttl: Evicts keys closest to expiration first.
- volatile-random: Randomly evicts keys with expiration times.
- volatile-lru: Uses LRU algorithm to evict least recently used keys with expiration times.
- volatile-lfu: Uses LFU algorithm to evict least frequently used keys with expiration times (Redis 4.0+).
The allkeys group considers all keys for eviction, regardless of expiration status:
- allkeys-random: Randomly evicts any key in the dataset.
- allkeys-lru: Uses LRU algorithm to evict least recently used keys from all data.
- allkeys-lfu: Uses LFU algorithm to evict least frequently used keys from all data (Redis 4.0+).
LRU Algorithm Implementation
LRU (Least Recently Used) algorithm evicts data that hasn't been accessed for the longest time. Traditional LRU implementations maintain a linked list where the head represents most recently used (MRU) data and the tail represents least recently used (LRU) data.
When data is accessed, it's moved to the MRU end. When new data needs to be added and cache is full, the LRU end data is removed, and new data is placed at the MRU end.
Redis optimizes traditional LRU to avoid performance overhead from maintaining linked lists. Instead, it tracks the last access time for each data entry and uses a sampling approach:
- Randomly select N data points as candidates (configurable via maxmemory-samples).
- Compare the last access times of these candidates.
- Evict the candidate with the oldest access time.
This approach avoids the need to maintain a full linked structure while still providing reasonable eviction decisions. For example:
CONFIG SET maxmemory-samples 100
Handling Evicted Data
When data is evicted, the system must determine whether to directly remove it or write it back to the database:
- Clean data: Data unchanged since retrieval from the database can be directly deleted.
- Dirty data: Modified data that differs from the database must be written back to maintain consistency.
Redis automatically deletes evicted data without writing back to the database. Therefore, when modifying cached data in Redis, applications should simultaneously update the database to prevent data loss during eviction.
Strategy Selection Recommendations
The choice of eviction strategy significantly impacts cache efficiency:
- allkeys-lru: Recommended when data access patterns show clear hot/cold distinctions. This keeps frequently accessed data in cache.
- allkeys-random: Suitable when data access frequencies are relatively uniform without clear hot/cold separation.
- volatile-lru: Useful for scenarios with persistent data (like pinned content) that shouldn't be evicted. By not setting expiration times on these items, they remain in cache while other data follows LRU eviction rules.
Effective caching requires balancing cache size and eviction strategy. Consider the following when implementing Redis caching:
- Analyze your data access patterns to determine appropriate cache size (typically 15-30% of total dataset).
- Select an eviction strategy based on your data's access characteristics.
- Implement proper write-back mechanisms for modified data to maintain consistency.