Redis and redis-cli: The In-Memory Store Behind Fast APIs

Redis is not “just a cache.” It is an in-memory data structure server that applications talk to over the network—often for speed, sometimes as the system of record for short-lived or high-churn data. This guide explains what Redis is, when it fits, how it behaves under load, and how to learn it hands-on with redis-cli.

In short

Redis keeps data in RAM and exposes simple commands over TCP. Pick the right data structure for the job, set TTLs and memory limits on purpose, and use redis-cli to inspect reality before you trust application logs.

What Redis is (and what it is not)

Redis (Remote Dictionary Server) is an open-source, in-memory data store. Clients send commands like SET user:42 "Babu" or ZADD leaderboard 100 "player1"; the server executes them and replies. Most operations are microseconds to low milliseconds—orders of magnitude faster than round-tripping a relational database for hot paths.

Redis is not a drop-in replacement for PostgreSQL or S3. It excels when latency matters, data fits in memory (or you accept eviction), and your access patterns map to its structures. It is also not magic durability: persistence exists (RDB snapshots, AOF logs), but design for the case where Redis restarts or fails—and plan how you rebuild state.

Managed offerings (ElastiCache, Memorystore, Azure Cache) and self-hosted Helm charts wrap the same core: a Redis-compatible server your apps reach on port 6379 by default.

The mental model: key → typed value

Everything lives under a key (a string name). The value has a type—string, hash, list, set, sorted set, stream, and more. Commands are type-specific: LPUSH works on lists; HSET on hashes. Calling the wrong command returns an error, which is Redis protecting you from silent corruption.

Keys often use namespaces with colons: session:abc123, cache:product:991. That convention helps humans and operators scan related data—though production systems should avoid KEYS * on large databases (see below).

Core data structures and when to use them

TypeThink of it asTypical uses
StringBlob, counter, or serialized JSONCache page fragments, feature flags, rate-limit counters (INCR)
HashField → value map under one keyUser profile fields, object cache without one key per attribute
ListOrdered sequence (linked-list semantics)Recent events, lightweight queues (with care—see streams)
SetUnordered unique membersTags, “users online”, deduplication
Sorted set (ZSET)Members with scores (ranked)Leaderboards, priority queues, time-series indexes by score
StreamAppend-only log with consumer groupsEvent sourcing light, job pipelines with acknowledgements
Bitmap / HyperLogLog / GEOSpecialized primitivesDaily active users (bitmap), cardinality estimates (HLL), location queries (GEO)

Choosing the structure up front saves pain. A leaderboard implemented as a JSON string in a string key works until you need top-N queries—then a sorted set is the natural fit.

Common architecture patterns

  • Cache-aside. App reads DB; on miss, loads DB and writes Redis with a TTL. Invalidation happens on writes (delete or update keys)—stale cache is the classic bug if you forget this.
  • Session store. Web sessions in Redis with TTL; enables horizontal scaling of stateless app servers.
  • Pub/Sub. Fire-and-forget messaging between services—not durable; subscribers offline miss messages. Use Streams or a message broker when you need persistence.
  • Distributed lock (carefully). SET lock:resource NX EX 30 with a unique token; release with a Lua script that checks the token. Redlock is debated—understand your consistency needs before betting production on locks in Redis alone.
  • Rate limiting. Counters with expiry, or sliding windows with sorted sets / Redis Cell module patterns.

On Kubernetes, Redis often runs as a StatefulSet or is consumed as a managed service; pair with network policies and secrets—not a ClusterIP wide open to every namespace. See StatefulSets with PostgreSQL and Redis for manifests, persistence on PVCs, and HA patterns; Kubernetes architecture for where such dependencies sit in the platform picture.

Single-threaded execution (and why it matters)

The main Redis process handles commands from clients one at a time per shard (Redis 7+ has threads for I/O and some background work, but the command execution model still shapes design). Implications:

  • Fast commands stay fast. Simple GET/SET scale to very high QPS on modest hardware.
  • Slow commands block everyone. KEYS *, huge SUNION, or multi-megabyte values hurt all clients on that instance. Prefer SCAN for iteration.
  • CPU-heavy Lua scripts run atomically but monopolize the thread—keep scripts short.

When one instance is not enough, you scale with Redis Cluster (hash slots across nodes) or split read load with replicas—knowing replicas can lag and read-your-writes may require routing to the primary.

Memory, TTL, and eviction

Redis is bounded by RAM. Set maxmemory and an eviction policy (allkeys-lru, volatile-lru, noeviction, etc.). noeviction returns errors when full—sometimes safer than silently dropping cache keys you did not mark with TTL.

Use TTL (EXPIRE, SET ... EX 300) for cache entries and sessions. Monitor memory with INFO memory and hosted dashboards. Large values fragment memory and slow replication—keep values reasonably small.

Persistence: RDB vs AOF

  • RDB — point-in-time snapshots on a schedule. Compact, fast restarts; you can lose data since the last snapshot.
  • AOF — append-only log of writes. More durable (especially with appendfsync everysec); files grow until rewrite.

Many teams use both or rely on managed backups. Treat Redis as the fast layer; authoritative business data still belongs in a database you trust for recovery.

