Common Cache Strategies: LRU and LFU

Common Cache Strategies: LRU and LFU

In distributed systems and high-concurrency applications, Redis, as the most popular in-memory database and caching middleware, is critical for performance optimization. When dealing with massive data access, deciding which data to retain and which to evict directly determines system response speed and resource utilization. Among these, eviction policies are a key component of Redis configuration, while LRU (Least Recently Used) and LFU (Least Frequently Used) are the two most classic and widely applied algorithms. Understanding their underlying logic and applicable scenarios is decisive for building efficient caching architectures.
The core idea of the LRU strategy is simple and intuitive: retain data accessed most recently in memory; once memory is full, evict the record that has not been used for the longest time.

From a technical implementation perspective, when Redis enables maxmemory-policy allkeys-lru, it maintains a doubly linked list in the background. When a write operation causes an overflow and triggers eviction, the LRU algorithm scans the front end of this list (i.e., the earliest accessed nodes), removes them, and releases the space. This strategy assumes that data lifecycle correlates positively with its "hotness"—data used more frequently or more recently holds higher value; conversely, data that has not been touched for a long time is highly unlikely to be read again.

Typical Application Scenarios for LRU

  • Web Server Caching: For example, using Nginx in conjunction with Redis to store static resources (HTML, CSS, JS). When a user accesses the same page for the second time, it directly hits memory, significantly reducing load on the origin server.
  • API Response Prefetching: Cache results of frequently called interface parameters, utilizing LRU to automatically clean up old data that has not been requested for a long time.

Code Example Logic

# Python pseudo-code simulating the LRU eviction process
class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
    
    def get(self, key):
        # Move the node to the tail of the list (mark as "recent") upon access
        if key in self.cache:
            return self.cache[key]
        
    def put(self, key, value):
        # If memory is full, remove the head element (least recently used)
        while len(self.cache) >= self.capacity and not is_full():
            remove_head() 

LFU: "Hotness First" Based on Frequency Dimension

If LRU focuses on "when it was used," then LFU focuses on "how many times it was used." The LFU (Least Frequently Used) strategy posits that even if a piece of data has not been accessed for a long time, if its total historical access count is extremely high, it indicates it is the core hot data in the system.

When maxmemory-policy allkeys-lfu is enabled in Redis, Redis maintains a counter for each key. When memory is insufficient and eviction is needed, the algorithm calculates the "recent N accesses" or "historical cumulative" access frequency of all candidate keys and removes the one with the lowest count. It is worth noting that LFU in Redis is not a simple linear statistic; instead, it employs a sliding window mechanism to avoid the problem where old data can never be evicted due to long-term lack of updates (by introducing a time decay factor).

Advantages and Limitations of LFU

  • Advantages: It is highly effective for datasets with distinct "long-tail" characteristics. For example, certain obscure documents may rarely be viewed but occasionally experience sudden high-traffic access; or in log analysis scenarios, specific error codes might exist for a long time but rarely be queried, whereas frequent business status codes are the priority. LFU can more precisely protect truly high-value historical data.
  • Limitations: Implementation complexity is far higher than LRU. It requires maintaining additional counters and data structures (such as skip lists or trees), which may incur certain CPU overhead and increased memory consumption in extreme cases.

Analysis of Applicable Scenarios for LFU

  • Log and Audit Systems: Store large volumes of access logs, where certain specific operation IDs might exist for a long time but rarely be retrieved, while key operation types need high hit rates.
  • Recommendation Algorithm Caching: In e-commerce or social scenarios, user browsing history often presents complex non-uniform distributions; simply evicting based on time might mistakenly kill high-value product historical snapshots.

Selection Recommendations for LRU vs. LFU

In practical engineering implementation, the choice of strategy depends on specific business data characteristics. If the dataset exhibits obvious "recently active" characteristics (such as session states or temporary tokens), LRU is usually the preferred solution due to its simple implementation and superior performance. However, when data presents complex access patterns or requires balancing the long-term value of historical data, LFU provides finer-grained control.

It is worth noting that Redis also supports other strategies such as volatile-lru or allkeys-random. In actual configuration, it is recommended to first analyze the time distribution and frequency characteristics of hot data using monitoring tools: if "old data not used for a long time" is found to be an important asset of the system (such as historical order details), switch decisively to LFU; if it is confirmed that only recently generated data holds business significance, sticking with LRU will yield the best cost-performance ratio.