Redis Explained — Caching & Data Structures for Beginners
Redis is an in-memory data structure store used as a database, cache, and message broker, known for its blazing-fast performance and versatile data types like strings, hashes, lists, and sets.
What You’ll Learn
By the end of this tutorial, you’ll understand Redis data structures (strings, hashes, lists, sets, sorted sets), implement caching patterns, use pub/sub messaging, and know where Redis fits in modern application architecture.
Why Redis Matters
Redis is the most popular caching and session store in the world, used by Twitter, GitHub, Stack Overflow, and thousands of other companies. At DodaTech, Redis powers Doda Browser’s session cache and Durga Antivirus Pro’s real-time threat lookup. A single Redis instance can handle millions of operations per second.
Redis Learning Path
flowchart LR
A[SQL Basics] --> B[MySQL]
B --> C[PostgreSQL]
C --> D[MongoDB]
D --> E[Redis]
E --> F[Database Design]
E --> G{You Are Here}
style G fill:#f90,color:#fff
What Is Redis? (The “Why” First)
Think of Redis as a supercharged sticky note board right next to your computer. When you need a piece of information quickly — a user’s session, a product detail, the latest notifications — instead of walking to the filing cabinet (your database), you check your sticky notes (Redis). It’s incredibly fast because everything is kept in RAM.
Redis vs Traditional Databases
| Aspect | Redis | MySQL/PostgreSQL | MongoDB |
|---|---|---|---|
| Storage | In-memory (RAM) | Disk | Disk + memory |
| Speed | Microseconds | Milliseconds | Milliseconds |
| Data model | Key-value + structures | Relational tables | Documents |
| Persistence | Optional (RDB/AOF) | Always | Always |
| Use case | Cache, real-time | Primary storage | Primary storage |
Installing Redis
# Ubuntu/Debian
sudo apt update
sudo apt install redis-server -y
sudo systemctl start redis
# macOS
brew install redis
brew services start redis
# Verify
redis-cli ping
# Output: PONGRedis Data Structures
Redis is more than a simple key-value store. It provides specialized data structures for different use cases.
1. Strings — The Foundation
Strings are the most basic Redis type — key-value pairs where the value is text, numbers, or binary data:
# Set and get a string
SET user:1:name "Alice Johnson"
GET user:1:name
# Output: "Alice Johnson"
# Strings also support numeric operations
SET counter 100
INCR counter # 101
INCRBY counter 5 # 106
DECR counter # 105
GET counter
# Output: "105"
# Set with expiration (TTL)
SET session:abc123 "user_data" EX 3600 # Expires in 1 hour
TTL session:abc123
# Output: 3599 (seconds remaining)Use cases: Caching HTML fragments, storing session data, rate limiting counters.
2. Hashes — Object Storage
Hashes are like miniature JSON objects — they store multiple fields under one key:
# Store user data as a hash
HSET user:1 name "Alice Johnson" email "alice@example.com" age 28 city "New York"
# Get specific fields
HGET user:1 name
# Output: "Alice Johnson"
# Get all fields
HGETALL user:1
# Output:
# 1) "name"
# 2) "Alice Johnson"
# 3) "email"
# 4) "alice@example.com"
# 5) "age"
# 6) "28"
# Increment a field
HINCRBY user:1 age 1
# Output: 29
# Check if field exists
HEXISTS user:1 email
# Output: (integer) 1Use cases: User profiles, product details, any object-like data that’s accessed by key.
3. Lists — Ordered Sequences
Lists are ordered sequences of strings, optimized for push/pop operations at both ends:
# Add to the right (end)
RPUSH notifications:user1 "New message from Bob"
RPUSH notifications:user1 "Your order has shipped"
RPUSH notifications:user1 "Password changed"
# Add to the left (beginning)
LPUSH notifications:user1 "URGENT: Account alert!"
# Get range (0 = first, -1 = last)
LRANGE notifications:user1 0 -1
# Output:
# 1) "URGENT: Account alert!"
# 2) "New message from Bob"
# 3) "Your order has shipped"
# 4) "Password changed"
# Pop from left (dequeue)
LPOP notifications:user1
# Output: "URGENT: Account alert!"
# Trim to specific length
LTRIM notifications:user1 0 49 # Keep only the 50 most recentUse cases: Message queues, activity feeds, chat message history.
4. Sets — Unique Collections
Sets store unique, unordered members with O(1) membership check:
# Add members
SADD tags:post:1 "redis" "database" "nosql" "caching"
SADD tags:post:1 "redis" "tutorial" # "redis" already exists, ignored
# Get all members
SMEMBERS tags:post:1
# Output: 1) "redis" 2) "database" 3) "nosql" 4) "caching" 5) "tutorial"
# Check membership
SISMEMBER tags:post:1 "redis"
# Output: (integer) 1
SISMEMBER tags:post:1 "mysql"
# Output: (integer) 0
# Set operations — find common tags between posts
SADD tags:post:2 "redis" "nosql" "python"
SINTER tags:post:1 tags:post:2
# Output: 1) "nosql" 2) "redis"
# Union — all tags from both posts
SUNION tags:post:1 tags:post:2Use cases: Unique visitor tracking, tags, friend relationships, access control lists.
5. Sorted Sets — Ranked Collections
Sorted sets are like sets but every member has a score, and they’re ordered by score:
# Add members with scores (leaderboard)
ZADD leaderboard 100 "Alice"
ZADD leaderboard 85 "Bob"
ZADD leaderboard 95 "Charlie"
ZADD leaderboard 120 "Diana"
# Get top 3 (highest scores)
ZREVRANGE leaderboard 0 2 WITHSCORES
# Output:
# 1) "Diana"
# 2) "120"
# 3) "Alice"
# 4) "100"
# 5) "Charlie"
# 6) "95"
# Get rank of a member
ZRANK leaderboard "Bob"
# Output: 0 (0-indexed from lowest) — Bob is lowest
# Increment score (when someone earns more points)
ZINCRBY leaderboard 15 "Bob"
# Output: "100"
# Get scores within a range
ZRANGEBYSCORE leaderboard 90 110 WITHSCORESUse cases: Leaderboards, rate limiting (sorted by timestamp), priority queues.
Caching Patterns — Redis’s Killer Feature
Simple Cache-Aside Pattern
This is the most common caching pattern:
import redis
import json
cache = redis.Redis(host="localhost", port=6379, decode_responses=True)
def get_product(product_id):
cache_key = f"product:{product_id}"
# 1. Try cache first
cached = cache.get(cache_key)
if cached is not None:
print("CACHE HIT")
return json.loads(cached)
# 2. Cache miss — get from database
print("CACHE MISS — fetching from database")
product = query_database(product_id) # Your database function
# 3. Store in cache with TTL
cache.setex(cache_key, 3600, json.dumps(product))
return product
def query_database(product_id):
# Simulate a slow database query
import time
time.sleep(2) # 2 second query time
return {"id": product_id, "name": "Wireless Mouse", "price": 29.99}
# Test
product = get_product(1) # Cache miss (2 seconds)
product = get_product(1) # Cache hit (instant!)
product = get_product(1) # Cache hit (instant!)Expected output:
CACHE MISS — fetching from database
CACHE HIT
CACHE HITRate Limiting with Redis
import redis
import time
cache = redis.Redis(host="localhost", port=6379, decode_responses=True)
def is_rate_limited(user_id, max_requests=10, window_seconds=60):
key = f"ratelimit:{user_id}:{int(time.time() / window_seconds)}"
current = cache.incr(key)
# Set expiry on first request
if current == 1:
cache.expire(key, window_seconds + 1)
return current > max_requests
# Simulate requests
for i in range(12):
user = "user_123"
if is_rate_limited(user):
print(f"Request {i+1}: RATE LIMITED")
else:
print(f"Request {i+1}: Allowed")
time.sleep(0.1)Expected output:
Request 1: Allowed
...
Request 10: Allowed
Request 11: RATE LIMITED
Request 12: RATE LIMITEDSession Storage
import redis
import json
import uuid
cache = redis.Redis(host="localhost", port=6379, decode_responses=True)
def create_session(user_data):
session_id = str(uuid.uuid4())
cache.hset(f"session:{session_id}", mapping=user_data)
cache.expire(f"session:{session_id}", 86400) # 24 hours
return session_id
def get_session(session_id):
data = cache.hgetall(f"session:{session_id}")
return data if data else None
# Usage
session = create_session({
"user_id": "123",
"username": "alice",
"role": "admin"
})
print(f"Session ID: {session}")
print(f"Session data: {get_session(session)}")Pub/Sub — Real-Time Messaging
Redis supports publish/subscribe messaging — one-to-many message broadcasting:
# publisher.py
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
r.publish("notifications", json.dumps({
"type": "order_shipped",
"order_id": 1234,
"user_id": 567
}))
print("Message published!")# subscriber.py
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
pubsub = r.pubsub()
pubsub.subscribe("notifications")
print("Waiting for messages...")
for message in pubsub.listen():
if message["type"] == "message":
print(f"Received: {message['data']}")Use cases: Real-time notifications, live chat, event broadcasting, cache invalidation across servers.
Persistence — Does Redis Lose Data on Restart?
Redis can persist data to disk. Two options:
| Method | How It Works | Recovery Speed | Data Loss |
|---|---|---|---|
| RDB (snapshot) | Periodic snapshots | Fast (single file) | Last snapshot interval |
| AOF (append-only) | Logs every write | Slower (replay) | Last second (with always fsync) |
| RDB + AOF | Both | AOF used first | Best protection |
# Configure in redis.conf
save 900 1 # RDB: save if 1 key changed in 900 seconds
save 300 10 # RDB: save if 10 keys changed in 300 seconds
appendonly yes # Enable AOF
appendfsync everysec # AOF: fsync every second (good balance)Common Redis Mistakes
1. Using Redis as a Primary Database
Redis is an in-memory store. While it can persist data, it’s designed as a cache/session store. If you lose your Redis server, you should be able to rebuild data from your primary database.
2. Not Setting Expiry (TTL)
Keys without TTL accumulate forever. For caches and sessions, always set TTL. Monitor memory usage and use MAXMEMORY policy to evict old data.
3. Storing Large Values
Redis works best with values under 100KB. Large values (MB+) consume memory and slow down operations. Store large objects in object storage (S3) and keep URLs in Redis.
4. Using KEYS in Production
KEYS * blocks Redis for the duration of the scan — unacceptable in production. Use SCAN for production iteration:
# NEVER do this in production:
KEYS user:*
# Use SCAN instead:
SCAN 0 MATCH user:* COUNT 1005. Ignoring Connection Pooling
Creating a new Redis connection for every request is expensive. Use connection pooling (most Redis clients support this automatically, but verify).
6. Forgetting About Memory Limits
Redis stores everything in RAM. Set maxmemory in redis.conf and choose an eviction policy:
maxmemory 4gb
maxmemory-policy allkeys-lru # Evict least recently used keys7. Not Using Redis for What It’s Good At
Don’t force relational patterns into Redis. Redis is not for complex queries, joins, or report generation. Use it for specific high-performance use cases.
Common Mistakes Beginners Make
1. Skipping the Fundamentals
Many beginners jump straight to advanced topics without mastering the basics. Take time to understand the core concepts before moving on.
2. Not Practicing Enough
Reading tutorials without writing code leads to shallow understanding. Code along with every example and experiment on your own.
3. Ignoring Error Messages
Error messages tell you exactly what went wrong. Read them carefully — they usually point to the line and type of issue.
4. Copy-Pasting Without Understanding
It’s tempting to copy code from tutorials, but typing it yourself and understanding each line builds real skill.
5. Giving Up Too Early
Every developer hits frustrating bugs. Take breaks, ask for help, and remember that struggling is part of learning.
Practice Questions
1. What data structures does Redis support?
Strings, hashes, lists, sets, sorted sets, bitmaps, hyperloglogs, geospatial indexes, and streams.
2. What does SET key value EX 3600 do?
Sets a key with a value and a TTL of 3600 seconds (1 hour). After 3600 seconds, the key is automatically deleted.
3. How does caching improve application performance?
By storing frequently accessed data in fast RAM (Redis), avoiding slow database queries. A cache hit returns data in microseconds vs milliseconds from the database.
4. What’s the difference between a list and a set in Redis?
Lists are ordered (can have duplicates, supports index access). Sets are unordered (no duplicates, supports membership check and set operations like union/intersection).
5. Challenge: Implement a simple task queue using Redis lists.
# Producer: add tasks
LPUSH task_queue "process_image:123.jpg"
LPUSH task_queue "send_email:user@example.com"
LPUSH task_queue "generate_report:Q2-2026"
# Consumer: pop and process
RPOP task_queue
# Output: "process_image:123.jpg"Real-World Task: Build a Rate Limiter
Create a Redis-based rate limiter that prevents API abuse:
import redis
import time
import os
cache = redis.Redis(
host=os.getenv("REDIS_HOST", "localhost"),
port=6379,
decode_responses=True
)
class RateLimiter:
def __init__(self, max_requests=100, window_seconds=60):
self.max_requests = max_requests
self.window = window_seconds
def check(self, client_id):
key = f"ratelimit:{client_id}:{int(time.time() / self.window)}"
count = cache.incr(key)
if count == 1:
cache.expire(key, self.window + 1)
return count <= self.max_requests, self.max_requests - count
# Usage
limiter = RateLimiter(max_requests=5, window_seconds=60)
for i in range(7):
allowed, remaining = limiter.check("api_client_1")
status = "✓" if allowed else "✗"
print(f"[{status}] Request {i+1}: remaining={remaining}")
time.sleep(0.5)Expected output:
[✓] Request 1: remaining=4
[✓] Request 2: remaining=3
[✓] Request 3: remaining=2
[✓] Request 4: remaining=1
[✓] Request 5: remaining=0
[✗] Request 6: remaining=0
[✗] Request 7: remaining=0This exact pattern is used by Doda Browser’s API and Durga Antivirus Pro’s update service to prevent abuse while ensuring legitimate users have full access.
FAQ
Try It Yourself
Install Redis and experiment with all data structures:
# Start Redis CLI
redis-cli
# Play with strings
SET greeting "Hello Redis"
GET greeting
# Play with lists
RPUSH fruits "apple" "banana" "cherry"
LRANGE fruits 0 -1
# Play with sets
SADD developers "alice" "bob" "charlie"
SADD managers "bob" "diana"
SINTER developers managers # Who is both? "bob"
# Play with sorted sets
ZADD scores 90 "alice" 85 "bob" 95 "charlie"
ZREVRANGE scores 0 -1 WITHSCORESWhat’s Next
What’s Next
Congratulations on completing this Redis tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro