Build Daily

Tinley Park · May 29, 2026
DatabaseSalvatore Sanfilippo (original); Redis Ltd. (current)

Redis

The in-memory data structure server. Reach for it when you need a cache, a rate-limiter, a queue, a session store, or a leaderboard — not when you need a primary store. Microsecond latency; durability is a configurable extra, not the default.

Updated May 28, 2026

Redis is what you reach for when the data is ephemeral, the latency budget is microseconds, and the data shape is "more interesting than a key-value but less interesting than a row." It is a data structure server — strings, hashes, lists, sets, sorted sets, streams, bitmaps, hyperloglogs — accessed over a network in O(1) or O(log n) per operation. Official docs at redis.io/docs own the command reference.

What it is

A single-threaded (per shard), in-memory key-value-and-more store with optional persistence. Single binary, single port, single config file. Sub-millisecond reads and writes when the dataset fits in RAM. Replication, sentinel-based failover, and Redis Cluster for sharding all built in.

Distribution: official Docker image, every cloud has a managed offering (ElastiCache, MemoryStore, Upstash). The Redis Ltd. license change in 2024 (RSALv2 / SSPL, then back-ish) prompted forks — Valkey is the BSD-licensed Linux Foundation fork and is functionally equivalent for everything in this page. KeyDB and Dragonfly are alternative implementations of the same protocol. The wire protocol (RESP) is stable across all of them.

The pitch: when the data is transient ("rate-limit counter for this IP for the next 60 seconds"), needed everywhere ("session token → user ID"), or shape-specific ("top N leaderboard, updated in real time"), Redis is the right primitive. The wrong primitive in Postgres for these workloads is "hit the disk for a million tiny lookups per second." The wrong primitive in an application-level cache is "lose it when the process restarts."

When to use it

Reach for it when:

  • You need a cache in front of slow data — query results, computed aggregates, API responses, rendered HTML.
  • You need a rate limiterINCR + EXPIRE on a key per IP/user gives you a token bucket in two commands.
  • You need a session store — JWT works for stateless; sessions in Redis work for stateful with revocation.
  • The data shape is sets, sorted sets, or streams — "is this user in the X cohort," "top traders by P&L this week," "stream of recent events."
  • You need a pub/sub or simple queue — Redis Streams + consumer groups give you a real durable queue. (Note: real durable queues with retries and DLQs may still warrant a real broker.)
  • You need distributed locks — Redlock when you need them; SET NX EX when you don’t need the strong-consistency story.

Skip it when:

  • The data is your source of truth — Redis can lose data on crash if you don’t carefully configure AOF. Even then, treat it as a cache and recompute, not the durable ledger.
  • The dataset does not fit in RAM — Redis is fundamentally an in-memory store. RAM cost scales with state.
  • You need complex queries — Redis is not a query engine. There is no "WHERE balance > 100." Model the access pattern in the data structure, or pick a different store.
  • You need transactions across many keys with strict isolation — MULTI / EXEC give you atomicity, not isolation in the database-theory sense.

At a glance

Core data structures

  • String — bytes, with GET, SET, INCR, EXPIRE. The workhorse.
  • Hash — field/value map per key. Object storage without serializing the whole thing.
  • List — doubly-linked list. LPUSH / RPOP give you a queue.
  • Set — unordered unique members. Membership tests in O(1).
  • Sorted set (ZSET) — members with scores. Leaderboards, time-windowed indexes, priority queues.
  • Stream — append-only log with consumer groups. Real queue semantics: ack, retry, pending lists.
  • Bitmap — bit-level operations on strings. Daily active users in a few KB.
  • HyperLogLog — approximate cardinality in 12 KB regardless of input size.

Persistence

  • RDB — periodic point-in-time snapshots. Fast, compact, loses data between snapshots.
  • AOF — append-only log of every write. Durable, larger, configurable fsync policy.
  • Both at once — recommended for production. RDB for fast restart, AOF for durability.

