Redis and Redis Streams Cheat Sheet - Quick Reference Guide

Learning Objectives: By the end of this lesson, you will be able to:

  • Quickly reference essential Redis commands and patterns
  • Find Redis Streams operations and consumer group commands
  • Access Go code snippets for common Redis operations
  • Troubleshoot issues using diagnostic commands
  • Implement best practices with ready-to-use code templates

1. Welcome and Introduction (5 minutes)

Icebreaker Activity

Have you ever been in the middle of coding and needed to quickly look up “What was that Redis command for getting all keys matching a pattern?” or “How do I claim pending messages in a consumer group?” Rather than searching through documentation or scrolling through old code, what if you had everything in one place?

This lesson is your Swiss Army knife for Redis developmentโ€”a comprehensive, organized reference that you can bookmark and return to whenever you need quick answers.

Lesson Overview

  • Duration: 60-90 minutes (reference material for ongoing use)
  • Prerequisites:
    • Basic understanding of Redis concepts
    • Familiarity with Go programming
    • Completed basic Redis lessons (recommended but not required)
  • Materials Needed:
    • Redis instance (for testing commands)
    • Go development environment
    • This cheat sheet (bookmark it!)

2. Learning Objectives and Session Outline (5 minutes)

What We’ll Cover Today:

  1. Core Redis Commands - Essential operations for all data types
  2. Redis Streams Quick Reference - Stream operations and consumer groups
  3. Go Code Snippets - Ready-to-use Golang examples with go-redis
  4. Common Patterns - Frequently used implementation patterns
  5. Troubleshooting Commands - Diagnostic and monitoring commands
  6. Performance Tips - Quick optimization guidelines

Success Criteria:

By the end of this session, you should be able to:

  • Quickly find the right command for any Redis operation
  • Copy and adapt code snippets for your projects
  • Debug Redis issues using diagnostic commands
  • Implement common patterns without starting from scratch

3. Foundation Knowledge (15-20 minutes)

Core Concepts

Command Syntax Conventions

Throughout this cheat sheet, we use these conventions:

  • UPPERCASE - Redis commands
  • <required> - Required parameter
  • [optional] - Optional parameter
  • key - A Redis key name
  • ... - Multiple values allowed

Redis CLI vs Go Code

For each operation, we provide both:

  1. Redis CLI command - For testing and debugging
  2. Go code - For application integration

Real-World Analogy: Think of this cheat sheet as a cookbook. Just as a cookbook gives you recipes you can follow and adapt, this guide provides tested patterns you can use and customize for your specific needs.

Why This Matters: Having quick access to correct syntax and patterns:

  • Reduces development time significantly
  • Prevents common mistakes
  • Ensures you’re using best practices
  • Provides working examples you can trust

Key Terminology

  • CLI Command: Command executed in redis-cli or terminal
  • TTL: Time To Live - expiration time for a key
  • NX/XX: Options for conditional operations (Not eXists / eXists)
  • Pipeline: Batch multiple commands to reduce network round trips
  • Transaction: Group of commands executed atomically
  • PEL: Pending Entries List in consumer groups

4. Detailed Explanation and Examples (25-30 minutes)

Redis Data Types Overview

Data Type Use Case Key Commands
String Caching, counters, flags GET, SET, INCR, DECR
List Queues, activity feeds, stacks LPUSH, RPUSH, LPOP, RPOP, LRANGE
Set Unique items, tags, relationships SADD, SMEMBERS, SINTER, SUNION
Hash Objects, user profiles HSET, HGET, HGETALL, HINCRBY
Sorted Set Leaderboards, ranking, time-series ZADD, ZRANGE, ZRANK, ZSCORE
Stream Event logs, message queues XADD, XREAD, XREADGROUP, XACK

5. Redis Commands Quick Reference (20-25 minutes)

๐Ÿ”‘ STRING Operations

Set and Get

# Redis CLI
SET key "value"                    # Set string value
SET key "value" EX 60             # Set with 60 seconds expiration
SET key "value" NX                # Set only if key doesn't exist
SET key "value" XX                # Set only if key exists
GET key                           # Get value
MGET key1 key2 key3              # Get multiple values
MSET key1 "val1" key2 "val2"     # Set multiple values
GETSET key "newvalue"             # Set new value, return old value
SETEX key 60 "value"              # Set with expiration (seconds)
PSETEX key 60000 "value"          # Set with expiration (milliseconds)
SETNX key "value"                 # Set if not exists
// Go Code
import (
    "context"
    "time"
    "github.com/redis/go-redis/v9"
)

