Redis is easy to start and easy to misuse. A small instance can appear fast while its dataset grows without a budget, keys never expire, one command touches too much data, or a failover exposes assumptions the application never documented.

Production readiness is not established by a successful SET and GET. It comes from understanding what occupies memory, how entries leave, which operations can block progress, and what the application does when Redis slows, restarts, or loses data.

Give memory a purpose and a limit

Begin by classifying the data. Is Redis holding a rebuildable cache, ephemeral coordination state, session data, queues, counters, or information treated as durable? Mixing categories on one deployment can create conflicting requirements. A cache may welcome eviction while a queue cannot silently lose an item because memory is full.

Estimate memory from realistic keys, values, data structures, and cardinality—not only raw payload bytes. Redis needs memory for object and allocator overhead, dictionaries, client buffers, replication, persistence work, and temporary operations. Fragmentation means resident memory can differ from the logical dataset size.

Set a memory policy that leaves room for the deployment’s operating behavior. A limit equal to machine memory is not a safe budget if background persistence, replication buffers, the operating system, or failover processes also need memory. Exact headroom depends on version, configuration, workload, and persistence choices, so validate it with representative tests.

Monitor at least logical memory use, resident memory, fragmentation indicators, key count, expirations, evictions, rejected writes, connected clients, replication state, and latency. Correlate changes with application releases and key namespaces so growth has an owner.

TTL is a data-lifecycle rule

An expiry tells Redis when a key becomes eligible for removal. It is not a freshness guarantee for the underlying business value. A value can be stale before its TTL ends, and an application can accidentally replace a key without preserving the intended expiry depending on the command used.

Define TTLs by data contract:

  • cached representations should reflect their tolerated staleness and source-protection strategy;
  • session expiry should match authentication and product rules;
  • locks need ownership tokens and bounded lifetimes, not merely a guessed timeout;
  • rate-limit windows and counters need semantics consistent with the chosen commands.

Audit keys with no expiry when the namespace is expected to be temporary. A missing TTL is a common source of slow memory growth. Also avoid treating one global TTL as good design: hot reference data, costly reports, and user-specific state have different update and risk profiles.

Redis expires keys both when they are accessed and through active expiry work. An application should not depend on deletion happening at one exact instant. If exact business timing matters, use an explicit workflow and durable state rather than relying only on cache expiry.

Mass expiration is another operational risk. If many keys receive the same TTL at the same moment, they can expire together, causing CPU work and a wave of misses against the source. Add bounded jitter where the business contract permits, and use request coalescing or refresh-ahead for hot data.

Large keys create disproportionate work

A “big key” may contain a large string or a collection with many members. The important risk is not one universal byte threshold; it is the effect on commands, network transfer, replication, persistence, deletion, and failover under your workload.

An apparently simple command can process or return a large amount of data. Fetching a large value increases network and client memory cost. Operations over large collections can monopolize command execution long enough to affect unrelated requests. Deleting a large value may also create latency unless the chosen version and command can reclaim it asynchronously.

Prevent large keys at the application boundary. Limit payload size, collection cardinality, and batch operations. Split values only when the resulting access and invalidation model remains understandable. Pagination with incremental iteration is usually safer than requesting an entire large collection, but cursor-based scans still consume resources and need bounded frequency.

Identify key-size distribution in staging and production-safe diagnostics. Sample by namespace, investigate outliers, and connect them to code ownership. A low average can conceal one hot, enormous key.

Hot keys are a related but different problem. A small value requested at extreme frequency can saturate a shard, connection pool, or network path. Replicas, local caching, key splitting, or changed access patterns may help, but consistency and invalidation must remain explicit.

Eviction policy expresses acceptable loss

When Redis reaches maxmemory, the configured eviction policy determines whether it removes keys or rejects memory-growing commands. Policies can select among keys with TTLs or the broader keyspace and can use approximations of least-recently-used, least-frequently-used, random, or shortest-TTL behavior. Exact availability and semantics depend on Redis version.

Choose based on the data contract, not a generic recommendation. If the deployment is exclusively a rebuildable cache, eviction can be expected behavior. If non-evictable coordination or workflow state shares the same instance, eviction may be unacceptable—and mixing it with cache data may be the architectural mistake.

An eviction is not harmless just because the value can be rebuilt. It creates a miss and transfers load to the source. During memory pressure, sustained eviction can become sustained database pressure. Track eviction rate and miss cost, then protect the source with concurrency limits, stale serving, or controlled degradation as appropriate.

noeviction does not solve capacity; it changes failure into rejected writes. Applications must detect and handle that outcome. Test it deliberately rather than discovering it under full memory.

Persistence does not mean “no data loss”

Redis offers snapshot and append-only persistence options, along with replication and failover topologies. Each configuration has a window and a performance profile. Acknowledging a write before it reaches durable storage or a replica means some acknowledged data can be lost under particular failures.

Document what an acknowledgement means in the deployed configuration. Test restart and failover with representative load. Confirm how clients discover the new primary, what happens to in-flight commands, whether writes are retried, and whether those retries are idempotent.

If losing Redis data would violate a business guarantee, question whether Redis is being treated as a cache while acting as the system of record. Persistence features can be appropriate, but they require a deliberate durability and recovery design rather than assumptions inherited from another database.

Keep commands and clients bounded

Avoid unbounded key enumeration and commands whose cost scales with an uncontrolled collection. Use incremental scanning for operational discovery, with the understanding that scans do not provide a static snapshot. Keep scripts and transactions small enough that they do not monopolize execution.

Client pools also need limits. Excess connections consume server and application resources; too few can queue requests in the application. Set timeouts and deadlines, distinguish connection failures from command latency, and avoid retry storms. One retrying layer with a budget is safer than clients, proxies, and jobs all retrying independently.

Measure latency distributions rather than averages. Slow operations, network pauses, persistence work, or resource saturation may affect only part of the traffic while still damaging user-facing tail latency.

Plan for cache loss and cold recovery

If Redis is a cache, rehearse operating without it. The source may not be able to absorb a full miss rate even though every individual request has a fallback path. Protect it with admission control and bounded concurrency.

After restart or flush, an empty cache can trigger a cold-start surge. Warm only evidence-based hot data, ramp traffic, or coalesce loaders. The broader Distributed Caching article covers invalidation, stampedes, and source protection across cache technologies.

Memcached remains a different tool for workloads centered on disposable opaque values and simple cache operations. The decision is not a product contest. Data operations, memory behavior, failure expectations, and team capability should determine the fit.

When Redis behavior is entangled with application latency or database saturation, a performance and reliability assessment can trace the complete request path. The operational goal is straightforward: bounded memory, owned key lifecycles, predictable loss behavior, and tested recovery—not merely a fast in-memory response during normal conditions.