Introduction
A cold cache — one with no data — means every initial request is a cache miss that hits the database. Cache warming pre-populates Redis with data before traffic arrives, ensuring fast responses from the very first request after a deployment or restart.
Key Concepts
- Cold Cache: A cache with no entries, causing 100% miss rate until populated.
- Warm Cache: A cache pre-loaded with expected data, providing hits from the start.
- Lazy Warming: Data is cached only on first access (default cache-aside behavior).
- Eager Warming: Data is proactively loaded into the cache before any request needs it.
Real World Context
After deploying a new version of your application, all Redis data might be flushed or your instance restarted. If your site serves 10,000 requests per second, a cold cache means all 10,000 requests hit the database simultaneously — a recipe for an outage. Warming the cache during deployment prevents this.
Deep Dive
Strategy 1: Startup Warming
Load critical data when the application boots:
pythonimport redis def warm_cache_on_startup(r, db): """Load frequently accessed data at application start""" # Warm top products products = db.query("SELECT * FROM products WHERE featured = true") pipe = r.pipeline(transaction=False) for product in products: pipe.set(f"cache:product:{product.id}", serialize(product), ex=3600) pipe.execute() print(f"Warmed {len(products)} products") # Warm configuration config = db.query("SELECT * FROM app_config") for item in config: r.set(f"cache:config:{item.key}", item.value, ex=86400) print(f"Warmed {len(config)} config entries")
Using a pipeline for bulk warming is critical — loading 10,000 keys one by one takes seconds, while a pipeline completes in milliseconds.
Strategy 2: Scheduled Warming
Refresh cache on a schedule before data goes stale:
pythonimport schedule import time def refresh_popular_products(r, db): """Run every 10 minutes to keep popular products warm""" products = db.query( "SELECT * FROM products ORDER BY views DESC LIMIT 1000" ) pipe = r.pipeline(transaction=False) for p in products: pipe.set(f"cache:product:{p.id}", serialize(p), ex=900) pipe.execute() schedule.every(10).minutes.do(refresh_popular_products, r, db)
This ensures the top 1,000 products are always cached, even if no one has accessed them recently.
Strategy 3: Deploy-Time Warming
Warm the cache as part of your deployment pipeline:
bash# In your CI/CD pipeline: # 1. Deploy new version # 2. Run cache warming script # 3. Switch traffic to new version python warm_cache.py --categories --featured-products --config
By warming before switching traffic, users never experience a cold cache.
Selective Warming
Do not warm everything — focus on data with the highest impact:
pythondef selective_warm(r, db): """Only warm data that is expensive and frequently accessed""" # High impact: frequently accessed + expensive to compute warm_analytics_dashboards(r, db) # 500ms DB query, 100 req/min warm_product_recommendations(r, db) # 2s ML inference, 50 req/min # Skip: cheap queries or rarely accessed data # warm_user_settings(r, db) # 5ms query, 1 req/day per user
Common Pitfalls
- Warming too much data — Loading your entire database into Redis wastes memory and time. Focus on the top 1-5% of data by access frequency.
- Not using pipelining for bulk loads — Warming 10,000 keys without pipelining takes 100x longer due to network round-trips.
Best Practices
- Warm before traffic switches — In blue-green deployments, warm the new environment's cache before routing traffic to it.
- Log warming metrics — Track how many keys were warmed and how long it took. Alert if warming fails or takes too long.
Summary
- A cold cache causes a burst of database load after deploys or restarts
- Eager warming pre-loads critical data using pipelines for efficiency
- Scheduled warming keeps popular data fresh between access windows
- Focus warming on high-impact data: frequently accessed and expensive to generate
Code Examples
import redis
def warm_cache(r, db):
"""Warm top 100 products using pipeline"""
products = db.query(
"SELECT * FROM products ORDER BY views DESC LIMIT 100"
)
pipe = r.pipeline(transaction=False)
for p in products:
pipe.set(f"cache:product:{p.id}", serialize(p), ex=3600)
pipe.execute()
print(f"Warmed {len(products)} products")