ctx := context.Background()
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})

// Basic operations
err := rdb.Set(ctx, "key", "value", 0).Err()                    // No expiration
err = rdb.Set(ctx, "key", "value", 60*time.Second).Err()       // With expiration
val, err := rdb.Get(ctx, "key").Result()                        // Get value
vals, err := rdb.MGet(ctx, "key1", "key2").Result()            // Get multiple

// Conditional set
err = rdb.SetNX(ctx, "key", "value", 0).Err()                  // Set if not exists
err = rdb.SetXX(ctx, "key", "value", 0).Err()                  // Set if exists

// Get and set
oldVal, err := rdb.GetSet(ctx, "key", "newvalue").Result()     // Atomic get and set

Increment and Decrement

# Redis CLI
INCR counter                      # Increment by 1
INCRBY counter 5                  # Increment by 5
INCRBYFLOAT price 2.5            # Increment by float
DECR counter                      # Decrement by 1
DECRBY counter 3                  # Decrement by 3
// Go Code
val, err := rdb.Incr(ctx, "counter").Result()                  // Returns new value
val, err := rdb.IncrBy(ctx, "counter", 5).Result()
val, err := rdb.IncrByFloat(ctx, "price", 2.5).Result()
val, err := rdb.Decr(ctx, "counter").Result()
val, err := rdb.DecrBy(ctx, "counter", 3).Result()

๐Ÿ“ LIST Operations

# Redis CLI
LPUSH list "item1" "item2"        # Push to left (beginning)
RPUSH list "item1" "item2"        # Push to right (end)
LPOP list                          # Pop from left
RPOP list                          # Pop from right
BLPOP list 10                      # Blocking pop (10 sec timeout)
BRPOP list 10                      # Blocking pop from right
LRANGE list 0 -1                   # Get all elements (0 to end)
LRANGE list 0 9                    # Get first 10 elements
LLEN list                          # Get list length
LINDEX list 0                      # Get element at index
LSET list 0 "newvalue"            # Set element at index
LTRIM list 0 99                    # Keep only first 100 elements
LINSERT list BEFORE "pivot" "new"  # Insert before pivot
// Go Code
// Push operations
err := rdb.LPush(ctx, "list", "item1", "item2").Err()
err = rdb.RPush(ctx, "list", "item1", "item2").Err()

// Pop operations
val, err := rdb.LPop(ctx, "list").Result()
val, err = rdb.RPop(ctx, "list").Result()

// Blocking pop (waits for element)
vals, err := rdb.BLPop(ctx, 10*time.Second, "list").Result()

// Range and length
vals, err := rdb.LRange(ctx, "list", 0, -1).Result()  // Get all
length, err := rdb.LLen(ctx, "list").Result()

// Index operations
val, err := rdb.LIndex(ctx, "list", 0).Result()
err = rdb.LSet(ctx, "list", 0, "newvalue").Err()

// Trim (keep range)
err = rdb.LTrim(ctx, "list", 0, 99).Err()

๐ŸŽฏ SET Operations

# Redis CLI
SADD set "member1" "member2"      # Add members
SREM set "member1"                # Remove member
SMEMBERS set                      # Get all members
SISMEMBER set "member1"           # Check if member exists
SCARD set                         # Get set size
SPOP set                          # Remove and return random member
SRANDMEMBER set 3                 # Get 3 random members (no remove)

# Set operations
SINTER set1 set2                  # Intersection
SUNION set1 set2                  # Union
SDIFF set1 set2                   # Difference (in set1 but not set2)
SINTERSTORE dest set1 set2        # Store intersection result
// Go Code
// Add and remove
err := rdb.SAdd(ctx, "set", "member1", "member2").Err()
err = rdb.SRem(ctx, "set", "member1").Err()

// Query
members, err := rdb.SMembers(ctx, "set").Result()
exists, err := rdb.SIsMember(ctx, "set", "member1").Result()
count, err := rdb.SCard(ctx, "set").Result()

// Random operations
member, err := rdb.SPop(ctx, "set").Result()
members, err := rdb.SRandMemberN(ctx, "set", 3).Result()

// Set operations
inter, err := rdb.SInter(ctx, "set1", "set2").Result()
union, err := rdb.SUnion(ctx, "set1", "set2").Result()
diff, err := rdb.SDiff(ctx, "set1", "set2").Result()