High availability: replicas, Sentinel, Cluster

  • Primary + replica — async replication; manual or automated failover.
  • Sentinel — monitors primaries, promotes a replica on failure, updates clients that support Sentinel discovery.
  • Cluster — sharding across masters with hash slots; clients must be cluster-aware.

Pick based on scale and ops maturity—not every side project needs Cluster on day one.

Security basics

  • Bind to private networks; use TLS and ACL users in production (ACL SETUSER).
  • Disable or rename dangerous commands (FLUSHALL, CONFIG) where policy allows.
  • Never expose port 6379 to the public internet without authentication—automated scanners abound.
  • Secrets in environment variables or secret stores, not in Git—same discipline as GitOps secrets hygiene.

Installing Redis and redis-cli

redis-cli ships with Redis. You can install the server locally for learning or only the CLI against a remote instance.

# macOS
brew install redis
brew services start redis   # optional: run as a service

# Ubuntu / Debian
sudo apt update && sudo apt install -y redis-server redis-tools

# Docker (quick lab)
docker run -d --name redis -p 6379:6379 redis:7-alpine

# Verify
redis-cli ping
# Expected: PONG

Connecting with redis-cli

# Default: localhost:6379
redis-cli

# Remote host + port
redis-cli -h redis.internal -p 6379

# With ACL user and password (Redis 6+)
redis-cli -h redis.internal -u redis://appuser:[email protected]:6379/0

# TLS (flags vary by build; check your distro docs)
redis-cli -h redis.example.com --tls --cert ./client.crt --key ./client.key --cacert ./ca.pem

Inside the CLI, commands are typed at the prompt. Many tutorials show 127.0.0.1:6379>—that is redis-cli’s interactive mode. End interactive mode with quit or Ctrl+D.

redis-cli essentials: strings and keys

SET greeting "Namaste"
GET greeting

SET counter 0
INCR counter
INCRBY counter 10

# Set with TTL (seconds)
SET session:token "abc" EX 3600
TTL session:token

EXISTS greeting
DEL greeting

# Inspect type before using list/set commands
TYPE counter

Hashes, lists, sets, sorted sets

# Hash — one key, many fields
HSET user:42 name "Babu" role "platform"
HGET user:42 name
HGETALL user:42

# List — queue / timeline
LPUSH tasks "send-email" "reindex-search"
LRANGE tasks 0 -1
RPOP tasks

# Set — unique members
SADD tags:post:7 "redis" "devops" "cache"
SMEMBERS tags:post:7
SISMEMBER tags:post:7 "redis"

# Sorted set — score + member
ZADD leaderboard 1500 "player1" 1200 "player2"
ZREVRANGE leaderboard 0 9 WITHSCORES
ZINCRBY leaderboard 50 "player2"

Streams (durable-ish queues)

XADD events * action "login" user_id "42"
XREAD COUNT 10 STREAMS events 0

# Consumer group (workers share load)
XGROUP CREATE events processors $ MKSTREAM
XREADGROUP GROUP processors worker1 COUNT 1 STREAMS events >
# After processing:
XACK events processors <message-id>

Streams fit event pipelines better than naive LPUSH/BRPOP when you need consumer groups and pending-entry lists.

Pub/Sub (ephemeral fan-out)

# Terminal 1
SUBSCRIBE notifications

# Terminal 2
PUBLISH notifications "deploy finished"

Subscribers only receive messages while connected—no backlog.

Inspection, debugging, and safe iteration

INFO server
INFO memory
INFO stats

DBSIZE

# Prefer SCAN over KEYS in production
SCAN 0 MATCH session:* COUNT 100

MONITOR          # streams all commands — dev only; crushes prod
SLOWLOG GET 10   # recent slow commands

CLIENT LIST
MEMORY USAGE mykey

MONITOR and KEYS * are invaluable in development and hazardous at scale. Default to SCAN, metrics, and application-level tracing.

One-shot mode and scripting

You do not have to enter interactive mode:

redis-cli SET deploy:version "2026.05.20"
redis-cli GET deploy:version

# Pipe commands
cat commands.txt | redis-cli --pipe

# Lua (atomic on the server)
redis-cli EVAL "return redis.call('GET', KEYS[1])" 1 mykey

From laptop to production checklist

  • Define what happens on cache miss and on Redis outage (degrade vs fail).
  • Set TTLs and key naming conventions; document invalidation on writes.
  • Configure maxmemory, eviction, persistence, and backups for your RPO/RTO.
  • Use connection pooling in apps; avoid opening a new TCP connection per HTTP request.
  • Alert on memory, replication lag, rejected connections, and evicted keys.
  • Load-test with realistic value sizes—micro-benchmarks lie.

How Redis fits next to your stack

If you run apps in containers, Redis is often a sidecar or a shared platform service—same network thinking as in Docker’s hidden side. If you operate Kubernetes, treat Redis like any stateful dependency: backups, upgrades, and capacity planning are platform work, not “the app team’s Redis problem.”

For cost and footprint awareness when Redis runs in the cloud, the FinOps series (Part 1) is a useful lens—memory-heavy instances are easy to leave running oversized.

Further reading

  • redis.io documentation — commands, data types, ACL, cluster
  • Redis University / OSS tutorials — structured labs
  • Martin Kleppmann — Designing Data-Intensive Applications (caching, replication, consistency trade-offs)

Blog index · Docker hidden side · Kubernetes architecture · GitOps principles

Back to blog list