Redis Intro¶
Everything in RAM , all the time Redis is an in-memory data structure store that operates entirely in RAM for reads and writes measured in microseconds rather than milliseconds - it's the difference between serving a cached page in 200 microseconds and hitting Postgres for the same data in 20 milliseconds , and when you're serving millions of requests per day , that 100x speedup means the difference between handling traffic spikes gracefully and burning your server CPU on repeated database queries that all return the same data
What Redis actually is¶
Redis isn't a primary database (though people try to use it as one and regret it when the server crashes and everything disappears). It's an in-memory key-value store with advanced data structures that happen to support persistence as an option. You store data in Redis because you need SPEED - caching , session storage , rate limiting , pub/sub messaging , real-time leaderboards , job queues
The key insight: Redis stores everything in RAM which means reads are CPU-register-fast but storage is expensive (RAM costs more per GB than SSD) and volatile (power goes out , data goes with it unless you explicitly persist). This is why Redis is always paired with a persistent database - Postgres or MongoDB holds the source of truth , Redis holds the hot cache
Installation¶
# Ubuntu / Debian
sudo apt update && sudo apt install redis-server -y
# macOS
brew install redis
brew services start redis
# Verify
redis-cli ping
# PONG
# Check version
redis-cli --version
# redis-cli 7.2.x
redis-cli - the Swiss Army knife¶
# Connect to localhost default port
redis-cli
# Connect to remote server
redis-cli -h redis.example.com -p 6379 -a "password"
# Ping the server
redis-cli ping
# PONG
# Check server info
redis-cli info server
# Monitor all commands in real-time (great for debugging)
redis-cli monitor # CAUTION: this outputs everything - don't run in production
# Get server stats
redis-cli info stats
Data types - the core of Redis¶
Redis supports more than just simple key-value strings. Each data type has specific operations optimized for particular use cases
Strings - the basic building block¶
SET user:100:name "Omar the Tester"
SET page:home:count 0
INCR page:home:count # -> 1
INCRBY page:home:count 5 # -> 6
GET user:100:name # -> "Omar the Tester"
# TTL - time to live (auto-expire)
SET session:abc123 "user_data_here" EX 3600 # Expires in 1 hour
TTL session:abc123 # -> 3598 (seconds remaining)
EXPIRE session:abc123 7200 # Extend TTL to 2 hours
Lists - ordered collections (queues)¶
# Push to right (end)
RPUSH notifications:user:100 "New login detected"
RPUSH notifications:user:100 "Password changed"
# Push to left (beginning)
LPUSH notifications:user:100 "URGENT: Account suspended"
# Pop from left (processing queue)
LPOP notifications:user:100 # -> "URGENT: Account suspended"
# Range - get without removing
LRANGE notifications:user:100 0 -1 # All items
# List length
LLEN notifications:user:100
Sets - unordered unique collections¶
# Add to set
SADD user:100:roles "user"
SADD user:100:roles "admin"
SADD user:100:roles "user" # Duplicate - ignored (sets enforce uniqueness)
# Check membership
SISMEMBER user:100:roles "admin" # -> 1 (true)
SISMEMBER user:100:roles "superadmin" # -> 0 (false)
# Get all members
SMEMBERS user:100:roles # -> "user", "admin"
# Set operations - powerful for relationship queries
SADD followers:user:100 "user:200" "user:300" "user:400"
SADD followers:user:200 "user:100" "user:300"
SINTER followers:user:100 followers:user:200 # Mutual followers -> "user:300"
SUNION followers:user:100 followers:user:200 # All unique followers
Sorted Sets - ranked collections¶
# Add with score (for ranking/sorting)
ZADD leaderboard:game:weekly 1500 "player:ali"
ZADD leaderboard:game:weekly 2200 "player:omar"
ZADD leaderboard:game:weekly 1800 "player:khaled"
# Get top players
ZREVRANGE leaderboard:game:weekly 0 2 WITHSCORES
# 1) "player:omar" (2200)
# 2) "player:khaled" (1800)
# 3) "player:ali" (1500)
# Get player rank
ZREVRANK leaderboard:game:weekly "player:ali" # -> 2 (0-indexed , third place)
# Increment score
ZINCRBY leaderboard:game:weekly 500 "player:ali"
# Ali now has 2000 - jumps to second place
# Get score range (players between 1000 and 2000)
ZCOUNT leaderboard:game:weekly 1000 2000 # -> 2
ZRANGEBYSCORE leaderboard:game:weekly 1000 2000
Hashes - objects as flat maps¶
# Store an object
HSET user:100 email "omar@example.com" username "omar_hacker" role "admin"
# Get single field
HGET user:100 email # -> "omar@example.com"
# Get all fields
HGETALL user:100
# 1) "email" -> "omar@example.com"
# 2) "username" -> "omar_hacker"
# 3) "role" -> "admin"
# Increment a hash field
HINCRBY user:100 login_count 1
# Check field existence
HEXISTS user:100 email # -> 1 (true)
# Get multiple fields
HMGET user:100 email username # -> "omar@example.com", "omar_hacker"
Use cases - where Redis shines¶
Caching - store computed query results or API responses with TTL so subsequent requests bypass the expensive computation. Cache-aside pattern: check Redis first, if miss, query database and populate Redis
Session storage - store user sessions with automatic expiry. Logged-in sessions should be in Redis , not in Postgres , because every page load reads the session and you don't want your primary database handling 100x more reads than writes
Rate limiting - track API request counts per user/IP in a sorted set or counter with TTL. When count exceeds threshold, reject the request. Redis is perfect for this because INCR is atomic and EXPIRE handles reset intervals
Pub/Sub messaging - publish messages to channels that multiple subscribers consume. Useful for real-time notifications , WebSocket message broadcasting , and event-driven architectures where services need to react to state changes
Queues - use Lists or Streams for background job processing. Producers LPUSH tasks, workers BRPOP tasks from the queue. Streams add consumer groups, message persistence, and acknowledgment for more reliable processing
# Simple queue pattern
# Producer:
LPUSH job:queue "email:send:user:100:welcome"
LPUSH job:queue "export:generate:report:Q4"
# Consumer (blocking pop - waits for work):
BRPOP job:queue 0 # 0 = no timeout , wait forever
# -> "job:queue" -> "export:generate:report:Q4"
Security footgun - default Redis¶
Out of the box, Redis listens on 0.0.0.0:6379 with no authentication, no TLS, and a default user that can run every command including FLUSHALL , CONFIG , and DEBUG. Automated scanners find open Redis instances within hours of deployment and use them for cryptocurrency mining, data exfiltration, and DDoS amplification (Redis protocol can be abused for reflection attacks)
# Minimum security changes in redis.conf:
# 1. Bind to localhost only
bind 127.0.0.1
# 2. Require password (legacy , but better than nothing)
requirepass your_very_long_password_here
# 3. Rename dangerous commands (or disable them)
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command EVAL "" # Lua scripting - powerful but dangerous
rename-command DEBUG ""
# 4. Disable dangerous features (7.0+ ACL)
aclfile /etc/redis/users.acl
prerequisites¶
db_05_postgres_security.md - Redis is often the second database in a stack alongside Postgres. Understanding the relational security model helps you understand why Redis needs different security patterns (network isolation matters more , authentication is thinner , persistence is optional)
next → db_11_redis_patterns.md