๐Ÿ—‚๏ธ HASH Operations

# Redis CLI
HSET user:1 name "John" age "30"  # Set multiple fields
HGET user:1 name                   # Get single field
HMGET user:1 name age              # Get multiple fields
HGETALL user:1                     # Get all fields and values
HDEL user:1 age                    # Delete field
HEXISTS user:1 name                # Check if field exists
HKEYS user:1                       # Get all field names
HVALS user:1                       # Get all values
HLEN user:1                        # Get number of fields
HINCRBY user:1 age 1              # Increment field by integer
HINCRBYFLOAT user:1 balance 10.5  # Increment by float
HSETNX user:1 email "a@b.com"     # Set if field doesn't exist
// Go Code
// Set operations
err := rdb.HSet(ctx, "user:1", "name", "John", "age", 30).Err()
err = rdb.HSet(ctx, "user:1", map[string]interface{}{
    "name": "John",
    "age":  30,
}).Err()

// Get operations
val, err := rdb.HGet(ctx, "user:1", "name").Result()
vals, err := rdb.HMGet(ctx, "user:1", "name", "age").Result()
all, err := rdb.HGetAll(ctx, "user:1").Result()  // map[string]string

// Delete and exists
err = rdb.HDel(ctx, "user:1", "age").Err()
exists, err := rdb.HExists(ctx, "user:1", "name").Result()

// Keys and values
keys, err := rdb.HKeys(ctx, "user:1").Result()
vals, err := rdb.HVals(ctx, "user:1").Result()
count, err := rdb.HLen(ctx, "user:1").Result()

// Increment
newVal, err := rdb.HIncrBy(ctx, "user:1", "age", 1).Result()
newVal, err := rdb.HIncrByFloat(ctx, "user:1", "balance", 10.5).Result()

๐Ÿ† SORTED SET Operations

# Redis CLI
ZADD leaderboard 100 "player1"    # Add with score
ZADD leaderboard 200 "player2" 150 "player3"  # Add multiple
ZRANGE leaderboard 0 -1           # Get all (ascending)
ZRANGE leaderboard 0 9            # Get top 10
ZRANGE leaderboard 0 -1 WITHSCORES  # With scores
ZREVRANGE leaderboard 0 9         # Get top 10 (descending)
ZSCORE leaderboard "player1"      # Get score
ZRANK leaderboard "player1"       # Get rank (0-based, ascending)
ZREVRANK leaderboard "player1"    # Get rank (descending)
ZINCRBY leaderboard 10 "player1"  # Increment score
ZREM leaderboard "player1"        # Remove member
ZCARD leaderboard                 # Get size
ZCOUNT leaderboard 100 200        # Count members in score range

# Range by score
ZRANGEBYSCORE leaderboard 100 200           # Get members with score 100-200
ZRANGEBYSCORE leaderboard -inf +inf LIMIT 0 10  # Get first 10 (pagination)
// Go Code
// Add members
err := rdb.ZAdd(ctx, "leaderboard", redis.Z{Score: 100, Member: "player1"}).Err()
err = rdb.ZAdd(ctx, "leaderboard",
    redis.Z{Score: 200, Member: "player2"},
    redis.Z{Score: 150, Member: "player3"},
).Err()

// Get ranges
members, err := rdb.ZRange(ctx, "leaderboard", 0, -1).Result()  // Ascending
membersWithScores, err := rdb.ZRangeWithScores(ctx, "leaderboard", 0, -1).Result()
members, err = rdb.ZRevRange(ctx, "leaderboard", 0, 9).Result()  // Top 10

// Score and rank
score, err := rdb.ZScore(ctx, "leaderboard", "player1").Result()
rank, err := rdb.ZRank(ctx, "leaderboard", "player1").Result()
rank, err = rdb.ZRevRank(ctx, "leaderboard", "player1").Result()

// Increment and remove
newScore, err := rdb.ZIncrBy(ctx, "leaderboard", 10, "player1").Result()
err = rdb.ZRem(ctx, "leaderboard", "player1").Err()

// Counts
count, err := rdb.ZCard(ctx, "leaderboard").Result()
count, err = rdb.ZCount(ctx, "leaderboard", "100", "200").Result()

