Redis โ Complete Beginner Tutorial
6 modules ยท 18 examples ยท Click โถ Run on any example to see the output
๐ก How to use this page
Read each command, study the output, then hit โถ Run to see the expected response.
Strings & Key Basics
Every piece of data in Redis is stored under a key. Keys are strings. Values can be any of Redis's data types, but the simplest is also a string. Strings store text, numbers, JSON blobs, binary data โ anything up to 512MB. Commands are case-insensitive but conventionally written in uppercase.
SET and GET โ Storing and Retrieving Strings
SET stores a value under a key. GET retrieves it. If the key does not exist, GET returns nil. MSET and MGET work on multiple keys at once. SET with NX only sets if the key does not already exist (useful for locks).
# โโ Basic SET and GET โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Store a simple string
SET greeting "Hello, Redis!"
# Retrieve it
GET greeting
# Store a number as a string
SET score 95
GET score
# SET overwrites existing values
SET greeting "Hello, World!"
GET greeting
# โโ Multiple keys at once โโโโโโโโโโโโโโโโโโโโโโโโโ
# MSET โ set multiple keys in one command
MSET name "Alice" age "20" city "Cape Town"
# MGET โ get multiple keys at once
MGET name age city
# โโ Conditional SET โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# NX โ only set if key does NOT exist
SET lock:resource1 "locked" NX
SET lock:resource1 "locked2" NX # fails โ already exists!
GET lock:resource1 # still "locked"
# XX โ only set if key DOES exist
SET greeting "Hi there!" XX # succeeds โ key exists
SET newkey "value" XX # fails โ key doesn't exist
GET newkey # nilRedis key naming convention uses colons as separators for namespacing: user:1:name, session:abc123, rate:192.168.1.1. This is just a convention โ Redis treats the colon like any other character โ but it makes keys readable and organises your keyspace logically.
Key Management โ EXISTS, DEL, TYPE, RENAME, KEYS
Redis has many commands for managing keys themselves. EXISTS checks if a key is present. DEL removes one or more keys. TYPE tells you what data structure a key holds. KEYS lists keys matching a pattern. RENAME renames a key.
# Store some data first
SET username "alice"
SET score 95
HSET user:1 name "Alice" age 20
# โโ EXISTS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
EXISTS username # 1 (yes)
EXISTS missing_key # 0 (no)
EXISTS username score # 2 (both exist)
# โโ TYPE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
TYPE username # string
TYPE score # string
TYPE user:1 # hash
# โโ RENAME โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
RENAME username login_name
GET login_name # "alice"
GET username # nil (old key gone)
# โโ DEL โ delete one or many keys โโโโโโโโโโโโโโโโ
DEL score # 1 (deleted 1 key)
DEL login_name user:1 # 2 (deleted 2 keys)
EXISTS login_name # 0 (gone)
# โโ KEYS โ find keys by pattern โโโโโโโโโโโโโโโโโโโ
# (Avoid KEYS in production on large databases!)
SET session:abc "user1"
SET session:xyz "user2"
SET cache:home "html"
SET cache:about "html"
KEYS session:* # all session keys
KEYS cache:* # all cache keys
KEYS * # ALL keys (dangerous on prod!)Never use KEYS * in production on a large Redis database. It blocks the server while scanning every key โ potentially for seconds on a database with millions of keys. Use SCAN instead for production: SCAN 0 MATCH session:* COUNT 100 โ it iterates in batches without blocking.
GETSET, APPEND, STRLEN and GETRANGE
Redis strings have additional manipulation commands. GETDEL returns and deletes in one atomic operation. APPEND adds to the end of a string. STRLEN returns the length in bytes. GETRANGE returns a substring.
# โโ GETDEL โ get and delete atomically โโโโโโโโโโโ
SET temp_token "abc123xyz"
GETDEL temp_token # returns "abc123xyz" and deletes
GET temp_token # nil (deleted)
# โโ GETEX โ get and set expiry โโโโโโโโโโโโโโโโโโโโ
SET session:user1 "active"
GETEX session:user1 EX 300 # get and set 300s expiry
TTL session:user1 # 299 (approximately)
# โโ APPEND โ concatenate strings โโโโโโโโโโโโโโโโโ
SET log ""
APPEND log "2026-04-23 " # returns new length: 11
APPEND log "Server started" # returns new length: 25
APPEND log "
"
GET log
# โโ STRLEN โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SET message "Hello, Redis!"
STRLEN message # 13
# โโ GETRANGE โ substring โโโโโโโโโโโโโโโโโโโโโโโโโโ
SET greeting "Hello, World!"
GETRANGE greeting 0 4 # "Hello"
GETRANGE greeting 7 11 # "World"
GETRANGE greeting 0 -1 # full string
# โโ SETRANGE โ overwrite part of string โโโโโโโโโโโ
SETRANGE greeting 7 "Redis"
GET greeting # "Hello, Redis!"APPEND is commonly used for building time-series logs or accumulating data before flushing to a persistent store. But remember: Redis strings can grow to 512MB, so always have a plan for clearing them. Set an expiry or explicitly delete when done.
Numbers & Expiry
Redis INCR and DECR commands are atomic โ even under high concurrency, two clients incrementing the same counter will never get the same number. This makes Redis perfect for counters, rate limiters, and unique ID generators. Expiry lets keys automatically delete themselves after a set time.
Atomic Counters โ INCR, DECR, INCRBY
INCR increments a string value as if it were an integer by 1. DECR decrements by 1. INCRBY and DECRBY add/subtract a specific amount. INCRBYFLOAT works with decimal numbers. These operations are atomic โ safe for concurrent use without any locking.
# โโ Basic increment / decrement โโโโโโโโโโโโโโโโโโ
# INCR creates key with 0 then increments to 1
INCR page_views # 1
INCR page_views # 2
INCR page_views # 3
GET page_views # "3"
# DECR decrements
SET stock 100
DECR stock # 99
DECR stock # 98
GET stock # "98"
# โโ INCRBY and DECRBY โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SET score 0
INCRBY score 10 # 10
INCRBY score 25 # 35
DECRBY score 5 # 30
GET score # "30"
# โโ INCRBYFLOAT โ for decimal numbers โโโโโโโโโโโโ
SET price 9.99
INCRBYFLOAT price 2.50 # 12.49
INCRBYFLOAT price -1.00 # 11.49
GET price # "11.49"
# โโ Real pattern: unique ID generator โโโโโโโโโโโโโ
# Every call returns the next unique ID
INCR global:user_id # 1
INCR global:user_id # 2
INCR global:user_id # 3INCR is atomic โ no two clients can get the same value, even with thousands of concurrent requests. This is why it's used for unique ID generators, rate limiters, and view counters. In any other language you'd need a lock; in Redis, INCR does it for you.
Key Expiry โ EXPIRE, TTL, PERSIST, SETEX
Expiry is one of Redis's most powerful features. Set a key to auto-delete after a number of seconds with EXPIRE or inline with SET ... EX. TTL shows remaining seconds. PERSIST removes the expiry. PEXPIRE and PTTL work in milliseconds.
# โโ SET with expiry inline โโโโโโโโโโโโโโโโโโโโโโโ
# Set and expire in one command (most common)
SET session:user123 '{"userId":1}' EX 1800 # 30 minutes
TTL session:user123 # 1799 (approximately)
# โโ EXPIRE on an existing key โโโโโโโโโโโโโโโโโโโโโ
SET cache:homepage "<html>...</html>"
EXPIRE cache:homepage 300 # expire in 5 minutes
TTL cache:homepage # 300
# Wait a moment...
TTL cache:homepage # 299 (counting down)
# โโ PERSIST โ remove the expiry โโโโโโโโโโโโโโโโโโโ
PERSIST cache:homepage
TTL cache:homepage # -1 (never expires now)
# โโ EXPIREAT โ expire at a Unix timestamp โโโโโโโโโ
# Expire at exactly 1800000000 Unix time
EXPIREAT promo:summer 1800000000
# โโ TTL return values โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SET permanent_key "value"
TTL permanent_key # -1 (no expiry set)
TTL missing_key # -2 (key does not exist)
# โโ PEXPIRE โ millisecond precision โโโโโโโโโโโโโโโ
SET quick_cache "data"
PEXPIRE quick_cache 5000 # expire in 5000ms = 5 seconds
PTTL quick_cache # remaining millisecondsTTL returning -1 means the key exists but has no expiry. TTL returning -2 means the key does not exist at all. This distinction is important when checking cache state: -2 means "cache miss, fetch from DB", -1 means "this was deliberately set as permanent".
Rate Limiting Pattern with Counters
One of the most powerful Redis patterns. Combine INCR with EXPIRE to build a sliding or fixed-window rate limiter. This approach is atomic and handles thousands of requests per second without any database queries.
# โโ Fixed-window rate limiter โโโโโโโโโโโโโโโโโโโโ
# Allow 100 requests per minute per IP address
# Simulate client requests from 192.168.1.1
INCR rate:192.168.1.1 # 1 (first request)
EXPIRE rate:192.168.1.1 60 # set 60-second window
INCR rate:192.168.1.1 # 2
INCR rate:192.168.1.1 # 3
# ... (keep going)
INCR rate:192.168.1.1 # 100
INCR rate:192.168.1.1 # 101 โ block this request!
TTL rate:192.168.1.1 # seconds until window resets
# โโ Page view counter with daily reset โโโโโโโโโโโ
# Use date in the key for automatic daily grouping
SET views:2026-04-23 0
INCR views:2026-04-23 # 1
INCR views:2026-04-23 # 2
INCRBY views:2026-04-23 50
GET views:2026-04-23 # "52"
# Set to expire at midnight
EXPIREAT views:2026-04-23 1745452800 # midnight timestamp
# โโ Multiple counters โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
INCR stats:api:GET:/users
INCR stats:api:GET:/users
INCR stats:api:POST:/users
MGET stats:api:GET:/users stats:api:POST:/usersThe INCR + EXPIRE pattern has a small race condition: if the server crashes after INCR but before EXPIRE, the key stays forever. The fix is to check with IF: use INCR and then only EXPIRE if the result is 1 (first request). This ensures EXPIRE is always set on the first increment.
Hashes
A Redis Hash is like a dictionary inside a key โ it stores multiple field-value pairs under one key. This is perfect for representing objects like user profiles, product records, or configuration settings. Hashes are more memory-efficient than storing each field as a separate key.
HSET, HGET, HGETALL, HMSET
HSET creates or updates fields in a hash. HGET retrieves one field. HGETALL returns all fields and values. HMGET returns multiple fields at once. The modern HSET can set multiple fields in one command (HMSET is now deprecated).
# โโ Create a user hash โโโโโโโโโโโโโโโโโโโโโโโโโโโ
# HSET key field value [field value ...]
HSET user:1 name "Alice" age 20 city "Cape Town" grade "A" score 95
# โโ Retrieve fields โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
HGET user:1 name # single field
HGET user:1 age
HGET user:1 missing # nil (field doesn't exist)
# โโ HGETALL โ all fields and values โโโโโโโโโโโโโโโ
HGETALL user:1
# โโ HMGET โ multiple specific fields โโโโโโโโโโโโโ
HMGET user:1 name grade score
# โโ Add and update fields โโโโโโโโโโโโโโโโโโโโโโโโโ
HSET user:1 email "alice@example.com" # add new field
HSET user:1 score 98 # update existing
HGETALL user:1
# โโ HSETNX โ only set if field doesn't exist โโโโโ
HSETNX user:1 name "Bob" # 0 (failed โ name exists)
HSETNX user:1 phone "083" # 1 (success โ new field)
HGET user:1 name # still "Alice"
# โโ HEXISTS โ check if field exists โโโโโโโโโโโโโโโ
HEXISTS user:1 email # 1 (yes)
HEXISTS user:1 address # 0 (no)Storing a user as a hash (HSET user:1 name "Alice" age 20) is far more efficient than storing separate keys (SET user:1:name "Alice", SET user:1:age "20"). For 1 million users, the hash approach uses ~10x less memory. Always model objects as hashes, not separate keys.
HDEL, HKEYS, HVALS, HLEN, HINCRBY
Redis provides commands to inspect and modify hash structure. HKEYS lists all field names. HVALS lists all values. HLEN counts fields. HDEL removes fields. HINCRBY atomically increments a numeric hash field.
# Setup
HSET product:42 name "Laptop" price 12999 stock 5 category "Electronics"
# โโ Inspect the hash โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
HKEYS product:42 # all field names
HVALS product:42 # all values
HLEN product:42 # number of fields
# โโ HDEL โ remove a field โโโโโโโโโโโโโโโโโโโโโโโโโ
HDEL product:42 category # 1 (removed)
HEXISTS product:42 category # 0 (gone)
HLEN product:42 # 3 fields left
# โโ HINCRBY โ atomic numeric increment โโโโโโโโโโโ
HINCRBY product:42 stock -2 # sold 2: stock = 3
HINCRBY product:42 price 1000 # price increase: 13999
HINCRBY product:42 stock -3 # sold 3: stock = 0
HGET product:42 stock # "0"
# โโ HINCRBYFLOAT โ for decimal values โโโโโโโโโโโโโ
HSET account:1 balance 1000.00
HINCRBYFLOAT account:1 balance 99.99 # deposit
HINCRBYFLOAT account:1 balance -25.50 # withdrawal
HGET account:1 balance
# โโ Pattern: session storage โโโโโโโโโโโโโโโโโโโโโโ
HSET session:abc123 userId 1 role "admin" loginAt "2026-04-23T10:00:00Z"
EXPIRE session:abc123 1800 # 30-minute session
HGETALL session:abc123
TTL session:abc123HINCRBY on stock is a perfect atomic "decrement and check" pattern. In SQL you'd write UPDATE products SET stock = stock - 1 WHERE id = 42 AND stock > 0. In Redis, use HINCRBY then check if the result went negative โ if so, HINCRBY back to undo it.
Lists
A Redis List is a linked list of strings, ordered by insertion. You can push to either end and pop from either end in O(1) time. This makes Redis Lists perfect for queues (push right, pop left), stacks (push right, pop right), and keeping the last N items of a feed.
LPUSH, RPUSH, LRANGE, LLEN
LPUSH pushes to the left (front). RPUSH pushes to the right (end). LRANGE retrieves a range by index โ 0 is the first element, -1 is the last. LLEN returns the number of elements.
# โโ LPUSH โ push to the front โโโโโโโโโโโโโโโโโโโโ
LPUSH notifications "New message from Bob"
LPUSH notifications "You have a new follower"
LPUSH notifications "Your post got 10 likes"
# Latest notification is first (index 0)
LRANGE notifications 0 -1
# โโ RPUSH โ push to the back โโโโโโโโโโโโโโโโโโโโโโ
RPUSH queue:emails "welcome@user1.com"
RPUSH queue:emails "promo@user2.com"
RPUSH queue:emails "receipt@user3.com"
LRANGE queue:emails 0 -1
# โโ LLEN โ count elements โโโโโโโโโโโโโโโโโโโโโโโโโ
LLEN notifications # 3
LLEN queue:emails # 3
# โโ LRANGE with index ranges โโโโโโโโโโโโโโโโโโโโโโ
RPUSH fruits apple banana cherry date elderberry
LRANGE fruits 0 2 # first 3 items
LRANGE fruits -2 -1 # last 2 items
LRANGE fruits 1 3 # items at index 1, 2, 3
# โโ LINDEX โ get item at specific index โโโโโโโโโโโ
LINDEX fruits 0 # "apple" (first)
LINDEX fruits -1 # "elderberry" (last)
LINDEX fruits 2 # "cherry"LRANGE 0 -1 gets ALL elements. LRANGE 0 9 gets the first 10. To keep only the last 100 items in a list (like a capped activity feed), use LTRIM after each insert: RPUSH feed item; LTRIM feed -100 -1. This trims the list to the last 100 elements automatically.
LPOP, RPOP โ Queues and Stacks
LPOP removes and returns from the front. RPOP removes and returns from the back. Together these implement queues (RPUSH + LPOP) and stacks (RPUSH + RPOP). BLPOP is the blocking version โ it waits for data to arrive if the list is empty.
# โโ Queue pattern: RPUSH to enqueue, LPOP to dequeue
RPUSH job:queue "send-welcome-email user:1"
RPUSH job:queue "resize-image img:42"
RPUSH job:queue "send-invoice order:99"
LLEN job:queue # 3 jobs waiting
# Worker processes jobs one by one
LPOP job:queue # "send-welcome-email user:1" โ process this
LPOP job:queue # "resize-image img:42"
LPOP job:queue # "send-invoice order:99"
LPOP job:queue # nil (queue empty)
# โโ Stack pattern: RPUSH to push, RPOP to pop โโโโโ
RPUSH history "visited /home"
RPUSH history "visited /products"
RPUSH history "visited /cart"
RPUSH history "visited /checkout"
RPOP history # "visited /checkout" (most recent)
RPOP history # "visited /cart"
RPOP history # "visited /products"
# โโ LMPOP โ pop multiple elements at once โโโโโโโโโ
RPUSH batch:work "task1" "task2" "task3" "task4" "task5"
LMPOP 1 batch:work LEFT COUNT 3 # pop 3 from left
# โโ LINSERT โ insert before/after an element โโโโโโ
RPUSH steps "step1" "step3" "step4"
LINSERT steps BEFORE "step3" "step2"
LRANGE steps 0 -1The queue pattern (RPUSH + LPOP) is one of Redis's most common real-world uses. Background job queues (email sending, image processing, notifications) are often implemented this way. For production, consider BLPOP instead of LPOP โ it blocks and waits for data, eliminating the need for polling loops.
Sets & Sorted Sets
A Set stores unique strings with no order. It's perfect for tags, followers, and unique visitors. Sorted Sets add a floating-point score to each member, enabling ranked data โ leaderboards, priority queues, and anything where order matters.
Sets โ SADD, SMEMBERS, SINTER, SUNION
SADD adds members โ duplicates are silently ignored. SMEMBERS returns all members. Sets support powerful set theory operations: SINTER (intersection), SUNION (union), SDIFF (difference). These run server-side without data transfer.
# โโ Basic set operations โโโโโโโโโโโโโโโโโโโโโโโโโ
SADD tags:post:1 "javascript" "react" "webdev"
SADD tags:post:1 "javascript" # duplicate โ ignored!
SMEMBERS tags:post:1
SCARD tags:post:1 # count members: 3
SISMEMBER tags:post:1 "react" # 1 (yes)
SISMEMBER tags:post:1 "python" # 0 (no)
# โโ Followers / following โโโโโโโโโโโโโโโโโโโโโโโโโ
SADD following:alice "bob" "charlie" "diana"
SADD following:bob "alice" "charlie" "edward"
# Who do Alice and Bob both follow? (intersection)
SINTER following:alice following:bob
# Everyone either of them follows (union)
SUNION following:alice following:bob
# Who does Alice follow that Bob doesn't? (difference)
SDIFF following:alice following:bob
# โโ Unique visitors โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SADD visitors:2026-04-23 "user:1" "user:2" "user:3"
SADD visitors:2026-04-23 "user:1" # duplicate โ ignored
SADD visitors:2026-04-24 "user:2" "user:4" "user:5"
# Unique visitors today
SCARD visitors:2026-04-23 # 3
# New visitors yesterday who didn't visit today
SDIFF visitors:2026-04-24 visitors:2026-04-23
# Total unique visitors across both days
SUNIONSTORE all_visitors visitors:2026-04-23 visitors:2026-04-24
SCARD all_visitorsSINTERSTORE, SUNIONSTORE, SDIFFSTORE save the result into a new key instead of returning it. This lets you compute set operations server-side and cache the result for reuse. Use it for computing "mutual friends", "common tags", or "users who qualify for a promotion".
Sorted Sets โ ZADD, ZRANGE, ZREVRANGE, ZRANK
Sorted Sets associate every member with a floating-point score. Members are always kept sorted by score. This enables instant leaderboards, priority queues, and any ranked data. ZRANGE returns ascending, ZREVRANGE returns descending.
# โโ Leaderboard โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ZADD leaderboard 95 "Alice"
ZADD leaderboard 82 "Bob"
ZADD leaderboard 91 "Charlie"
ZADD leaderboard 78 "Diana"
ZADD leaderboard 88 "Edward"
ZADD leaderboard 91 "Fiona" # same score as Charlie
# Get all members ascending (lowest first)
ZRANGE leaderboard 0 -1 WITHSCORES
# Get all members descending (highest first)
ZREVRANGE leaderboard 0 -1 WITHSCORES
# Top 3 players only
ZREVRANGE leaderboard 0 2 WITHSCORES
# โโ Rank and Score โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ZRANK leaderboard "Alice" # rank from low to high (4)
ZREVRANK leaderboard "Alice" # rank from high to low (0)
ZSCORE leaderboard "Alice" # "95"
# โโ Update score โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ZINCRBY leaderboard 8 "Bob" # Bob: 82 + 8 = 90
ZSCORE leaderboard "Bob" # "90"
# โโ Count and range by score โโโโโโโโโโโโโโโโโโโโโโ
ZCOUNT leaderboard 80 100 # how many score 80-100?
ZRANGEBYSCORE leaderboard 85 +inf WITHSCORES # score >= 85
# โโ Remove members โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ZREM leaderboard "Diana"
ZCARD leaderboard # 5 members leftWhen two members have the same score (Charlie and Fiona both 91), Redis breaks the tie alphabetically. ZREVRANK returns 0 for the top player โ rank 1 in leaderboard terms. Add 1 to get the human-friendly rank: user's position = ZREVRANK leaderboard "Alice" + 1.
Pub/Sub & Caching Patterns
Redis Pub/Sub lets components communicate without knowing about each other. Publishers send messages to channels. Subscribers receive them in real time. Combined with caching patterns (cache-aside, write-through) and pipelines (batching commands), these tools cover the vast majority of Redis use cases in production.
PUBLISH and SUBSCRIBE โ Messaging
SUBSCRIBE listens on a channel. PUBLISH sends a message to all subscribers. PSUBSCRIBE subscribes to channels matching a pattern. Messages are delivered in real time โ Redis does not store them. This is fire-and-forget messaging.
# โโ Terminal 1: Subscribe to channels โโโโโโโโโโโโ
SUBSCRIBE notifications
# Now waiting for messages...
SUBSCRIBE chat:room1 chat:room2
# Subscribed to 2 channels
# Pattern subscribe โ match any channel with wildcard
PSUBSCRIBE chat:*
# Receives messages from chat:room1, chat:room2, chat:general, etc.
# โโ Terminal 2: Publish messages โโโโโโโโโโโโโโโโโโ
PUBLISH notifications "New order received: #1234"
PUBLISH chat:room1 "Alice: Hello everyone!"
PUBLISH chat:room2 "Bob: Has anyone done the homework?"
PUBLISH chat:general "System: Server maintenance at 2am"
# โโ What Terminal 1 sees โโโโโโโโโโโโโโโโโโโโโโโโโโ
# (from SUBSCRIBE notifications)
# 1) "message"
# 2) "notifications"
# 3) "New order received: #1234"
# (from PSUBSCRIBE chat:*)
# 1) "pmessage"
# 2) "chat:*"
# 3) "chat:room1"
# 4) "Alice: Hello everyone!"
# PUBSUB โ inspect active pub/sub state
PUBSUB CHANNELS # list active channels
PUBSUB CHANNELS chat:* # channels matching pattern
PUBSUB NUMSUB notifications # subscriber count per channelRedis Pub/Sub messages are NOT persisted โ if a subscriber is offline, they miss the message permanently. For reliable messaging where subscribers might be temporarily offline, use Redis Streams (XADD, XREAD) instead โ Streams act like a persistent log that subscribers can catch up with.
Cache-Aside Pattern โ The Most Common Caching Strategy
Cache-aside (or lazy loading) is the dominant caching pattern. The application checks Redis first. On a cache hit, data is returned from Redis. On a cache miss, the app fetches from the database, stores in Redis, and returns the data. TTL ensures the cache stays fresh.
# โโ Cache-aside pattern in pseudocode โโโโโโโโโโโโ
# (Shown as Redis commands โ in real apps, your
# Node.js/Python/etc. code does the logic)
# Step 1: Check cache
GET cache:user:42
# Cache MISS (returns nil) โ fetch from database
# (your app queries the DB here)
# โ DB returns: { id: 42, name: "Alice", email: "alice@ex.com" }
# Step 2: Store in cache with TTL
SET cache:user:42 '{"id":42,"name":"Alice","email":"alice@ex.com"}' EX 300
# Step 3: Future requests โ cache HIT
GET cache:user:42 # returns JSON immediately
# โโ Cache invalidation โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# When user data changes (update in DB), delete cache
DEL cache:user:42 # next request re-populates
# โโ Caching a list โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SET cache:products:page1 '[{"id":1},{"id":2},...]' EX 60
# โโ Pipeline: batch multiple commands โโโโโโโโโโโโโ
# (In redis-cli, use MULTI/EXEC for transactions)
MULTI
SET cache:home "<html>home page</html>" EX 300
SET cache:about "<html>about page</html>" EX 300
INCR stats:cache:writes
EXEC
# โโ Verify all three ran โโโโโโโโโโโโโโโโโโโโโโโโโโ
MGET cache:home cache:about
GET stats:cache:writesCache key naming best practice: include enough context to be unique and easy to invalidate. cache:user:42 is good โ easy to delete when user 42 updates. cache:users is bad โ you'd delete the entire user cache when any user changes. Cache at the right granularity.
Pipelines, SCAN and Production Patterns
Pipelines batch multiple commands in a single network round-trip. SCAN safely iterates keys without blocking. These production-ready patterns complete the toolkit for real Redis deployments.
# โโ SCAN: production-safe key iteration โโโโโโโโโโ
# KEYS * blocks the server โ use SCAN instead
# SCAN cursor [MATCH pattern] [COUNT hint]
# Returns: [next_cursor, [keys]]
# First call โ start with cursor 0
SCAN 0 MATCH session:* COUNT 100
# Continue with the returned cursor until it returns 0
SCAN 42 MATCH session:* COUNT 100
SCAN 0 MATCH session:* COUNT 100 # cursor 0 = done
# โโ Pattern: object with expiry โโโโโโโโโโโโโโโโโโโ
# Store a verification code (expires in 10 minutes)
HSET verify:email:alice@ex.com code "847291" attempts 0
EXPIRE verify:email:alice@ex.com 600
HGET verify:email:alice@ex.com code # "847291"
HINCRBY verify:email:alice@ex.com attempts 1 # track attempts
# โโ Pattern: distributed lock โโโโโโโโโโโโโโโโโโโโโ
# Atomic: SET only if NOT exists, with expiry
SET lock:payment:order42 "worker-server-1" NX EX 30
# Only one worker gets this (NX ensures uniqueness)
# Lock auto-releases after 30 seconds (EX handles crashes)
# Check who holds the lock
GET lock:payment:order42
# Release the lock (only if you hold it)
DEL lock:payment:order42
# โโ OBJECT ENCODING โ see how Redis stores data โโโ
SET counter 42
OBJECT ENCODING counter # "int" (optimised!)
SET bignum 99999999999999
OBJECT ENCODING bignum # "embstr"The distributed lock pattern (SET key value NX EX seconds) is the simplest form of a Redis lock. It solves the "only one process should run this at a time" problem across multiple servers. The EX ensures the lock releases automatically if the holder crashes โ preventing permanent deadlocks.
You finished the Redis tutorial!
You now know every major Redis data structure โ strings, numbers, hashes, lists, sets, sorted sets โ plus expiry, pub/sub, caching patterns, and production best practices. Add Redis to your next Node.js or Python project.