From Fundamentals to Production-Grade Caching, Sessions, Pub/Sub, Counters, Locks and Failure Handling
1. What is Redis?
Redis is a high-performance, primarily in-memory data store.
The easiest mental model is:
Redis is a shared, extremely fast data-structure server that applications access over the network.
Instead of limiting you to simple key → value pairs, Redis lets the value associated with a key be a String, Hash, List, Set, Sorted Set, Stream, and several other specialized structures. Redis officially describes itself as an in-memory data store that can be used for caching, messaging, streaming, document/vector workloads and other use cases, with replication and optional disk persistence.
A simple application might look like this:
flowchart LR
U[User] --> A[Application]
A --> R[(Redis)]
A --> D[(Database)]
R -->|Fast cached data| A
D -->|Source of truth| A
Without Redis:
Application
|
| SQL query
v
Database
|
| 20-200 ms
v
Application
With Redis:
Application
|
| GET product:100
v
Redis
|
| Often sub-millisecond / low-millisecond
v
Application
The exact latency depends on network, deployment, workload and payload size, but the important idea is:
memory access is normally much faster than repeatedly performing expensive database queries or external API calls.
2. Why Does Redis Exist?
Imagine an e-commerce site.
Thousands of customers repeatedly request:
Product ID: 100
Name: MacBook Pro
Price: ₹180000
Category: Laptop
Availability: In Stock
Without caching:
Customer 1 ──→ Database
Customer 2 ──→ Database
Customer 3 ──→ Database
Customer 4 ──→ Database
Customer 5 ──→ Database
The database repeatedly performs essentially the same work.
With Redis:
┌──────────────┐
Customer ───────>│ Application │
└──────┬───────┘
|
Check Redis
|
┌─────────▼─────────┐
│ Redis │
│ product:100 │
└─────────┬─────────┘
|
Cache HIT
|
v
Return
Only when Redis does not have the data does the application normally query the database.
3. Redis Is Not Just a Cache
This is one of the most important Redis concepts.
Redis can be used for:
| Requirement | Redis capability |
|---|---|
| Application caching | Strings / Hashes |
| User sessions | Strings / Hashes + TTL |
| Counters | INCR, INCRBY |
| Leaderboards | Sorted Sets |
| Unique membership | Sets |
| Queues | Lists / Streams |
| Live notifications | Pub/Sub |
| Event processing | Streams |
| Rate limiting | Counters + expiration |
| Temporary distributed coordination | SET NX |
| Geospatial lookup | Geospatial indexes |
| Unique visitor estimation | HyperLogLog |
Redis’ current documentation describes Redis as a data structure server, with native structures intended for use cases ranging from caching and queuing to event processing.
4. Redis Architecture — Simple Mental Model
Redis normally sits between your application and slower systems.
flowchart TB
USER[Users]
LB[Load Balancer]
A1[Application Instance 1]
A2[Application Instance 2]
A3[Application Instance 3]
R[(Redis)]
DB[(Primary Database)]
USER --> LB
LB --> A1
LB --> A2
LB --> A3
A1 --> R
A2 --> R
A3 --> R
A1 --> DB
A2 --> DB
A3 --> DB
Redis provides something extremely useful here:
shared fast state.
Application instances do not need to keep everything inside their own memory.
For example:
App-1 local memory ≠ App-2 local memory
But:
App-1 ──┐
App-2 ──┼──> Same Redis
App-3 ──┘
All application instances can access the same session, counter, cached object or temporary lock.
5. Running Redis Locally
For learning, Docker is one of the easiest approaches. Redis’ official documentation provides Docker as one of its supported installation/run options.
Start Redis:
docker run --name redis-lab \
-p 6379:6379 \
-d redis
Check the container:
docker ps
Open Redis CLI:
docker exec -it redis-lab redis-cli
Test Redis:
PING
Result:
PONG
Your Redis environment is ready.
6. Your First Redis Commands
Store something:
SET name "Rajesh"
Read it:
GET name
Result:
"Rajesh"
Delete it:
DEL name
Check whether it exists:
EXISTS name
Expiration:
SET verification_code "928431" EX 300
This means:
Key = verification_code
Value = 928431
TTL = 300 seconds
Check remaining TTL:
TTL verification_code
Redis supports expiration directly as part of SET as well as through expiration commands such as EXPIRE.
7. Keys Are Extremely Important
Redis applications commonly use readable hierarchical key names.
Instead of:
123
prefer:
user:123
For a session:
session:a81fc92
For a product:
product:1001
For a shopping cart:
cart:user:100
For a rate limiter:
rate:user:100:2026-08-19T07:33
For a temporary lock:
lock:order:5432
A useful convention is:
<domain>:<entity>:<id>
For example:
ecommerce:product:1001
ecommerce:user:700
ecommerce:session:abc123
Good key naming dramatically improves debugging and operations.
8. Redis Data Structures
Redis’ most important characteristic is that values are not all treated as identical blobs.
The core structures you should understand first are:
| Structure | Think of it as | Common use case |
|---|---|---|
| String | Single value | Cache, token, counter |
| Hash | Object / record | User, product, session |
| List | Ordered sequence | Queue |
| Set | Unique collection | Tags, membership |
| Sorted Set | Ranked collection | Leaderboard |
| Stream | Append-only event log | Event processing |
| Bitmap | Bits | Daily activity flags |
| HyperLogLog | Approximate unique count | Unique visitors |
Redis currently supports an even broader collection of native and specialized data types, but mastering these core structures gives you the foundation for most application workloads.
9. Redis String
A Redis String is the simplest structure.
key → value
Example:
SET product:100:name "iPhone"
GET product:100:name
Strings are ideal for:
- cached JSON
- tokens
- OTPs
- feature flags
- configuration
- counters
Example JSON:
SET product:100 '{"name":"iPhone","price":80000}'
Retrieve:
GET product:100
Output:
{"name":"iPhone","price":80000}
When should you use String?
Use String when:
One key represents one logical value.
For example:
user:100:last_login → "2026-08-19"
10. Redis Hash
A Hash represents an object containing fields.
Think:
user:100
|
+-- name = Asha
+-- city = Delhi
+-- age = 30
Commands:
HSET user:100 name "Asha" city "Delhi" age 30
Read one field:
HGET user:100 name
Read everything:
HGETALL user:100
Update one field:
HSET user:100 city "Mumbai"
A Redis Hash naturally represents many application records because Redis hashes are field-value collections similar to dictionaries or maps in programming languages.
Appropriate use cases
User profile
Shopping cart
Session information
Product metadata
Application configuration
11. Redis List
A List is an ordered sequence.
Task A
Task B
Task C
Task D
Add:
LPUSH jobs "send-email"
Add another:
LPUSH jobs "generate-report"
Read:
LRANGE jobs 0 -1
Redis lists preserve insertion ordering.
They can be useful for simple queue-like workloads.
However:
For sophisticated durable event processing, retries, consumer groups and replay, consider Redis Streams or a dedicated messaging platform rather than treating a List as a complete messaging system.
12. Redis Set
A Set contains unique values.
SADD product:100:tags electronics
SADD product:100:tags mobile
SADD product:100:tags apple
Get members:
SMEMBERS product:100:tags
Adding apple again:
SADD product:100:tags apple
does not create a duplicate.
Think:
Set = unique unordered collection
Typical use cases:
User roles
Permissions
Unique tags
Online users
Group membership
Unique categories
Redis sets support constant-time add, remove and membership checks in typical cases.
13. Redis Sorted Set
A Sorted Set contains unique members, but every member has a score.
Example leaderboard:
ZADD leaderboard 500 Alice
ZADD leaderboard 900 Bob
ZADD leaderboard 700 Charlie
Read ranking:
ZRANGE leaderboard 0 -1 WITHSCORES
Conceptually:
Bob 900
Charlie 700
Alice 500
Use Sorted Sets for:
Leaderboards
Priority systems
Ranking
Scores
Scheduling
Top-N queries
The difference is:
SET
Unique values
SORTED SET
Unique values + score + ordering
Redis defines sorted sets as unique strings maintained in order according to their associated score.
14. Redis Streams
A Stream is fundamentally different.
Think:
Event 1
Event 2
Event 3
Event 4
↓
new events keep arriving
Example:
XADD orders * order_id 1001 status created
Another:
XADD orders * order_id 1002 status created
Read:
XRANGE orders - +
Redis describes Streams as an append-only log designed to record events in sequence and make them available for processing.
Good use cases include:
Order events
Audit events
Telemetry
Background processing
Consumer groups
Durable event workflows
15. Data Structure Decision Guide
A very practical way to remember Redis structures is:
Need one value?
|
String
Need object fields?
|
Hash
Need ordered queue?
|
List
Need unique values?
|
Set
Need ranking?
|
Sorted Set
Need durable event log?
|
Stream
Or:
| Requirement | Choose |
|---|---|
| Cache product JSON | String |
| Store session fields | Hash |
| Simple task queue | List |
| Unique users | Set |
| Leaderboard | Sorted Set |
| Durable event processing | Stream |
16. What Is Caching?
Caching means:
Store expensive-to-obtain data somewhere faster so the next request does not have to perform the expensive operation again.
Suppose:
Database query = 80 ms
Redis GET = much faster
Instead of hitting the database 10,000 times for the same product:
Database
Database
Database
Database
Database
we cache it:
Redis
/ / | \ \
request request
|
Database only
when necessary
17. Cache Hit and Cache Miss
These two terms are fundamental.
Cache HIT
Application asks Redis:
GET product:100
Redis has it.
Application
|
v
Redis
|
HIT
|
v
Response
No database query is needed.
Cache MISS
Redis does not have it.
Application
|
v
Redis
|
MISS
|
v
Database
The application fetches the data and normally stores it in Redis.
18. Cache-Aside Pattern
Cache-aside is probably the most important Redis caching pattern to learn.
It is also called:
Lazy Loading
The application manages the cache.
The process is:
flowchart TD
A[Application receives request]
B{Data in Redis?}
C[Return cached data]
D[Query Database]
E[Store result in Redis]
F[Return result]
A --> B
B -->|Yes - HIT| C
B -->|No - MISS| D
D --> E
E --> F
Algorithm:
1. Check Redis.
2. If found → return it.
3. If not found → query database.
4. Put database result into Redis.
5. Return result.
19. Cache-Aside Example
Assume:
GET /products/100
Pseudo-code:
def get_product(product_id):
key = f"product:{product_id}"
# Step 1: Check Redis
cached = redis.get(key)
if cached:
return cached
# Step 2: Cache miss
product = database.find_product(product_id)
# Step 3: Put data into Redis
redis.set(key, product, ex=300)
# Step 4: Return result
return product
The first request looks like:
Client
|
v
Application
|
| GET product:100
v
Redis
|
MISS
|
v
Database
|
Product
|
v
Redis SET + TTL
|
v
Client
Second request:
Client
|
v
Application
|
v
Redis
|
HIT
|
v
Client
No database access.
20. Why Cache-Aside Is So Popular
It has several attractive properties.
The database remains the source of truth.
Redis contains copies of frequently accessed data.
If Redis loses a cache entry:
Not catastrophic
because the application can recreate it from the database.
This architecture is extremely common:
Redis = optimization
Database = authority
That distinction is one of the safest Redis design principles.
21. The Problem With Cache-Aside
Caching introduces another copy of the data.
Suppose:
Database:
product:100.price = ₹80,000
Redis:
product:100.price = ₹80,000
Someone updates the database:
product:100.price = ₹75,000
But Redis still contains:
₹80,000
You now have:
Stale Data
That leads directly to one of the hardest caching problems:
Cache Invalidation
22. Read-Through / Cache-Through
In cache-aside:
Application knows how to load the cache.
Conceptually:
Application
|
+---- Redis
|
+---- Database
In a read-through/cache-through abstraction, the application interacts with a cache layer:
Application
|
v
Cache Layer
|
+---- Redis
|
+---- Database loader
When data is missing:
Cache Layer
|
v
Database
|
v
Redis
The difference is primarily who owns the loading logic.
Redis commands themselves do not magically execute your SQL query because GET misses. A framework, library or application layer implements that behavior.
23. Write-Through Caching
Write-through means:
When the application writes data, the authoritative store and cache are updated as part of the write path.
Conceptually:
flowchart LR
A[Application]
C[Cache Layer]
R[(Redis)]
D[(Database)]
A -->|Write| C
C --> D
C --> R
Example:
UPDATE product price
|
+----> Database = ₹75,000
|
+----> Redis = ₹75,000
The goal is to keep Redis warm and synchronized.
24. Write-Behind / Write-Back
Another architecture sometimes discussed alongside these patterns is write-behind.
Application
|
v
Cache
|
| asynchronous
v
Database
The application may return before the database has been updated.
Advantage:
Very fast writes
Risk:
Cache failure before persistence
↓
Potential data loss
This pattern requires much more careful durability design.
For ordinary application caching, cache-aside or well-designed write-through strategies are usually easier to reason about.
25. Cache Pattern Comparison
| Pattern | Read miss handled by | Writes handled by | Complexity | Typical use |
|---|---|---|---|---|
| Cache-aside | Application | Application/DB | Low | Most applications |
| Read-through | Cache abstraction | Application | Medium | Framework-managed caching |
| Write-through | Cache abstraction | Cache + DB | Medium | Frequently read data |
| Write-behind | Cache layer | Eventually DB | High | Specialized high-write workloads |
The easiest pattern for most developers to start with is:
Cache-Aside
26. TTL — Time To Live
TTL determines how long a cached value remains valid.
Example:
SET product:100 "..." EX 300
means:
Store this key for 300 seconds.
After that:
Redis removes/expires it.
Check:
TTL product:100
27. Why TTL Is Important
Without TTL:
Old cache entries
+
Unused cache entries
+
Potentially stale values
=
Memory and correctness problems
TTL provides a safety mechanism.
But:
TTL is not a replacement for proper invalidation when freshness matters.
28. How Should You Select TTL?
This question does not have one universal answer.
Do not think:
Redis TTL should always be 5 minutes.
Instead ask:
How stale is this data allowed to become?
Examples:
| Data | Possible TTL |
|---|---|
| Country list | Hours / days |
| Product description | Minutes / hours |
| Product price | Shorter |
| Inventory | Seconds or event-invalidated |
| User profile | Minutes |
| OTP | 1–10 minutes |
| Session | Based on session policy |
| Static configuration | Long |
| Real-time balance | Often avoid ordinary stale caching |
The correct TTL comes primarily from the application’s freshness requirement, not from a Redis rule.
29. TTL Decision Model
Consider four things:
Business freshness requirement
+
How frequently data changes
+
Cost of rebuilding cache
+
Traffic / database capacity
=
TTL decision
For example:
Product Description
changes once per week
↓
TTL could be relatively long
But:
Flash Sale Price
changes frequently
↓
Long TTL could be dangerous
30. TTL Jitter
Imagine 100,000 product keys are populated at 10:00 AM with:
TTL = exactly 3600 seconds
At 11:00 AM:
100,000 keys expire
Suddenly:
100,000 requests → database
This can cause a cache stampede.
Instead of identical TTLs:
3600
3600
3600
3600
3600
use jitter:
3412
3678
3554
3721
3498
For example:
ttl = 3600 + random.randint(-300, 300)
Now expiration is distributed across time.
31. Absolute vs Sliding Expiration
Absolute expiration
Session:
Created 09:00
Expires 10:00
Usage does not extend it.
Sliding expiration
Every valid activity refreshes expiration.
09:00 login
|
09:20 activity → expiry becomes 10:20
|
09:50 activity → expiry becomes 10:50
Sliding expiration is common for user sessions.
However, security-sensitive applications may combine:
Idle timeout
+
Maximum absolute lifetime
so a session cannot live forever.
32. Cache Invalidation
Cache invalidation means:
Removing or replacing cached data when the underlying authoritative data changes.
Example:
Database
product:100
price = ₹80,000
Redis
product:100
price = ₹80,000
Price changes:
Database = ₹75,000
What should happen to Redis?
You need an invalidation strategy.
33. Strategy 1 — TTL-Only Invalidation
Update DB:
Database = ₹75,000
Do nothing to Redis.
Wait until:
TTL expires
Simple but potentially stale.
Use when:
Temporary staleness is acceptable.
34. Strategy 2 — Delete Cache After Database Update
One of the most common strategies is:
1. Update Database
2. Delete Redis key
Example:
database.update_product(product)
redis.delete(f"product:{product.id}")
Next request:
Redis MISS
|
v
Database
|
v
Fresh data
|
v
Redis
This is often safer than trying to independently reconstruct the exact cached representation during every write.
35. Strategy 3 — Update the Cache
Another option:
UPDATE database
UPDATE Redis
Example:
database.update_product(product)
redis.set(
f"product:{product.id}",
serialize(product),
ex=300
)
Advantage:
Cache remains warm.
But application logic becomes more complicated because every write path must correctly update every relevant cache key.
36. Strategy 4 — Event-Driven Invalidation
Large systems often use events.
Example:
Product Service
|
| ProductUpdated
v
Message Broker
|
v
Cache Invalidation Worker
|
v
Redis DEL product:100
This helps when many systems maintain derived caches.
37. Cache Invalidation Golden Rule
Think of cached data as:
A derived copy
not:
Automatically synchronized truth
The system must answer:
Who updates the authoritative data?
Who invalidates the cache?
What happens if invalidation fails?
How stale may the cache become?
TTL should frequently remain as a backup safety mechanism even if you actively invalidate keys.
38. Cache Stampede
Now consider this very common failure.
A popular key:
homepage:recommendations
receives:
20,000 requests/second
Redis contains it.
Everything works:
20,000 requests
|
v
Redis
Then the key expires.
All requests see:
CACHE MISS
And all query the database.
flowchart TD
K[Popular Redis key expires]
A1[Request 1]
A2[Request 2]
A3[Request 3]
A4[Request 10,000]
DB[(Database)]
K --> A1
K --> A2
K --> A3
K --> A4
A1 --> DB
A2 --> DB
A3 --> DB
A4 --> DB
This is:
Cache Stampede
also called:
Thundering Herd
Dogpile Effect
39. Why Cache Stampede Is Dangerous
Normally:
Redis protects database.
During a stampede:
Redis key expires
↓
Huge number of cache misses
↓
Huge database traffic
↓
Database slows down
↓
Requests take longer
↓
Even more requests accumulate
↓
Possible outage
A cache that was intended to protect the database can therefore contribute to a sudden load spike if expiration is not designed carefully.
40. Stampede Protection — TTL Jitter
First technique:
Do not expire thousands of related keys simultaneously.
Instead:
baseTTL + random jitter
Example:
ttl = 300 + random.randint(0, 60)
41. Stampede Protection — Single Refresher
Instead of allowing every request to rebuild the cache:
Request A → obtains refresh lock
Request B → waits / serves stale
Request C → waits / serves stale
Request D → waits / serves stale
Only:
Request A → Database
Once refreshed:
Redis populated
and other requests use the new value.
42. Stale-While-Revalidate
Another powerful pattern:
Cache value exists
but soft TTL expired
Instead of blocking the customer:
Return slightly stale value
+
Refresh cache in background
Conceptually:
┌──> Return stale value
Request → Redis ────┤
└──> One worker refreshes
|
v
Database
This can be excellent for:
Homepages
Recommendations
Content
Catalog information
It may be unacceptable for:
Account balances
Critical inventory guarantees
Security permissions
The business meaning of stale data matters.
43. Stale Data Risk
Caching always creates the possibility of:
Database value ≠ cached value
Example:
Database inventory = 0
Redis inventory = 5
If Redis is blindly trusted:
Customer may purchase nonexistent stock.
Therefore ask:
Is the data informational, or is it authoritative for a critical decision?
For critical decisions:
Redis can accelerate the process
but the authoritative transactional system may still need to validate the final action.
44. Redis Pub/Sub
Pub/Sub stands for:
Publish / Subscribe
Components:
Publisher
Channel
Subscriber
Example:
flowchart LR
P[Order Service]
C((order-events))
S1[Email Service]
S2[Notification Service]
S3[WebSocket Service]
P -->|Publish| C
C --> S1
C --> S2
C --> S3
Redis uses commands such as:
SUBSCRIBE
PUBLISH
UNSUBSCRIBE
for this model.
45. Redis Pub/Sub Example
Terminal 1:
SUBSCRIBE orders
Terminal 2:
PUBLISH orders "order-1001-created"
Subscriber receives the message.
Another example:
PUBLISH notifications "user-100-logged-in"
46. Pub/Sub Is Not a Durable Queue
This is critical.
Redis Pub/Sub provides at-most-once delivery.
If a subscriber is disconnected when the message is published:
Message can be lost permanently.
Redis explicitly documents this behavior and recommends Streams when stronger delivery guarantees and persistence are required.
Think:
Pub/Sub
"Who is listening RIGHT NOW?"
versus:
Stream
"Record this event so consumers can process it."
47. Pub/Sub vs Redis Streams
| Feature | Pub/Sub | Streams |
|---|---|---|
| Persistent messages | No | Yes |
| Offline consumer receives old message | No | Possible |
| Consumer groups | No | Yes |
| Replay | No | Yes |
| Simple live broadcast | Excellent | Possible |
| Durable processing | Poor fit | Good fit |
Use Pub/Sub for:
Live notifications
WebSocket fan-out
Live dashboards
Cache invalidation notification
Ephemeral events
Use Streams when:
Events must not simply disappear because a consumer disconnected.
48. Redis Pub/Sub vs Kafka
They solve overlapping but different problems.
Redis Pub/Sub
↓
Fast ephemeral broadcast
Kafka
↓
Durable distributed event log
A useful simplified comparison:
| Requirement | Redis Pub/Sub | Kafka |
|---|---|---|
| Live broadcast | Excellent | Yes |
| Durable event log | No | Yes |
| Replay | No | Yes |
| Long retention | No | Yes |
| Consumer offsets | No | Yes |
| Very simple notifications | Excellent | Often overkill |
| Enterprise event backbone | Usually no | Strong fit |
Do not automatically replace Kafka with Redis Pub/Sub simply because both can transmit messages.
49. Redis for User Sessions
Sessions are one of Redis’ classic use cases.
Imagine three application instances:
flowchart TB
U[User]
LB[Load Balancer]
A1[App 1]
A2[App 2]
A3[App 3]
R[(Redis Session Store)]
U --> LB
LB --> A1
LB --> A2
LB --> A3
A1 --> R
A2 --> R
A3 --> R
Without shared session storage:
User logs in through App-1.
Next request goes to App-2.
App-2:
"Who are you?"
With Redis:
App-1
App-2
App-3
|
v
Same Redis session
Any application instance can resolve the session.
50. Session Example
Key:
session:f81d4fae
Hash:
HSET session:f81d4fae \
user_id 100 \
role admin \
authenticated true
Expiration:
EXPIRE session:f81d4fae 1800
Meaning:
Session expires after 30 minutes
On logout:
DEL session:f81d4fae
51. Session Design Recommendations
A session should normally have an expiration policy.
Avoid:
session:* with no expiration forever
Also avoid placing unnecessary sensitive information directly into session objects.
Prefer:
Session ID
User ID
Authorization/context information needed for session
Timestamps
Minimal metadata
Keep the authoritative user/account records elsewhere unless Redis has deliberately been designed as their primary system.
52. Distributed Counters
Imagine ten application servers.
All must increment:
Number of API requests
Local counters do not work:
App-1 = 100
App-2 = 200
App-3 = 170
Redis provides shared counters:
Redis
|
api_requests = 470
/ | \
App1 App2 App3
53. INCR
Initialize:
SET pageviews 0
Increment:
INCR pageviews
Again:
INCR pageviews
Result:
2
Add 10:
INCRBY pageviews 10
Result:
12
Redis performs these counter operations atomically, which makes them extremely useful when many application instances modify the same counter.
54. Counter Use Cases
Counters work well for:
Page views
API calls
Downloads
Login attempts
Rate limiting
Usage quotas
Votes
Temporary metrics
Example:
INCR article:500:views
Ten application instances can safely increment the same Redis key.
55. Simple Rate Limiting
Suppose:
Maximum = 100 requests/minute/user
Key:
rate:user:100:202608190733
First request:
SET rate:user:100:202608190733 1 EX 60 NX
Subsequent requests:
INCR rate:user:100:202608190733
If counter becomes:
101
reject the request.
Concept:
Request
|
v
INCR counter
|
+-- <= 100 → Allow
|
+-- > 100 → Reject
Production-grade rate limiting may use Lua scripts, sorted sets or other algorithms such as sliding windows or token buckets depending on fairness and precision requirements.
56. Lightweight Distributed Locks
Suppose two workers attempt:
Generate invoice 1001
at the same time.
Without coordination:
Worker A ──> Generate
Worker B ──> Generate
Possible duplicate invoice
A Redis lock can coordinate temporary access.
57. Basic Redis Lock
Use:
SET lock:invoice:1001 abc123 NX EX 10
Meaning:
SET = create key
NX = only if key does not already exist
EX 10 = automatically expire after 10 seconds
abc123 = unique owner token
Redis’ SET command supports conditional creation with NX and expiration with EX/PX.
If successful:
Worker owns lock.
If it fails:
Someone else owns lock.
58. Why Lock Expiration Is Essential
Never create:
SET lock:invoice:1001 locked NX
without thinking about failure.
Suppose:
Worker obtains lock
|
v
Worker crashes
Lock remains forever.
Now:
Nobody can process invoice 1001.
Expiration creates a lease:
Lock eventually disappears.
59. Do Not Blindly Delete Somebody Else’s Lock
Consider:
Worker A obtains lock for 10 sec.
Worker A becomes slow.
Lock expires.
Worker B obtains new lock.
Worker A wakes up.
Worker A executes DEL lock.
Worker A has accidentally deleted:
Worker B's lock.
That is why the value should contain a unique ownership token.
Example:
lock:invoice:1001
|
v
550e8400-e29b-41d4-a716
The owner should release the lock only if the stored token still belongs to it.
A classic safe release is an atomic compare-and-delete operation, often implemented with a small Lua script.
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
60. What Redis Locks Are Good For
Good examples:
Prevent duplicate background refresh
Coordinate cache rebuild
Prevent duplicate scheduled job
Short-lived idempotency coordination
Temporary singleton work
Be more cautious with:
Bank transfer correctness
Financial settlement
Strict leader election
Irreversible infrastructure control
Safety-critical coordination
A Redis lock is useful, but it is not automatically a substitute for a consensus system or database transaction.
For strict correctness, consider:
Database constraints
Transactions
Idempotency keys
Fencing tokens
Consensus-based coordination
depending on the problem.
61. Connection Pooling
Applications should not normally do this for every request:
Request
|
Create TCP connection
|
Authenticate
|
Execute GET
|
Close connection
Then repeat:
10,000 times/second
Connection establishment itself costs resources.
Instead, clients typically reuse connections.
flowchart LR
A[Application]
P[Connection Pool]
C1[Connection 1]
C2[Connection 2]
C3[Connection 3]
R[(Redis)]
A --> P
P --> C1
P --> C2
P --> C3
C1 --> R
C2 --> R
C3 --> R
Redis’ current client documentation explicitly includes pooling/multiplexing as a client concern, and documented clients include Python, Java, JavaScript, Go, .NET, PHP, Rust and others.
62. Connection Pool Mental Model
Instead of:
Create
Use
Destroy
Create
Use
Destroy
Create
Use
Destroy
do:
Create reusable connections
|
v
Pool
|
+---- request
+---- request
+---- request
However, Redis clients differ.
Some primarily use:
Connection pools
while others rely heavily on:
Connection multiplexing
Therefore:
Follow the architecture of your chosen Redis client rather than blindly configuring an enormous pool.
63. Connection Pool Parameters to Understand
Typical settings include:
Maximum connections
Minimum idle connections
Connection timeout
Read timeout
Write timeout
Pool wait timeout
Idle timeout
Health checks
TLS settings
Authentication
Example conceptual configuration:
max_connections = 50
connect_timeout = 1s
read_timeout = 500ms
pool_timeout = 500ms
Those are examples, not universal production values.
Pool sizing must consider:
Application concurrency
Number of application instances
Redis connection capacity
Command latency
Pipeline/multiplexing behavior
Traffic pattern
64. Connection Pooling Golden Rule
Do not calculate:
50 connections sounds good.
Instead calculate total system load.
Suppose:
100 application pods
×
100 Redis connections/pod
=
10,000 connections
A seemingly small per-instance pool can become enormous across a cluster.
Always think:
Per-instance configuration × number of instances
65. Redis Failure Handling
Eventually one of these will happen:
Redis unavailable
Network problem
Timeout
Connection exhaustion
Failover
High latency
Memory pressure
Application bug
Your architecture must define what happens next.
This is often more important than the happy-path Redis code.
66. Example — Redis Cache Failure
Normal:
Application
|
v
Redis
|
HIT
Redis unavailable:
Application
|
X
Redis
|
v
Database
For ordinary cache data, a common strategy is:
Redis fails → bypass cache → query source of truth.
This is called:
Graceful degradation
But there is an important danger.
67. Redis Failure Can Become Database Failure
Imagine:
Redis handles 50,000 requests/sec
Redis fails.
Your fallback says:
Use database.
Now:
50,000 requests/sec → Database
The database may support only:
5,000 queries/sec.
Result:
Redis outage
↓
Database overload
↓
Application outage
This is called a cascading failure.
68. Protecting the Database During Redis Failure
Useful mechanisms include:
Short Redis timeouts
Circuit breakers
Request rate limiting
Bulkheads
Database connection limits
Load shedding
Fallback data
Stale data
Retry limits
Backoff
Jitter
Conceptually:
flowchart TD
A[Request]
B{Redis healthy?}
C[Redis]
D[Circuit Breaker]
E{DB capacity available?}
F[Database]
G[Fallback / reject / stale response]
A --> B
B -->|Yes| C
B -->|No| D
D --> E
E -->|Yes| F
E -->|No| G
69. Never Use Unlimited Retries
Bad:
Redis timeout
↓
Retry
↓
Retry
↓
Retry
↓
Retry forever
During an outage every application instance begins retrying.
Now the recovering Redis server receives:
Massive retry storm.
Better:
Short timeout
Bounded retries
Exponential backoff
Jitter
Circuit breaker
70. Fail-Open vs Fail-Closed
Redis failure behavior depends on what Redis is doing.
Consider a cache:
Redis unavailable
↓
Query database
Usually reasonable.
Now consider a security control:
Redis stores temporary account lockout information.
Redis unavailable.
Should you:
Allow everyone?
Maybe not.
This introduces:
Fail-open vs fail-closed
| Redis responsibility | Possible failure behavior |
|---|---|
| Product cache | Bypass Redis |
| Recommendation cache | Serve stale/default |
| Session store | User may need to reauthenticate |
| Rate limiter | Business/security decision |
| Lock | Do not blindly continue |
| Feature flag | Use safe default |
| Counters | Queue/degrade depending on importance |
There is no universal fallback rule.
The fallback must match the business consequence.
71. Failure Handling Example
A useful cache algorithm looks conceptually like:
def get_product(product_id):
try:
product = redis.get(product_id)
if product:
return product
except RedisError:
# Cache unavailable.
# Continue carefully to source of truth.
pass
product = database.get_product(product_id)
try:
redis.set(product_id, product, ex=300)
except RedisError:
# Do not fail user request just because cache population failed.
pass
return product
Notice:
Redis failure
≠
Automatically fail customer request
because Redis is only a cache in this architecture.
72. Negative Caching
Suppose someone repeatedly requests:
product:999999
which does not exist.
Without negative caching:
Request
↓
Redis MISS
↓
Database
↓
Not found
Again:
Redis MISS
Database
Not found
A malicious or accidental flood can repeatedly hit the database.
You can temporarily cache:
NOT_FOUND
for a short TTL.
Example:
product:999999 → NOT_FOUND
TTL = 30 seconds
This is:
Negative caching
But use short TTLs because the object might later be created.
73. Hot Keys
Suppose nearly all traffic requests:
product:iphone-latest
That single key becomes extremely hot.
Redis
|
product:iphone-latest
/ / / / / / / / /
millions of requests
Even if Redis overall has enough capacity, extremely skewed key access can create localized load problems.
Monitor:
Traffic distribution
Command latency
Hot keys
Network throughput
CPU
Caching architecture is not just about total key count.
74. Large Values
Do not casually store enormous payloads under one key.
Example:
cache:everything
=
500 MB JSON
Problems include:
Network latency
Serialization cost
Memory pressure
Blocking/processing cost
Huge invalidations
Poor cache efficiency
Prefer appropriately sized objects:
product:100
product:101
product:102
when that matches your access pattern.
75. Redis Memory Is Not Infinite
Redis is often memory-centric.
That means capacity planning matters.
Think:
Number of keys
×
Average key size
×
Average value size
+
Redis structure overhead
+
Replication/persistence overhead
+
Operational headroom
Do not estimate only:
My JSON files total 5 GB,
therefore I need exactly 5 GB RAM.
Real Redis memory consumption includes metadata and internal representations.
76. TTL vs Eviction
These are different concepts.
TTL
You deliberately say:
This key should expire in 300 seconds.
Eviction
Redis is under configured memory pressure and removes keys according to the configured memory-management policy.
Therefore:
Expiration = data lifetime policy
Eviction = memory pressure policy
Do not design correctness around the assumption that a cache entry will always survive until its TTL.
77. What Should Be Cached?
Good caching candidates often have:
High read frequency
Expensive computation
Expensive DB query
Repeated identical access
Tolerance for controlled staleness
Reasonable object size
Examples:
Product catalog
Configuration
User profile summary
Search suggestions
Dashboard aggregates
Recommendation results
Reference data
78. What Should Not Automatically Be Cached?
Be cautious with:
Highly volatile authoritative financial data
One-time data rarely read again
Huge objects
Data with extremely low cache hit rate
Data where stale values are dangerous
Queries cheaper than cache management
Sensitive data without proper controls
Caching has a cost:
Code complexity
Invalidation logic
Memory
Observability
Failure modes
Staleness
Operational burden
A cache should solve a real performance or scalability problem.
79. Cache Hit Ratio
One useful metric is:
Cache Hit Ratio
=
Cache Hits
──────────────
Hits + Misses
Example:
Hits = 9,000
Misses = 1,000
Hit ratio:
9000 / 10000
=
90%
A high hit ratio often indicates that Redis is successfully shielding the backend.
But high hit ratio alone does not prove success.
You should also ask:
Did application latency improve?
Did database load decrease?
Is the data fresh enough?
What is Redis cost?
Are hot keys forming?
Are evictions increasing?
80. Important Redis Production Metrics
Monitor at least the following categories.
Performance
Command latency
p50
p95
p99
Operations/sec
Network throughput
Cache
Hits
Misses
Hit ratio
Evictions
Expired keys
Memory
Used memory
Memory fragmentation
maxmemory utilization
Large keys
Connections
Connected clients
Connection failures
Rejected connections
Pool exhaustion
Availability
Errors
Timeouts
Failovers
Replication health
Replication lag
Backend impact
Database QPS
Database CPU
Database connection utilization
Database latency
You need both Redis metrics and application/backend metrics to understand whether caching is helping.
81. Complete Production Cache Request
Now combine the concepts.
flowchart TD
U[Client Request]
A[Application]
R{Redis available?}
H{Cache Hit?}
C[Return Cached Value]
CB[Circuit Breaker / Protection]
DB[(Database)]
S[Store in Redis with TTL + Jitter]
RESP[Return Response]
FAIL[Fallback / Load Shed / Error]
U --> A
A --> R
R -->|Yes| H
R -->|No| CB
H -->|Yes| C
H -->|No| CB
CB -->|Backend capacity available| DB
CB -->|Backend protected| FAIL
DB --> S
S --> RESP
This is much closer to a real production cache than simply:
redis.get()
82. Complete E-Commerce Example
Let’s connect all the Redis use cases into one system.
flowchart TB
USER[Customers]
APP[Shopping Application]
REDIS[(Redis)]
DB[(SQL Database)]
PAYMENT[Payment Service]
EMAIL[Email Worker]
USER --> APP
APP --> REDIS
APP --> DB
APP --> PAYMENT
REDIS --> EMAIL
Redis could provide:
product:* → product cache
session:* → sessions
cart:* → shopping carts
rate:* → rate limiting
product:*:views → counters
leaderboard:* → sorted sets
lock:order:* → lightweight coordination
notifications → Pub/Sub
order-events → Streams
One Redis system therefore exposes several data-structure capabilities, but each use case has different durability and failure requirements.
83. Example Request — Product Page
Customer requests:
GET /products/100
Process:
1. Application receives request.
2. GET product:100 from Redis.
3. HIT?
YES
↓
Return product.
4. MISS?
↓
Query database.
5. Database returns product.
6. Store:
product:100
TTL = base + jitter
7. Return customer response.
84. Example Request — Add to Cart
Redis Hash:
HSET cart:user:100 product:500 2
Meaning:
User 100
Cart
Product 500
Quantity 2
Another:
HSET cart:user:100 product:600 1
Now:
cart:user:100
|
+-- product:500 = 2
|
+-- product:600 = 1
Add expiration if carts should disappear after inactivity.
85. Example Request — User Session
Login:
HSET session:abc123 \
user_id 100 \
authenticated 1
Set expiration:
EXPIRE session:abc123 1800
Any application node can now resolve:
session:abc123
86. Example Request — Product Views
Each product page:
INCR product:100:views
Across:
App-1
App-2
App-3
App-4
the counter is shared.
87. Example Request — Avoid Duplicate Order Processing
Worker attempts:
SET lock:order:7001 unique-token NX EX 15
Success:
Process order.
Failure:
Another worker currently owns the lease.
After work:
Atomic owner-token check
+
delete lock
88. Example — Live Order Notification
Publisher:
PUBLISH orders "Order 7001 confirmed"
Subscribers:
WebSocket server
Notification service
Dashboard
receive it while connected.
If the business requirement becomes:
"This message must never simply disappear."
do not rely solely on Pub/Sub.
Use something durable such as:
Redis Streams
Kafka
another durable broker
depending on requirements.
89. When Redis Should NOT Be Used
This is just as important as knowing when Redis should be used.
Do not automatically use Redis simply because:
Redis is fast.
90. Do Not Use Redis as a Replacement for Every Database
Suppose you need:
SELECT customer.name,
SUM(order.amount)
FROM customers
JOIN orders ...
JOIN invoices ...
WHERE ...
GROUP BY ...
A relational database is designed for this type of workload.
Redis does not automatically replace:
PostgreSQL
MySQL
Oracle
SQL Server
91. Avoid Redis for Huge Cold Datasets
Imagine:
20 TB archive
that is accessed:
once every three months.
Putting the entire dataset in expensive memory may provide little benefit.
A disk/object-storage/database architecture may be more appropriate.
92. Avoid Redis for Large Binary Objects
Avoid treating Redis as:
Image repository
Video repository
Backup repository
ISO repository
Massive file store
Instead use systems designed for large objects:
Amazon S3
Google Cloud Storage
Azure Blob Storage
Object storage/CDN
Redis may cache metadata or small hot objects, but large binary storage is usually a poor fit.
93. Avoid Pub/Sub for Durable Messaging
Requirement:
PaymentCreated event MUST be processed.
Subscriber crashes.
With Pub/Sub:
Message may disappear.
That violates the requirement.
Redis officially documents Pub/Sub’s at-most-once semantics.
Use:
Redis Streams
Kafka
RabbitMQ
or another durable messaging architecture
depending on the system’s requirements.
94. Do Not Use Redis Locks as Magical Distributed Transactions
This:
SET lock:money-transfer NX EX 10
does not suddenly provide:
Global transactional correctness
Distributed consensus
Exactly-once execution
Financial settlement guarantees
Distributed systems can experience:
Network delays
Process pauses
Clock/time issues
Lease expiration
Failover
Partial failure
Use Redis locking for problems whose correctness model fits a lease-based lock.
95. Avoid Redis When Caching Gives No Benefit
Suppose:
Request A reads unique data once.
Request B reads different unique data once.
Request C reads different unique data once.
Cache hit ratio approaches:
0%
Now Redis adds:
Network call
Serialization
Memory
Infrastructure cost
Operational complexity
without meaningfully reducing database work.
Do not cache merely because caching is fashionable.
96. Redis Decision Matrix
| Requirement | Redis? | Feature |
|---|---|---|
| Fast product cache | ✅ | String / Hash |
| User sessions | ✅ | Hash/String + TTL |
| Distributed counter | ✅ | INCR |
| Leaderboard | ✅ | Sorted Set |
| Unique membership | ✅ | Set |
| Live ephemeral notifications | ✅ | Pub/Sub |
| Durable event processing | ✅ | Streams |
| Approximate unique visitor count | ✅ | HyperLogLog |
| Temporary coordination | ✅ | SET NX EX |
| Large relational joins | ❌ | Use relational DB |
| Huge video archive | ❌ | Use object storage |
| Durable Pub/Sub-style event history | ❌ Pub/Sub | Use Streams/Kafka/etc. |
| Strong consensus | ❌ | Use appropriate coordination mechanism |
| Low reuse / zero cache hits | Usually ❌ | Cache may add no value |
97. Cache-Aside vs Write-Through Summary
CACHE-ASIDE
Application
|
+--> Redis
|
+--> Database
Application owns cache logic.
READ-THROUGH
Application
|
v
Cache abstraction
|
+--> Redis
|
+--> Database loader
Cache abstraction owns loading logic.
WRITE-THROUGH
Application
|
v
Cache abstraction
|
+--> Cache
|
+--> Database
Both are updated on the write path.
98. TTL Summary
Never ask only:
What TTL should Redis use?
Ask:
How stale may this data safely become?
How often does it change?
How expensive is reconstruction?
What happens when thousands of keys expire?
Can we actively invalidate it?
Then choose:
TTL
+
Jitter
+
Invalidation
+
Stampede strategy
99. Cache Invalidation Summary
Four common mechanisms:
1. TTL expiration
2. Delete on database change
3. Update cache on database change
4. Event-driven invalidation
A strong production architecture often combines:
Active invalidation
+
TTL safety net
100. Stampede Summary
Problem:
Popular key expires
↓
Thousands of MISSes
↓
Thousands of DB queries
↓
Database overload
Solutions include:
TTL jitter
Single refresher / mutex
Stale-while-revalidate
Request coalescing
Early refresh
Backend rate protection
101. Connection Pooling Summary
Do:
Application process
|
v
Reusable Redis client
|
Pool / multiplexed connections
|
v
Redis
Avoid:
Every request
↓
New connection
↓
Redis
↓
Close connection
And remember:
pool size
×
application instance count
=
total potential connections
102. Failure Handling Summary
For each Redis use case ask:
What happens if Redis disappears for 30 seconds?
Cache:
Maybe bypass Redis.
Session:
Maybe temporarily unavailable/re-authenticate.
Rate limiter:
Choose fail-open or fail-closed intentionally.
Lock:
Do not blindly continue.
Pub/Sub:
Messages during disconnect may be lost.
Counter:
Decide whether temporary loss/unavailability matters.
The same Redis failure cannot have one identical fallback strategy for every use case.
103. Production Redis Golden Rules
Rule 1
Treat the database as the source of truth when Redis is being used purely as a cache.
Rule 2
Use the Redis data structure that naturally represents the problem.
Rule 3
Do not cache everything.
Rule 4
Give temporary cache data a deliberate expiration strategy.
Rule 5
Use TTL jitter for large groups of similarly expiring keys.
Rule 6
Design cache invalidation before production.
Rule 7
Protect the database from cache failure.
Rule 8
Use bounded retries and timeouts.
Rule 9
Reuse Redis connections through the architecture supported by your client.
Rule 10
Monitor hits, misses, memory, evictions, latency and backend load.
Rule 11
Do not use Pub/Sub when you require durable delivery.
Rule 12
Treat Redis locks as leases, not magical distributed transactions.
Rule 13
Use unique lock ownership tokens.
Rule 14
Never depend blindly on stale cached data for critical decisions.
Rule 15
Design Redis failure behavior before Redis actually fails.
104. The Complete Mental Model
If you remember only one diagram from this tutorial, remember this:
flowchart TB
CLIENT[Clients]
APP[Application Layer]
POOL[Redis Client / Connection Pool]
REDIS[(Redis)]
DB[(Authoritative Database)]
EVENTS[Durable Event System]
CLIENT --> APP
APP --> POOL
POOL --> REDIS
APP --> DB
APP --> EVENTS
REDIS -->|Cache / Sessions / Counters / Locks| APP
DB -->|Source of Truth| APP
Redis is positioned where fast shared state creates value.
105. Redis in One Sentence
Redis is a high-speed shared data-structure store that applications commonly use to reduce expensive work and coordinate temporary state, but every Redis use case must have deliberate policies for freshness, expiration, durability and failure.
106. Final Topic Map
You can now connect the complete learning journey:
flowchart TD
A[Redis Fundamentals]
B[Redis Data Structures]
C[Caching]
D[Cache-Aside]
E[Read/Write-Through]
F[TTL]
G[Cache Invalidation]
H[Stampede & Staleness]
I[Pub/Sub]
J[Sessions]
K[Counters]
L[Distributed Locks]
M[Connection Pooling]
N[Failure Handling]
O[When NOT to Use Redis]
P[Production Redis Architecture]
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
H --> I
H --> J
H --> K
H --> L
I --> M
J --> M
K --> M
L --> M
M --> N
N --> O
O --> P
107. Final GOLD Standard Comparison
| Topic | Core Question | Key Redis Concept |
|---|---|---|
| Redis | Why use it? | Fast shared state |
| Data structures | How should data be represented? | String/Hash/List/Set/ZSet/Stream |
| Cache-aside | Who manages cache misses? | Application |
| Read-through | Who loads missing data? | Cache abstraction |
| Write-through | How are writes synchronized? | Cache + DB write path |
| TTL | How long may data live? | Expiration |
| Invalidation | How do we remove stale copies? | Delete/update/event |
| Stampede | What if many misses occur together? | Jitter/locking/SWR |
| Stale data | How old may data safely become? | Freshness policy |
| Pub/Sub | Need real-time ephemeral broadcast? | PUBLISH / SUBSCRIBE |
| Sessions | Need shared temporary user state? | Hash/String + TTL |
| Counters | Need atomic shared numbers? | INCR |
| Lightweight locks | Need temporary coordination? | SET NX EX/PX |
| Pooling | How should applications connect efficiently? | Reuse/multiplex |
| Failure handling | What happens when Redis fails? | Fallback/circuit breaker |
| Don’t use Redis | Is Redis actually the right system? | Architectural judgment |
108. The Most Important Redis Lesson
Learning Redis commands is relatively easy:
GET
SET
DEL
EXPIRE
HSET
SADD
ZADD
INCR
PUBLISH
SUBSCRIBE
The difficult—and valuable—part is learning to answer:
Should this data be cached?
Who owns the truth?
How long may the cached copy live?
How is it invalidated?
What if the key expires during heavy traffic?
What if Redis goes down?
What if the database goes down?
Can stale data hurt the business?
Do I need durability?
Do I need atomicity?
Do I actually need Redis?
When you can answer those questions, you are no longer simply using Redis commands.
You are designing a production Redis architecture.