// Range by score
members, err = rdb.ZRangeByScore(ctx, "leaderboard", &redis.ZRangeBy{
    Min: "100",
    Max: "200",
}).Result()

6. Redis Streams Cheat Sheet (20-25 minutes)

๐ŸŒŠ Basic Stream Operations

# Redis CLI
# Add entry (auto-generated ID)
XADD stream * field1 "value1" field2 "value2"

# Add entry with specific ID
XADD stream 1609459200000-0 field1 "value1"

# Add with max length (trimming)
XADD stream MAXLEN 1000 * field1 "value1"
XADD stream MAXLEN ~ 1000 * field1 "value1"  # Approximate (faster)

# Read from beginning
XREAD COUNT 10 STREAMS stream 0

# Read from end (only new entries)
XREAD COUNT 10 STREAMS stream $

# Blocking read (wait for new entries)
XREAD BLOCK 5000 STREAMS stream $  # 5 second timeout

# Read range
XRANGE stream - +                   # All entries
XRANGE stream - + COUNT 10          # First 10 entries
XRANGE stream 1609459200000 +      # From specific timestamp

# Stream info
XLEN stream                         # Number of entries
XINFO STREAM stream                 # Detailed stream info

# Delete entries
XDEL stream 1609459200000-0        # Delete specific entry
XTRIM stream MAXLEN 1000           # Trim to 1000 entries
// Go Code
import (
    "context"
    "time"
    "github.com/redis/go-redis/v9"
)

ctx := context.Background()
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})

// Add entry with auto ID
id, err := rdb.XAdd(ctx, &redis.XAddArgs{
    Stream: "stream",
    Values: map[string]interface{}{
        "field1": "value1",
        "field2": "value2",
    },
}).Result()

// Add with trimming
id, err = rdb.XAdd(ctx, &redis.XAddArgs{
    Stream: "stream",
    MaxLen: 1000,      // Keep max 1000 entries
    Approx: true,      // Approximate trimming (faster)
    Values: map[string]interface{}{
        "field": "value",
    },
}).Result()

// Read entries
messages, err := rdb.XRead(ctx, &redis.XReadArgs{
    Streams: []string{"stream", "0"},  // stream name, start ID
    Count:   10,                        // max entries
    Block:   0,                         // 0 = no blocking
}).Result()

// Blocking read
messages, err = rdb.XRead(ctx, &redis.XReadArgs{
    Streams: []string{"stream", "$"},   // $ = only new entries
    Block:   5 * time.Second,           // wait up to 5 seconds
}).Result()

// Read range
messages, err := rdb.XRange(ctx, "stream", "-", "+").Result()
messages, err = rdb.XRangeN(ctx, "stream", "-", "+", 10).Result()

// Stream info
length, err := rdb.XLen(ctx, "stream").Result()

// Delete and trim
deleted, err := rdb.XDel(ctx, "stream", "1609459200000-0").Result()
trimmed, err := rdb.XTrimMaxLen(ctx, "stream", 1000).Result()

๐Ÿ‘ฅ Consumer Groups

# Redis CLI
# Create consumer group
XGROUP CREATE stream group 0       # Start from beginning
XGROUP CREATE stream group $       # Start from end
XGROUP CREATE stream group 0 MKSTREAM  # Create stream if not exists

# Delete consumer group
XGROUP DESTROY stream group

# Read as consumer group
XREADGROUP GROUP group consumer1 COUNT 1 STREAMS stream >

# Read pending messages for this consumer
XREADGROUP GROUP group consumer1 COUNT 10 STREAMS stream 0

# Acknowledge message
XACK stream group 1609459200000-0

# Get pending info
XPENDING stream group              # Summary
XPENDING stream group - + 10       # Detailed (first 10)
XPENDING stream group - + 10 consumer1  # For specific consumer

# Claim messages from other consumers
XCLAIM stream group consumer2 3600000 1609459200000-0  # Min idle 1 hour

# Auto-claim messages
XAUTOCLAIM stream group consumer2 3600000 0-0 COUNT 10

# Consumer group info
XINFO GROUPS stream                # All groups
XINFO CONSUMERS stream group       # Consumers in group
// Go Code
// Create consumer group
err := rdb.XGroupCreate(ctx, "stream", "group", "0").Err()
err = rdb.XGroupCreateMkStream(ctx, "stream", "group", "0").Err()  // With MKSTREAM

// Delete group
err = rdb.XGroupDestroy(ctx, "stream", "group").Err()

