Redis Write Failures — Default Eviction Policy
OOM errors from default 'noeviction' policy: use MEMORY USAGE to detect session leaks and avoid late-night pager calls—debugging steps for production..
20+ years shipping high-throughput database systems. Drawn from code that ran under real load.
- Redis is a single-threaded, in-memory data structure server that operates in microseconds
- Uses RAM for storage — eliminates disk I/O latency (nanoseconds vs milliseconds)
- Core types: Strings, Hashes, Lists, Sets, Sorted Sets — each designed for a specific access pattern
- Key expiry (TTL) prevents memory bloat and ensures data freshness
- Production risk: default
noevictionpolicy causes write failures when memory fills up without warning
Imagine your brain vs a filing cabinet. When your teacher asks you the capital of France, you don't go rummaging through a cabinet — you just know it instantly because it's in your short-term memory. Redis is that short-term memory for your application. Your main database (PostgreSQL, MySQL) is the filing cabinet — thorough but slow. Redis sits in RAM, razor-close to your app, so fetching a value takes microseconds instead of milliseconds. It's a supercharged sticky-note board your entire server cluster can share.
Every high-traffic application hits the same wall eventually: the database becomes the bottleneck. A query that takes 40ms feels invisible during development but becomes catastrophic when 10,000 users hit it simultaneously. Twitter, GitHub, Stack Overflow, and Shopify all reached this wall — and Redis is a big part of how they broke through it. It's not an exaggeration to say that understanding Redis is what separates junior developers from engineers who can design systems that actually scale.
Redis (Remote Dictionary Server) solves the read-amplification problem. Most web applications read data far more than they write it — a product page might be read 50,000 times a day but updated once. Hammering your relational database with 50,000 identical queries is wasteful and slow. Redis lets you compute the answer once, store it in memory, and serve all 50,000 requests from there in microseconds. But Redis isn't just a cache — it's a full data structure server that can power rate limiters, leaderboards, pub/sub messaging, session stores, and queues.
By the end of this article you'll understand not just what Redis commands look like, but WHY each data structure exists, WHEN to reach for each one, and how to wire Redis into a real application pattern. You'll also learn the subtle mistakes — wrong expiry strategies, cache stampedes, missing persistence configs — that trip up developers who learned Redis from a cheat sheet instead of from first principles.
What Redis Eviction Policy Actually Does
Redis is an in-memory key-value store where all data lives in RAM. When memory fills up, Redis must decide which keys to remove to make room for new writes — that decision is the eviction policy. By default, Redis uses no eviction (noeviction), meaning writes fail with an OOM error when maxmemory is reached. This is not a bug; it's a deliberate safety mechanism to prevent data loss without explicit configuration.
The eviction policy is set via maxmemory-policy in redis.conf. With noeviction, Redis rejects SET, LPUSH, and other write commands, returning an error to the client. Other policies like allkeys-lru or volatile-ttl evict keys based on usage or TTL. The default choice forces you to acknowledge memory limits — a design that prevents silent data loss but requires proactive capacity planning.
Use noeviction when data integrity is critical and you can predict memory usage — for example, caching session tokens with fixed TTLs. In production, teams often switch to allkeys-lru for general caching, but the default catches engineers off guard when memory spikes cause sudden write failures. Understanding this default is the first step to designing resilient Redis deployments.
Why Redis Lives in RAM and Why That Changes Everything
Traditional databases store data on disk. Disk access — even an NVMe SSD — operates in the microseconds-to-milliseconds range. RAM access operates in nanoseconds. That's not a small difference; it's three orders of magnitude. Redis keeps its entire dataset in memory by default, which is the single most important architectural decision behind its speed.
But speed isn't the only trick. Redis is single-threaded for command execution. That sounds like a limitation until you understand what it eliminates: lock contention. In a multi-threaded database, threads fight over the same rows with locks. Redis sidesteps that fight entirely — one command runs to completion before the next starts. This makes Redis operations atomic by default, which matters enormously for things like incrementing a counter or checking-then-setting a value.
Redis also supports optional persistence. You can tell it to snapshot its RAM contents to disk every N seconds (RDB snapshotting) or to log every write command to an append-only file (AOF). Most production setups use both. This means Redis isn't just a volatile cache — it can survive a restart and recover its data.
The practical takeaway: use Redis for data that is read far more than it's written, where milliseconds matter, and where you can tolerate the data being slightly stale or reproducible if lost.
entity:id:field — e.g., user:1001:display_name or session:abc123. This makes keys self-documenting and lets tools like RedisInsight group them visually. Never use flat keys like displayname1001 — you'll hate yourself when you have 2 million keys to debug.maxmemory and instantaneous_ops_per_sec in production; never use KEYS in app code.Redis Data Structures — Picking the Right Tool for Each Problem
Redis isn't just a key-value store in the boring sense. It stores five core data types, and choosing the right one is the difference between an elegant solution and a painful hack.
Strings — the default. Good for counters, cached HTML, serialized JSON blobs, and session tokens. The INCR command atomically increments a string-as-integer, making it perfect for rate limiting and hit counters.
Hashes — think of a Hash as a mini dictionary attached to one key. Instead of storing a user as one giant JSON blob, you store their fields separately. This lets you update a single field without fetching and re-serializing the entire object.
Lists — ordered, duplicates allowed. Built on a linked list internally. Ideal for queues (push to the tail, pop from the head) and activity feeds (push new events to the head, trim the list to keep only the last N).
Sets — unordered, unique members. Perfect for tracking unique visitors, tagging systems, or finding common followers between two users with SINTER.
Sorted Sets — the crown jewel. Every member has a floating-point score. Redis keeps members ordered by score automatically. This is how you build leaderboards, priority queues, and range-based queries without a single SQL ORDER BY.
The Cache-Aside Pattern — Wiring Redis Into a Real Application
Knowing Redis commands is one thing. Knowing how to integrate Redis into your application code without creating subtle bugs is another. The most widely-used pattern is Cache-Aside (also called Lazy Loading). The logic is elegantly simple: when your app needs data, check Redis first. If it's there (a cache hit), return it immediately. If it's not (a cache miss), fetch it from the database, store it in Redis with a TTL, then return it. Redis never gets data pushed to it — your application pulls it through.
This pattern is powerful because it's self-healing. If Redis goes down and loses all its data, your app degrades gracefully — everything just goes to the database until Redis is warm again. The cache populates itself organically based on what users actually request, not what you predict they'll request.
The critical detail most tutorials skip: always set a TTL. Without one, your cache grows forever and you'll eventually run out of RAM. More importantly, stale data lives forever. If a product's price changes in your database but the Redis entry never expires, customers see wrong prices indefinitely. Your TTL is your freshness guarantee.
The code below shows this pattern implemented in Python with the redis-py library — the same library used by Instagram and Pinterest in production.
Redis Expiry, Eviction and Why Your Cache Will Betray You Without Them
TTLs are your first line of defense against stale data. But what happens when Redis runs out of memory before any keys expire? This is where eviction policies come in, and most developers don't think about them until Redis starts refusing writes in production — which is a very bad day.
Redis has several eviction policies configured via maxmemory-policy in your redis.conf. The default policy is noeviction — Redis refuses new writes when full. That sounds safe but it means your application starts throwing errors. For a cache, you almost always want allkeys-lru (evict the least recently used key across all keys) or volatile-lru (evict the least recently used key that has a TTL set).
There's also the cache stampede problem — also called the thundering herd. Imagine 500 concurrent users all request the same popular product page. The cache entry expires at the exact same moment. All 500 requests find a cache miss simultaneously and all fire a database query at once. Your database gets hammered with 500 identical queries in the same millisecond. The fix is probabilistic early expiration or using a mutex lock in your cache-miss path so only one request rebuilds the cache while others wait.
The rule of thumb: if your cache powers any page that gets high traffic, you need to think about stampedes. If your cache serves data with truly random access patterns, you probably don't.
maxmemory-policy noeviction by default. If you don't set a maxmemory limit AND an eviction policy before going to production, one of two things happens: Redis eats all available RAM until the OS kills it, or Redis fills up and starts returning COMMAND errors to your application. Always set maxmemory and maxmemory-policy allkeys-lru in your redis.conf before deploying. Check it now — seriously.Redis Eviction Policies — Technical Comparison Table
Choosing the right eviction policy is one of the most consequential Redis decisions you'll make in production. Each policy has a specific use case, and using the wrong one can silently corrupt your data or crash your application. Below is a technical comparison of all eight eviction policies available in Redis 7.x.
| Policy | Scope | Algorithm | Evicts | Best Use Case | Data Loss Risk |
|---|---|---|---|---|---|
| noeviction | N/A | N/A | nothing | Bounded permanent stores (e.g., config keys) | None (but writes fail) |
| allkeys-lru | Entire keyspace | LRU approximation | Any key (LRU) | Pure cache with uniform access | Low (least recently used) |
| volatile-lru | Keys with TTL | LRU approximation | TTL keys (LRU) | Hybrid: permanent data + cache | Low for permanent keys, medium for TTL keys |
| allkeys-random | Entire keyspace | Random | Any key | Cache where all keys are equally valuable | Low |
| volatile-random | Keys with TTL | Random | TTL keys | Scenarios with TTL keys that can be regenerated | Medium |
| volatile-ttl | Keys with TTL | TTL value | Key with shortest TTL remaining | Cache-first workloads where you want to keep active data longest | Medium (may evict soon-to-expire keys) |
| allkeys-lfu | Entire keyspace | LFU approximation | Least frequently used | Workloads with skewed access patterns (popular content) | Low |
| volatile-lfu | Keys with TTL | LFU approximation | TTL keys with least frequency | Similar to volatile-lru but frequency-based | Low for permanent keys |
How to choose: - Pure cache: allkeys-lru or allkeys-lfu (if access pattern is skewed). - Session store with TTLs: volatile-ttl (keys with short TTL evicted first – likely stale anyway) or volatile-lru. - Permanent data only: noeviction is acceptable if you never hit the memory limit, but always monitor maxmemory. - Random access: allkeys-random is simple and predictable.
Algorithm internals: Redis LRU/LFU are approximations, not exact. They use a sample pool of keys (default 5) to pick the one to evict. This is O(1) with low overhead. You can tune the sample size via maxmemory-samples in redis.conf. Larger samples improve eviction quality but use more CPU.
Production checklist: 1. Never rely on the default noeviction for any cache workload. 2. Set maxmemory based on available RAM and headroom for other processes. 3. Monitor evicted_keys in INFO stats – if it's >0, your cache is under memory pressure. 4. Combine eviction with TTLs to reduce pressure on LRU/LFU algorithms.
evicted_keys > 0 during normal traffic, your maxmemory is too low or your TTLs are too long. An eviction rate above 100 keys/second indicates a memory leak or a workload that doesn't fit in RAM. Add memory alerting at 70% and 85% of maxmemory to catch this before evictions spike.noeviction is dangerous for caches; always change it in production.evicted_keys as a leading indicator of memory pressure.Redis Persistence — RDB vs AOF and Production Trade-offs
By default, Redis stores everything in RAM. If the server restarts, all data is lost. For a cache, that's acceptable. But many teams use Redis as a session store, a rate-limiter state store, or even a primary database for high-frequency writes. In those cases, losing data on restart is catastrophic.
Redis offers two persistence mechanisms:
RDB (Redis Database) — periodic snapshots of the entire dataset to disk. You configure how often (e.g., save 900 1 means if at least 1 key changed in 900 seconds, save). RDB files are compact and great for backups/disaster recovery. The downside: you lose data between snapshots. A crash 10 minutes before the next snapshot loses 10 minutes of writes.
AOF (Append Only File) — logs every write command to an append-only file. You can replay the file on restart to reconstruct the dataset. AOF gives you finer granularity: you can configure appendfsync everysec (lose at most 1 second of writes) or always (every write forces disk sync, near-zero data loss but 30-50% slower writes).
Most production setups use both. Redis supports a combined mode: RDB for fast restores, AOF for durability. When both are enabled, Redis loads the AOF on restart because it's more complete.
The choice matters for your data loss tolerance. Session stores need AOF everysec. Cache layers don't need persistence at all. A leaderboard that can rebuild from database can afford RDB-only. Match the persistence config to your data's criticality.
- RDB: Low overhead, fast recovery, but data loss window (up to your save interval). Best for cache layers and scenarios where data can be rebuilt.
- AOF: Higher overhead (especially appendfsync always), but sub-second data loss. Best for session stores, rate limiters, critical counters.
- Both: Use both for maximum safety. Redis prioritizes AOF on restart. The small disk cost is worth the peace of mind.
- Production trap: AOF with appendfsync always can cause write latency spikes during disk syncs. Test with your write throughput before enabling.
RDB vs AOF — Decision Matrix
Choosing between RDB and AOF (or both) depends on your data loss tolerance, write throughput, and recovery speed requirements. This decision matrix helps you pick the right combination for your production workload.
| Factor | RDB-Only | AOF-Only (everysec) | Both | None |
|---|---|---|---|---|
| Data loss on restart | Up to last snapshot (minutes) | ≤1 second | ≤1 second (AOF loads first) | All data lost |
| Recovery time | Very fast (binary file) | Slower (command replay) | Slower (AOF replay) | Instant (empty) |
| Write throughput impact | Minimal (fork + save) | ~5-10% overhead (everysec) | ~10-15% overhead | None |
| Disk space | Low (single compact file) | High (grows linearly) | Higher (both files) | None |
| Best for | Caches, ephemeral data | Session stores, counters | Critical data (orders, state) | Throwaway test data |
| Cross-version compatibility | Yes (RDB format stable) | Limited (AOF format changes) | Yes (both) | N/A |
| Backup strategy | Periodic RDB copy | AOF file copy + BGREWRITEAOF | Both | No backup |
| Incremental restore | Not possible (full restore) | Partial replay (risk) | Same as AOF | N/A |
Decision rules: 1. Cache only → No persistence. If Redis crashes, the cache rebuilds from the database. 2. Session store → AOF everysec. Losing sessions forces mass logouts; 1-second loss is acceptable. 3. Primary data store → Both. RDB for fast recovery (if AOF is corrupted) and AOF for durability. 4. Rate limiter / metadata → AOF everysec or RDB-only depending on whether state can be rebuilt. 5. Cross-version upgrade → RDB-only redises persist across versions; AOF may require rewriting.
Production recommendation: Use RDB snapshots for backup and AOF everysec for crash recovery. Configure AOF auto-rewrite (auto-aof-rewrite-percentage 100, auto-aof-rewrite-min-size 64mb) to keep AOF files manageable. Test your recovery procedure quarterly by simulating a server restart.
aof-load-truncated yes to load the last valid portion, but you'll lose the last few writes. Test AOF recovery with a crash simulation during staging.Redis Persistence Architecture — Visual Diagram
The diagram illustrates two separate paths for persistence. On the left (solid arrows), every write command is buffered and, depending on appendfsync, flushed to the AOF file on disk. On the right (dashed arrows), a background process BGSAVE forks and writes a snapshot (RDB file) at configured intervals. During restart, Redis checks for persistence files: AOF takes precedence if both exist. If only RDB exists, it loads the snapshot; if neither, the dataset starts empty.
Key architectural details: - RDB uses copy-on-write: the fork creates a child process that snapshots the memory at that instant. The parent continues serving requests, modified pages are copied via COW. This can spike memory usage (if many pages are modified during save). - AOF rewrite also forks: the child reads the existing AOF and creates a compact version in a temp file, then swaps it atomically. - Both mechanisms rely on to ensure data is actually on disk. fsync()appendfsync everysec issues once per second in a background thread; fsync()appendfsync always calls for every write, synchronously blocking the main thread.fsync()
What this means in practice: - If you enable both RDB and AOF, restart time is determined by AOF replay speed (linear with size). Large AOF files (>5 GB) can take seconds to load. - If you care about startup time and have AOF enabled, schedule regular BGREWRITEAOF to keep the file small. - For read-heavy workloads, RDB alone may be sufficient because restarts are fast and data loss is acceptable.
BGSAVE, the child process sees a frozen snapshot of memory. But any writes from the parent (on a page the child hasn't copied yet) trigger a page copy (COW). The more writes during a save, the more memory is used. For an instance with 1 GB dataset and 1000 writes/sec, expect ~200 MB extra memory during the RDB save. Monitor process_rss during saves.slave-lazy-flush no to avoid triggering RDB saves on replica sync.Redis Cluster — Architectural Overview
Redis Cluster provides automatic sharding and high availability without needing a separate proxy. It's the production architecture for any dataset exceeding a single server's RAM or any workload demanding horizontal scalability. Understanding its architecture is essential for engineers building large-scale caching and real-time data platforms.
Key Components: - Nodes: Each node in a cluster is a regular Redis server running in cluster mode (cluster-enabled yes). - Hash Slots: The keyspace is divided into 16,384 hash slots (hash function: CRC16(key) mod 16384). Each node is responsible for a subset of slots. - Cluster Bus: A dedicated TCP channel (port + 10000) for inter-node communication: heartbeat, gossip, failover. - Configuration: Every node stores the cluster configuration (nodes.conf) with the slot assignment and the view of all nodes. - Replicas: Each master node can have one or more replicas that replicate its data. If a master fails, a replica is promoted.
How Data Is Distributed: When a client sends a command for a specific key, the Redis client library (e.g., redis-py-cluster) computes the hash slot and sends the command directly to the node responsible for that slot. If the client sends to the wrong node, that node returns a -MOVED redirect error with the correct node's address. Smart clients cache the slot mapping to avoid redirects.
Resharding: Adding or removing nodes involves moving hash slots between nodes. This is done online via redis-cli --cluster reshard. During migration, slots are source nodes mark as migrating and target nodes as importing. Keys are migrated in batches using MIGRATE command. The cluster remains available during resharding.
Failover: Master failure detection is based on a quorum of nodes marking the master as PFAIL (possibly failing), then FAIL if a majority agree. A replica then triggers an election using the cluster bus. The winning replica increases its currentEpoch and becomes master. Automatic failover requires that the majority of masters are reachable.
Limitations: - Multi-key operations (e.g., MGET, transactions, Lua scripts) only work if all keys hash to the same slot. Use hash tags {key} to force keys into the same slot. - No support for multiple databases (only DB 0). - Minimum 3 master nodes recommended; for high availability, 3 masters + 3 replicas in different racks/availability zones. - Network latency between nodes adds overhead for cluster bus and replication.
When to use Redis Cluster: - Dataset > 10 GB (or whatever fits in one node's RAM). - Need for automatic failover and high availability. - Write throughput exceeds what a single Redis instance can handle. - You need linear scalability by adding nodes.
When not to use: - Your dataset fits in one node and you don't need high availability – a single instance with Sentinel is simpler. - You rely heavily on multi-key operations across different keys. - Your workload is predominantly read-heavy with a small dataset – single instance is faster.
redis-py-cluster) is essential to avoid performance-impacting redirects.{user}:1001 and {user}:1002 go to same slot).Cache Hit vs Cache Miss — The Two Paths That Decide Your Latency
Every Redis request takes one of two paths. Cache hit means data was in RAM. Cache miss means you're about to pay the disk tax. Simple, right? Yet I've seen teams flame out because they only tested the happy path.
A cache hit returns data in sub-millisecond time. The application gets its response, the database never sees the request. That's the whole point of Redis.
A cache miss is the expensive fallback. Redis returns nil, the application queries the primary database, writes the result back to Redis for next time, then serves the response. That's at least one network round trip plus a disk read. In high traffic, a sudden wave of misses — from a cache flush or deployment — can take down your database.
Design for cache misses. Set proper expiry. Pre-warm caches after restart. Your database doesn't have a second chance.
Real-World Redis — Where the Big Shops Actually Use It
Competitor pages list 'Netflix uses Redis for caching' like it's a revelation. Let's get specific about where Redis earns its keep in production.
E-commerce — product catalog caches. Amazon doesn't query DynamoDB for every page load. They push the entire category view into Redis hashes. Product ID as key, all attributes as fields. Fetch in one round trip. Fresh inventory at scale.
Real-time gaming leaderboards — sorted sets with scores. Every player action updates their score via ZINCRBY. Top 100 is ZREVRANGE with LIMIT. No SQL JOINs. No page refreshes. This is why your mobile game updates ranks instantly.
Rate limiting — INCR with EXPIRE. User hits an endpoint, you INCR a key like ratelimit:user:47291:minute. Set TTL to 60 seconds. If value > threshold, reject. Atomic. Fast. No database write. Instagram uses this pattern for API throttling.
Session store — EXPIRE handles user logout automatically. No cron jobs to clean stale sessions. No table scans.
What Makes Redis Fast — The Three Pillars (Spoiler: Not Just RAM)
Everyone says 'Redis is fast because it's in-memory.' True, but lazy. Plenty of in-memory databases are slow. Redis has three architectural decisions that matter more than RAM.
Single-threaded event loop. No locks. No context switching between threads. One queue processes all commands sequentially. This means no race conditions on simple operations, no mutex overhead. Write a complex Lua script? It blocks everything else. Don't write slow Lua scripts.
Non-blocking I/O with epoll/kqueue. Redis doesn't spin waiting for network data. It registers file descriptors and gets notified when data arrives. This is why a single Redis instance handles 100K+ ops per second on modest hardware.
Data structures tuned for cache lines. Redis strings, lists, hashes are designed to fit in CPU L2 cache. Compare that to PostgreSQL which is optimised for disk pages. Redis data is already hot in memory and laid out for minimal pointer chasing.
Combine these: single-threaded + event-driven + cache-optimised structures. That's the real answer. Not just 'it's in RAM'.
Working With Numbers — Redis Isn't Just for Strings
Redis treats numbers as strings internally but provides atomic operations for integer arithmetic. When you store a numeric value, Redis sees a string that can be incremented or decremented using commands like INCR, DECR, INCRBY, and DECRBY. These operations are atomic—no race conditions, no lost updates. This makes Redis an excellent choice for real-time counters, rate limiters, and leaderboards. The WHY: atomicity eliminates the need for locks or transactions in single-instance scenarios. If you try to increment a non-numeric value (like a word), Redis returns an error. The error message is clear: 'ERR value is not an integer or out of range'. Always validate your data type before performing arithmetic. Redis stores integers as signed 64-bit values, so overflow will wrap around silently—watch for that in high-throughput counters.
Saving and Retrieving Key-Value Pairs — The Foundation of Every Redis App
Redis stores data as key-value pairs. The key is always a string, and the value can be one of many data types: string, list, set, hash, or sorted set. The most basic operations are SET and GET. SET associates a key with a value; GET retrieves it. If the key doesn't exist, GET returns nil (null in most clients). The WHY: Redis uses keyspace as a flat dictionary—no schema, no tables, no joins. This zero-overhead model delivers microsecond latency for single-key lookups. When you SET a key that already exists, the old value is silently overwritten. To avoid accidental overwrites, use SETNX (set if not exists) or check for nil before writing. Namespacing keys with colons (like 'user:1000:name') is a standard practice—Redis handles long keys fine, but shorter keys mean less memory overhead. TTL expiry is often set at write time to control cache lifetimes.
The Late-Night Pager: Redis Write Failures from Default Eviction Policy
maxmemory-policy config.maxmemory-policy noeviction combined with a memory leak from unbounded session storage. When Redis hit the maxmemory limit (set to 48 GB via monitoring tool), it refused all writes. No key had a TTL, so no keys expired. The leak was caused by a session table that never cleaned stale sessions.maxmemory-policy allkeys-lru in redis.conf, added TTLs to session keys (EXPIRE 3600), and configured memory alerting at 80% of maxmemory. Ran CONFIG SET maxmemory-policy allkeys-lru as a live fix, then bounced the service to clear memory.- Always configure
maxmemory-policybefore going to production — never rely on defaults. - Every SET command must include a TTL unless the key is truly permanent (and documented).
- Set memory usage alerts at 70% and 85% of maxmemory — don't wait for OOM errors.
- Use
MEMORY USAGEandMEMORY STATSin production monitoring to detect leaks early.
INFO memory to see used_memory_human vs maxmemory. Temporarily increase maxmemory with CONFIG SET maxmemory 2gb, then permanently fix the eviction policy and memory leak.SLOWLOG GET 10 to find slow commands. Check CLIENT LIST for long-running connections. Verify network latency between app and Redis with redis-cli --latency.INFO evicted_keys. If evicted_keys > 0, the maxmemory-policy is evicting keys. Review maxmemory setting and add TTLs where missing. Also check for FLUSHDB/FLUSHALL in slowlog.INFO expired_keys. If many keys share the same TTL, you have a cache stampede. Use random TTL jitter (±20%) on SETs to spread expiry times. Also check if a deployment cleared cache via FLUSHALL.redis-cli ping. If fails, verify systemctl status redis, check logs at /var/log/redis/redis-server.log. Ensure bind config allows app IP. Check firewall/security groups. redis-cli -h <host> -p 6379 PING from the app server.redis-cli CONFIG SET maxmemory 2gbredis-cli CONFIG SET maxmemory-policy allkeys-lrumaxmemory 1gb and maxmemory-policy allkeys-lru. Restart Redis to apply.Key takeaways
Common mistakes to avoid
4 patternsNot setting a TTL on cached keys
Using KEYS * in production to find matching keys
SCAN 0 MATCH product:* COUNT 100 returns up to 100 matching keys per call and a cursor to continue from. Never use KEYS in production code.Storing large serialized objects as Strings and updating them non-atomically
Setting the same TTL for all cache keys in a high-traffic endpoint
ttl = base_ttl + random.uniform(-0.2base_ttl, 0.2base_ttl). This spreads expiry over time. Also consider probabilistic early expiration (refresh the cache when TTL < 10% of original).Interview Questions on This Topic
Redis is single-threaded — how can it handle thousands of concurrent connections without being a bottleneck?
Frequently Asked Questions
20+ years shipping high-throughput database systems. Drawn from code that ran under real load.
That's NoSQL. Mark it forged?
18 min read · try the examples if you haven't