Steven's Knowledge

Getting Started

Run Redis with Docker, learn the core commands, integrate from a Node app

Getting Started

Redis takes about 30 seconds to stand up and another minute to start using productively. This page does both.

Bring It Up

# docker-compose.yml
services:
  redis:
    image: redis:7-alpine
    ports: ["6379:6379"]
    command: redis-server --save 60 1 --loglevel warning
    volumes:
      - redis-data:/data

  # Optional: a web UI
  redis-insight:
    image: redis/redisinsight:latest
    ports: ["5540:5540"]
    depends_on: [redis]

volumes:
  redis-data:
docker compose up -d
docker compose exec redis redis-cli ping
# PONG

Core Commands

Redis is a key-value store with rich value types. The basics:

Strings — the everyday case

SET   user:42:name  "alice"
GET   user:42:name                      # "alice"
SETEX session:abc   3600  "<token>"     # set with TTL of 3600s
EXPIRE user:42:name 60                  # add TTL to existing key
TTL   user:42:name                      # seconds remaining (-1 = no TTL, -2 = missing)
DEL   user:42:name
EXISTS user:42:name                     # 0 or 1

INCR  counter:visits                    # atomic increment; returns new value
INCRBY counter:visits 5

Hashes — like a row

HSET  user:42 name "alice" email "alice@example.com" tier "pro"
HGET  user:42 name                      # "alice"
HGETALL user:42                         # all fields
HINCRBY user:42 visits 1

Use hashes for objects with multiple fields — saves space vs many individual string keys.

Lists — queues and stacks

LPUSH queue:jobs "{...job json...}"
RPOP  queue:jobs                        # pop from the other end → FIFO queue
LLEN  queue:jobs                        # how many?

BRPOP queue:jobs 30                     # blocking pop, 30s timeout

Sets — unique membership

SADD  flagged:users  user-42  user-99
SISMEMBER flagged:users user-42         # 1 or 0
SCARD flagged:users                     # cardinality
SREM  flagged:users user-99

Sorted Sets — leaderboards, time-ordered queues

ZADD  leaderboard:weekly  1500 alice  1200 bob  890 carol
ZREVRANGE leaderboard:weekly 0 9 WITHSCORES    # top 10
ZSCORE leaderboard:weekly alice         # 1500
ZINCRBY leaderboard:weekly 50 alice

Sorted sets are the secret weapon — schedulers, rate limiters, time-series buffers, leaderboards.

Keys, TTL, and Memory

KEYS user:*                             # NEVER in production — blocks
SCAN 0 MATCH user:* COUNT 100           # cursored, safe alternative

DBSIZE
INFO memory
MEMORY USAGE user:42

KEYS * scans the entire keyspace and blocks the server — fine for dev, dangerous in production. Always use SCAN.

Integrate from Node

npm install ioredis
// app.js
const Redis = require('ioredis');
const redis = new Redis({ host: 'localhost', port: 6379 });

// Cache-aside pattern
async function getUserProfile(userId) {
  const cacheKey = `user:profile:${userId}`;

  // 1. Try cache
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached);

  // 2. Miss → fetch from DB
  const profile = await db.users.findById(userId);
  if (!profile) return null;

  // 3. Populate cache with TTL
  await redis.set(cacheKey, JSON.stringify(profile), 'EX', 300);  // 5 min
  return profile;
}

// Invalidate on write
async function updateUserProfile(userId, changes) {
  await db.users.update(userId, changes);
  await redis.del(`user:profile:${userId}`);
}

That's cache-aside, by far the most common pattern. The next page covers the rest.

Pipelining and Batching

Network round trips dominate Redis latency at scale. Pipeline:

// Bad: 100 round trips
for (const id of userIds) {
  await redis.get(`user:${id}`);
}

// Good: 1 round trip
const pipeline = redis.pipeline();
for (const id of userIds) pipeline.get(`user:${id}`);
const results = await pipeline.exec();

For multi-key atomicity, use MULTI/EXEC or MGET/MSET.

Pub/Sub (Quick Tour)

Redis can also broker messages:

// Subscriber
const sub = new Redis();
await sub.subscribe('events');
sub.on('message', (channel, msg) => console.log(channel, msg));

// Publisher (in another process)
const pub = new Redis();
await pub.publish('events', 'user logged in');

Pub/Sub is fire-and-forget — no persistence. For durable queues use Streams (XADD / XREAD / XGROUP) or a real queue from Message Queues.

Tear Down

docker compose down -v

What's Next

You can put data in Redis and get it back. The non-trivial part is using cache well:

  • Patterns — cache-aside, read/write-through, TTL strategies, invalidation, stampede prevention
  • Best Practices — HA, sizing, eviction, persistence, pitfalls

On this page