// Read as consumer group (new messages)
streams, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    Group:    "group",
    Consumer: "consumer1",
    Streams:  []string{"stream", ">"},  // > = undelivered messages
    Count:    1,
    Block:    2 * time.Second,
}).Result()

// Read pending messages
streams, err = rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    Group:    "group",
    Consumer: "consumer1",
    Streams:  []string{"stream", "0"},  // 0 = pending messages
    Count:    10,
}).Result()

// Process and acknowledge
for _, stream := range streams {
    for _, message := range stream.Messages {
        // Process message...
        
        // Acknowledge
        err = rdb.XAck(ctx, "stream", "group", message.ID).Err()
    }
}

// Get pending info
pending, err := rdb.XPending(ctx, "stream", "group").Result()

// Detailed pending info
pendingExt, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{
    Stream: "stream",
    Group:  "group",
    Start:  "-",
    End:    "+",
    Count:  10,
}).Result()

// Claim stale messages
claimed, err := rdb.XClaim(ctx, &redis.XClaimArgs{
    Stream:   "stream",
    Group:    "group",
    Consumer: "consumer2",
    MinIdle:  time.Hour,                     // Messages idle for 1+ hour
    Messages: []string{"1609459200000-0"},
}).Result()

// Auto-claim
msgs, _, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
    Stream:   "stream",
    Group:    "group",
    Consumer: "consumer2",
    MinIdle:  time.Hour,
    Start:    "0-0",
    Count:    10,
}).Result()

// Group info
groups, err := rdb.XInfoGroups(ctx, "stream").Result()
consumers, err := rdb.XInfoConsumers(ctx, "stream", "group").Result()

7. Common Patterns and Snippets (15-20 minutes)

Pattern 1: Caching with Expiration

// Cache-aside pattern
func GetUser(ctx context.Context, rdb *redis.Client, userID string) (*User, error) {
    cacheKey := fmt.Sprintf("user:%s", userID)
    
    // Try cache first
    cached, err := rdb.Get(ctx, cacheKey).Result()
    if err == nil {
        var user User
        json.Unmarshal([]byte(cached), &user)
        return &user, nil
    }
    
    // Cache miss - get from database
    user, err := getUserFromDB(userID)
    if err != nil {
        return nil, err
    }
    
    // Store in cache
    data, _ := json.Marshal(user)
    rdb.Set(ctx, cacheKey, data, 1*time.Hour)
    
    return user, nil
}

Pattern 2: Distributed Lock

func AcquireLock(ctx context.Context, rdb *redis.Client, lockKey string, ttl time.Duration) (bool, error) {
    // Try to set lock with NX (only if not exists)
    result, err := rdb.SetNX(ctx, lockKey, "locked", ttl).Result()
    return result, err
}

func ReleaseLock(ctx context.Context, rdb *redis.Client, lockKey string) error {
    return rdb.Del(ctx, lockKey).Err()
}

// Usage
lockKey := "lock:order:123"
acquired, _ := AcquireLock(ctx, rdb, lockKey, 10*time.Second)
if acquired {
    defer ReleaseLock(ctx, rdb, lockKey)
    // Critical section
    processOrder()
}

Pattern 3: Rate Limiting (Fixed Window)

func CheckRateLimit(ctx context.Context, rdb *redis.Client, userID string, limit int64, window time.Duration) (bool, error) {
    key := fmt.Sprintf("ratelimit:%s", userID)
    
    pipe := rdb.Pipeline()
    incr := pipe.Incr(ctx, key)
    pipe.Expire(ctx, key, window)
    _, err := pipe.Exec(ctx)
    
    if err != nil {
        return false, err
    }
    
    count := incr.Val()
    return count <= limit, nil
}

// Usage: 100 requests per minute
allowed, _ := CheckRateLimit(ctx, rdb, "user123", 100, 1*time.Minute)
if !allowed {
    return errors.New("rate limit exceeded")
}

Pattern 4: Leaderboard

// Add score
func AddScore(ctx context.Context, rdb *redis.Client, playerID string, score float64) error {
    return rdb.ZIncrBy(ctx, "leaderboard", score, playerID).Err()
}

// Get top N players
func GetTopPlayers(ctx context.Context, rdb *redis.Client, n int64) ([]redis.Z, error) {
    return rdb.ZRevRangeWithScores(ctx, "leaderboard", 0, n-1).Result()
}