Eviction

When maxmemory is set, Redis can evict keys to stay under the limit. allkeys-lru for a general cache; volatile-ttl for a TTL-centric cache; noeviction to fail writes instead. Pick deliberately.

How to integrate

Default integration for a new use case:

  1. Pick managed or self-hosted. Upstash (serverless, per-request pricing) for low-traffic; ElastiCache / MemoryStore for high-traffic; self-host on a small box for hobby. Valkey is a clean drop-in if the Redis license matters to you.
  2. Pick a client. Python: redis-py (sync) or redis.asyncio (async). Node: ioredis. Most clients pool connections by default.
  3. Set maxmemory and an eviction policy. A Redis instance with no memory cap will OOM the box.
  4. TTL everything by default. A cache without TTLs is a memory leak. EXPIRE or SET ... EX.
  5. Plan persistence consciously. If Redis is purely a cache, RDB-only is fine. If it’s a session store, AOF with appendfsync everysec is the typical compromise.
  6. Lua scripts for atomicity. When you need to do a check + write atomically (rate limit, distributed lock), a small EVAL is the right pattern.

In the GL stack

Redis is not a primary store anywhere in the active focus today. Cache and rate-limit needs at paiddaily.io are small enough to live in Postgres (with appropriate indexes) or in-process; the operational cost of a separate Redis instance has not paid for itself yet.

Where it would earn its place:

  • Wallet sync rate-limiting when call volume to RPC providers grows past where in-memory token buckets are sufficient.
  • Push notification queue as a more durable surface than an in-process channel.
  • Session store if and when JWT-only auth gets revisited.

The default until that day: don’t add Redis until the absence of Redis is the actual problem.

Gotchas

  • It is in-memory. Restarts that go beyond a single instance (failover, network partition, OOM kill) can lose data. Treat the cache contract as the default and reason carefully about anything stronger.
  • Single-threaded per shard. A slow Lua script or a KEYS * against a million-key database blocks every other client. Use SCAN, not KEYS, in production.
  • KEYS * in production is a footgun. Use SCAN for iteration. Period.
  • The license drama is real. Redis Ltd. moved off BSD in 2024. If license matters (commercial vendor distribution, certain compliance regimes), pick Valkey. The protocol and the command set are the same.
  • Cluster mode changes semantics. Multi-key operations require keys to live in the same hash slot. Plan key naming with {tags} if you might shard later.

Risks

  • Memory cost. RAM is expensive; an unbounded growing dataset in Redis can become the most expensive line item in your infrastructure. Cap it; TTL aggressively.
  • Operational complexity at scale. Sentinel and Cluster work, but they have sharp edges. The managed offerings are worth their cost the moment Redis becomes important to the system.
  • Wrong-tool risk. Redis is so fast that it’s tempting to use it for things that should be in a real database. Resist; it makes data loss a question of "when," not "if," for the data that mattered.

Alternatives

  • Valkey — the open-source Redis fork after the license change. Same protocol, same clients, BSD license. The default for fresh self-hosted deployments today.
  • Memcached — Wins when you only need a pure cache with no data structures, no persistence, no pub/sub. Loses on every "I wish I could do X" feature comparison. Simpler, narrower, perfectly good for what it does.
  • Dragonfly / KeyDB — Wins when you need multi-threaded throughput on a single instance. Loses on ecosystem maturity. Drop-in protocol; different implementation.
  • Postgres LISTEN/NOTIFY + SKIP LOCKED queues — Wins when you already have Postgres and the throughput is modest. Avoids another moving part. Loses on raw speed and richer data structures. Often the right default before adding Redis.
  • In-process cache (e.g., lru-cache, cachetools) — Wins when one process is the only consumer. Loses the moment you need shared state across processes. The right starting point for most caches.

Related

  • Postgres — when the cache becomes a source of truth, this is where it lives.
  • Neo4j — different shape, different problem. Both can sit next to Redis happily.