Memcached evicts items using a Least Recently Used (LRU) algorithm, but it’s not a simple "oldest first" deletion; it’s a probabilistic approximation that can lead to surprising data loss even when memory isn’t full.

Let’s watch this happen. Imagine we have a Memcached instance with a relatively small maxbytes setting, say 100MB, and we’re pushing data into it.

# Start Memcached with 100MB limit
memcached -m 100

# Populate with some items
for i in {1..1000}; do
  echo "set key_$i 0 60 100" | nc localhost 11211
  echo "value_for_$i" | nc localhost 11211
done

Now, let’s try to retrieve a key that we know we just set, but it’s gone.

echo "get key_500" | nc localhost 11211
# Expected: VALUE key_500 0 100\r\nvalue_for_500\r\nEND
# Actual: END

"But how?" you might ask. "I only used 100MB, and I have plenty of space left!" This is where the LRU approximation comes into play. Memcached doesn’t maintain a perfect, sorted list of all items by access time. Doing so would add significant overhead to every get and set operation. Instead, it uses a "sampling" approach. When Memcached needs to make space, it picks a random item from its internal data structures and checks if it’s "old enough" to be evicted. If it is, it’s evicted. If not, it picks another random item. This process repeats until enough space is freed.

The problem is that this random selection can, by chance, pick recently used items, especially if the cache is highly churned (lots of writes and reads). If the cache is close to full, the probability of picking an item that hasn’t been recently used is lower, and thus, even slightly older items might be evicted to make room for new ones. This is the "eviction" process, and it happens when Memcached’s internal memory usage reaches a threshold determined by maxbytes.

Tuning maxbytes:

The most direct control you have is the -m flag, which sets maxbytes. This is the total amount of memory Memcached will attempt to use for storing data.

  • Diagnosis: Monitor Memcached’s memory usage. You can use stats items to see how many items are in each slab and their approximate sizes.

    echo "stats items" | nc localhost 11211
    

    Look for the number of items and the age (which is a rough indicator of how long an item has been in the slab, not its absolute creation time).

  • Common Cause 1: maxbytes too small for your working set. If your application frequently writes more data than maxbytes can hold, eviction will occur.

    • Fix: Increase the -m value. For example, if you’re experiencing eviction and have plenty of RAM available on your server, try doubling it:
      memcached -m 200 # For 200MB
      
    • Why it works: A larger maxbytes allows Memcached to hold more items before it needs to start evicting, reducing the churn and the likelihood of essential data being removed prematurely.
  • Common Cause 2: High churn rate with a tight maxbytes limit. Even if your average data size is less than maxbytes, a burst of writes can trigger eviction.

    • Fix: While increasing -m is primary, you can also manage your application’s write patterns. If possible, batch writes or implement a tiered caching strategy. For Memcached itself, there isn’t a direct knob for this beyond maxbytes.
    • Why it works: Reducing the rate at which new data competes for space directly lowers the pressure on the eviction mechanism.
  • Common Cause 3: Uneven distribution of item sizes. If you have a few very large items and many small ones, the eviction algorithm might struggle. A large item might take up space that could have held many small, recently accessed items.

    • Fix: Re-evaluate your data serialization. If possible, break large objects into smaller, independent Memcached keys.
    • Why it works: Smaller, more granular items are more likely to be evicted individually when needed, rather than a single large item consuming a disproportionate amount of space and potentially causing other items to be evicted.
  • Common Cause 4: Stale configuration or server restart. Sometimes, the cache might appear "full" or evicting due to an incorrect initial maxbytes setting during startup.

    • Fix: Ensure your Memcached service is configured with the desired -m value in its startup script or systemd unit file. Restart Memcached if the configuration was recently changed.
    • Why it works: Guarantees that the memory limit is applied as intended from the moment Memcached starts.
  • Common Cause 5: Monitoring thresholds are too aggressive. You might be seeing "evictions" reported by monitoring tools, but the actual rate is acceptable for your application’s tolerance.

    • Fix: Adjust your monitoring alerts. Understand what constitutes an "unacceptable" eviction rate for your specific use case. A small number of evictions might be normal if your cache is consistently near its limit.
    • Why it works: Prevents alert fatigue and focuses on actual performance degradation rather than routine cache behavior.
  • Common Cause 6: Network latency impacting get operations. While not directly related to eviction policy, slow get operations can make items appear "older" than they are, potentially influencing which items are considered for eviction if the eviction logic were more complex (which it isn’t, but it’s a related operational concern).

    • Fix: Optimize network connectivity between your application servers and Memcached. Ensure sufficient bandwidth and low latency.
    • Why it works: Faster retrieval means items are more accurately reflected as "recently used" in the application’s perspective, though Memcached’s internal LRU doesn’t directly use this latency.

The core of Memcached’s eviction lies in its slab allocator. Items are grouped into "slabs" based on their size. Each slab has its own free list and eviction strategy. When Memcached needs to evict, it picks a random slab and then a random item within that slab. This means that if a particular slab becomes full of "old" items, even if other slabs have plenty of space and recently used items, eviction will still occur from the full slab.

You’ll likely encounter a memcached_server_return_code: 10 (ERR_ITEM_NOT_FOUND) or similar if an item you expect to be there has been evicted.

Want structured learning?

Take the full Memcached course →