// Get player rank
func GetPlayerRank(ctx context.Context, rdb *redis.Client, playerID string) (int64, error) {
    rank, err := rdb.ZRevRank(ctx, "leaderboard", playerID).Result()
    return rank + 1, err  // Convert 0-based to 1-based
}

Pattern 5: Stream Consumer with Retry

func ConsumeWithRetry(ctx context.Context, rdb *redis.Client, streamKey, group, consumer string) {
    maxRetries := 3
    
    for {
        // Check pending messages first
        pending, _ := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
            Group:    group,
            Consumer: consumer,
            Streams:  []string{streamKey, "0"},
            Count:    10,
        }).Result()
        
        for _, stream := range pending {
            for _, msg := range stream.Messages {
                retries := getRetryCount(msg)
                if retries >= maxRetries {
                    // Move to dead letter queue
                    moveToDeadLetter(ctx, rdb, msg)
                    rdb.XAck(ctx, streamKey, group, msg.ID)
                    continue
                }
                
                if processMessage(msg) {
                    rdb.XAck(ctx, streamKey, group, msg.ID)
                } else {
                    incrementRetryCount(ctx, rdb, msg)
                }
            }
        }
        
        // Read new messages
        streams, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
            Group:    group,
            Consumer: consumer,
            Streams:  []string{streamKey, ">"},
            Count:    1,
            Block:    2 * time.Second,
        }).Result()
        
        if err != nil {
            continue
        }
        
        for _, stream := range streams {
            for _, msg := range stream.Messages {
                if processMessage(msg) {
                    rdb.XAck(ctx, streamKey, group, msg.ID)
                }
            }
        }
    }
}

Pattern 6: Session Management

type Session struct {
    UserID    string
    Username  string
    Email     string
    ExpiresAt time.Time
}

func CreateSession(ctx context.Context, rdb *redis.Client, sessionID string, session *Session, ttl time.Duration) error {
    data, err := json.Marshal(session)
    if err != nil {
        return err
    }
    return rdb.Set(ctx, fmt.Sprintf("session:%s", sessionID), data, ttl).Err()
}

func GetSession(ctx context.Context, rdb *redis.Client, sessionID string) (*Session, error) {
    data, err := rdb.Get(ctx, fmt.Sprintf("session:%s", sessionID)).Result()
    if err != nil {
        return nil, err
    }
    
    var session Session
    err = json.Unmarshal([]byte(data), &session)
    return &session, err
}

func RefreshSession(ctx context.Context, rdb *redis.Client, sessionID string, ttl time.Duration) error {
    return rdb.Expire(ctx, fmt.Sprintf("session:%s", sessionID), ttl).Err()
}

8. Key Management and Utilities (10 minutes)

General Key Operations

# Redis CLI
KEYS pattern                       # Find keys (SLOW - don't use in production)
SCAN 0 MATCH user:* COUNT 100     # Iterate keys (SAFE for production)
EXISTS key                         # Check if key exists
DEL key1 key2                      # Delete keys
EXPIRE key 60                      # Set expiration (seconds)
EXPIREAT key 1609459200           # Expire at timestamp
TTL key                            # Get remaining TTL
PERSIST key                        # Remove expiration
TYPE key                           # Get key type
RENAME key newkey                  # Rename key
DUMP key                           # Serialize value
RESTORE key 0 <serialized>        # Restore value
// Go Code
// Find keys (use SCAN in production)
keys, cursor, err := rdb.Scan(ctx, 0, "user:*", 100).Result()

// Key operations
exists, err := rdb.Exists(ctx, "key").Result()
deleted, err := rdb.Del(ctx, "key1", "key2").Result()

// Expiration
err = rdb.Expire(ctx, "key", 60*time.Second).Err()
err = rdb.ExpireAt(ctx, "key", time.Now().Add(1*time.Hour)).Err()
ttl, err := rdb.TTL(ctx, "key").Result()
err = rdb.Persist(ctx, "key").Err()  // Remove expiration

// Type and rename
keyType, err := rdb.Type(ctx, "key").Result()
err = rdb.Rename(ctx, "oldkey", "newkey").Err()

9. Transactions and Pipelines (10 minutes)

Transactions (MULTI/EXEC)

# Redis CLI
MULTI                              # Start transaction
SET key1 "value1"
INCR counter
EXEC                              # Execute all commands
// Go Code using TxPipeline
pipe := rdb.TxPipeline()
pipe.Set(ctx, "key1", "value1", 0)
pipe.Incr(ctx, "counter")
_, err := pipe.Exec(ctx)

// With WATCH (optimistic locking)
err := rdb.Watch(ctx, func(tx *redis.Tx) error {
    // Get current value
    val, err := tx.Get(ctx, "key").Int()
    if err != nil && err != redis.Nil {
        return err
    }
    
    // Transaction
    _, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
        pipe.Set(ctx, "key", val+1, 0)
        return nil
    })
    return err
}, "key")

Pipelines (Batching)

// Go Code - Pipeline for performance
pipe := rdb.Pipeline()

// Queue multiple commands
for i := 0; i < 100; i++ {
    pipe.Set(ctx, fmt.Sprintf("key:%d", i), i, 0)
}

// Execute all at once
cmds, err := pipe.Exec(ctx)

// Access individual results
for _, cmd := range cmds {
    if cmd.Err() != nil {
        // Handle error
    }
}

10. Monitoring and Debugging Commands (10 minutes)

System Information

# Redis CLI
INFO                               # All info
INFO server                        # Server info
INFO memory                        # Memory usage
INFO stats                         # Statistics
INFO replication                   # Replication status
INFO clients                       # Client connections

MONITOR                           # Watch commands in real-time (SLOW)
SLOWLOG GET 10                    # Get slow queries
CONFIG GET maxmemory              # Get config value
CONFIG SET maxmemory 1gb          # Set config value

CLIENT LIST                       # List connected clients
CLIENT KILL ip:port               # Kill client connection

DBSIZE                            # Number of keys
FLUSHDB                           # Delete all keys in current DB
FLUSHALL                          # Delete all keys in all DBs

MEMORY USAGE key                  # Memory used by key
MEMORY DOCTOR                     # Memory analysis
// Go Code
// Get info
info, err := rdb.Info(ctx).Result()
memInfo, err := rdb.Info(ctx, "memory").Result()

// Database size
size, err := rdb.DBSize(ctx).Result()

// Memory usage for specific key
usage, err := rdb.MemoryUsage(ctx, "key").Result()

// Client list
clients, err := rdb.ClientList(ctx).Result()

Debugging Streams

# Redis CLI
XINFO STREAM mystream             # Stream details
XINFO STREAM mystream FULL        # Full details with entries
XINFO GROUPS mystream             # Consumer groups
XINFO CONSUMERS mystream mygroup  # Consumers in group

# Check specific consumer pending
XPENDING mystream mygroup - + 10 myconsumer
// Go Code
// Stream info
info, err := rdb.XInfoStream(ctx, "mystream").Result()
groups, err := rdb.XInfoGroups(ctx, "mystream").Result()
consumers, err := rdb.XInfoConsumers(ctx, "mystream", "mygroup").Result()

// Detailed pending
pending, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{
    Stream:   "mystream",
    Group:    "mygroup",
    Consumer: "myconsumer",
    Start:    "-",
    End:      "+",
    Count:    10,
}).Result()

// Analyze pending messages
for _, msg := range pending {
    fmt.Printf("ID: %s, Consumer: %s, Idle: %v, Retries: %d\n",
        msg.ID, msg.Consumer, msg.Idle, msg.RetryCount)
}

11. Performance Best Practices (5 minutes)

Quick Tips

Practice Why Example
Use pipelines for bulk operations Reduces network round trips Batch 100 SETs in one pipeline
Use SCAN instead of KEYS Non-blocking iteration SCAN 0 MATCH user:*
Set appropriate TTLs Automatic memory management SET key val EX 3600
Use connection pooling Reuse connections efficiently go-redis handles this
Avoid large keys Better memory distribution Split large hashes
Use hashes for objects More memory efficient HSET instead of multiple keys
Monitor slow queries Identify bottlenecks SLOWLOG GET 10
Use approximate trimming Faster stream trimming MAXLEN ~ 1000

Connection Pool Configuration

// Go Code - Optimized connection pool
rdb := redis.NewClient(&redis.Options{
    Addr:         "localhost:6379",
    Password:     "",
    DB:           0,
    PoolSize:     10,              // Max connections in pool
    MinIdleConns: 5,               // Min idle connections
    MaxRetries:   3,               // Retry failed commands
    DialTimeout:  5 * time.Second,
    ReadTimeout:  3 * time.Second,
    WriteTimeout: 3 * time.Second,
    PoolTimeout:  4 * time.Second,
})

12. Error Handling (5 minutes)

Common Errors

// Go Code - Proper error handling
import "github.com/redis/go-redis/v9"

// Key not found
val, err := rdb.Get(ctx, "key").Result()
if err == redis.Nil {
    // Key doesn't exist
    fmt.Println("Key not found")
} else if err != nil {
    // Other error
    log.Fatal(err)
} else {
    // Success
    fmt.Println("Value:", val)
}

// Check if consumer group exists
err := rdb.XGroupCreate(ctx, "stream", "group", "0").Err()
if err != nil {
    if strings.Contains(err.Error(), "BUSYGROUP") {
        // Group already exists - this is OK
        fmt.Println("Group exists")
    } else {
        // Real error
        log.Fatal(err)
    }
}

// Timeout handling
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

err := rdb.Set(ctx, "key", "value", 0).Err()
if err == context.DeadlineExceeded {
    fmt.Println("Operation timed out")
}

13. Summary and Review (5 minutes)

Key Takeaways

  1. Bookmark This Cheat Sheet - Keep it accessible for quick reference during development
  2. Use the Right Data Structure - Choose based on access patterns and use case
  3. Leverage Pipelines - Batch operations for better performance
  4. Monitor and Debug - Use INFO, SLOWLOG, and XINFO commands regularly
  5. Handle Errors Properly - Check for redis.Nil and other specific errors
  6. Use Consumer Groups - For reliable distributed message processing
  7. Set Appropriate TTLs - Prevent memory bloat with automatic expiration

Quick Command Reference Table

Need Redis CLI Go Method
Set value SET key val rdb.Set(ctx, key, val, 0)
Get value GET key rdb.Get(ctx, key).Result()
Add to list LPUSH list val rdb.LPush(ctx, list, val)
Add to stream XADD stream * k v rdb.XAdd(ctx, &XAddArgs{...})
Read stream (group) XREADGROUP ... rdb.XReadGroup(ctx, &XReadGroupArgs{...})
Acknowledge XACK stream group id rdb.XAck(ctx, stream, group, id)
Check pending XPENDING stream group rdb.XPending(ctx, stream, group)

Next Steps

  1. Practice Regularly - Try commands in redis-cli before implementing in Go
  2. Build Projects - Apply these patterns to real applications
  3. Monitor Production - Use the debugging commands to troubleshoot issues
  4. Explore Advanced Topics - Redis Cluster, Lua scripting, Redis Modules
  5. Stay Updated - Follow Redis releases for new features

For Instructors

Teaching Tips

  • Encourage students to keep this cheat sheet open while coding
  • Demonstrate live lookups using this reference
  • Have students create their own “most used” subset
  • Use this as a starting point for code reviews
  • Reference specific sections during other lessons

Common Student Questions

  • “Should I memorize all these commands?” - No, understanding patterns and knowing where to look is more important
  • “Which data structure should I use?” - Start with the access pattern, then choose the structure
  • “How do I know if I’m using Redis correctly?” - Monitor performance, check for anti-patterns in this guide
  • “What if I can’t find a command here?” - Check official Redis documentation for comprehensive reference

Assessment Ideas

Quick Quiz:

  1. What command reads new messages from a consumer group?
  2. How do you set a key with 60-second expiration?
  3. What’s the difference between LPUSH and RPUSH?
  4. How do you claim stale messages from another consumer?

Practical Exercise:

  • Given a use case, identify the appropriate data structure and commands
  • Debug a provided code snippet using the troubleshooting section
  • Optimize a slow query using pipeline or better commands

Code Challenge:

  • Implement a rate limiter using only this cheat sheet
  • Build a simple consumer with retry logic
  • Create a leaderboard with top scores and player ranks

Adaptation Notes

For Beginners:

  • Start with basic string operations
  • Focus on one data structure at a time
  • Use simple, single-command examples

For Advanced Students:

  • Emphasize optimization patterns
  • Discuss trade-offs between different approaches
  • Explore edge cases and error handling

Time Management:

  • Quick review: 30 minutes (overview only)
  • Standard session: 60 minutes (with practice)
  • Comprehensive: 90 minutes (with exercises)

Updates:

  • Keep this cheat sheet updated with new Redis versions
  • Add new patterns as they’re discovered in projects
  • Include team-specific conventions or wrappers