Redis请求触发式缓存流程:高并发场景下的运行逻辑问询
Hey, great question—this is a super common pain point with naive cache-aside patterns when traffic spikes out of nowhere. Let’s break down exactly what happens with your sample code, then talk about how to fix it.
低流量下的正常运行流程
First, let’s recap how your code works when traffic is light:
- A request comes in, checks if
contentexists in Redis withredis->hasKey('content') - If the key doesn’t exist, it fetches the data from the database
- Stores the fetched content in Redis with a 10-second expiration
- Returns the content to the user
- Subsequent requests for the next 10 seconds will hit Redis directly, no DB calls needed
1000并发请求下的问题(缓存风暴/击穿)
Now imagine 1000 requests hit that page at the exact same time, when the content key isn’t in Redis yet:
- All 1000 requests will pass the Redis check and jump straight to the database query
- Your database gets slammed with 1000 concurrent requests for the same data
- This can cause DB slowdowns, timeouts, or even crash the database entirely—especially if it’s not sized for that kind of sudden load
- Even once the first request finishes writing to Redis, many of the other 999 requests will already be in the middle of their DB calls, since Redis writes are fast but not instantaneous
This problem is often called cache stampede or cache breakdown for cold-start (or expired) hot keys.
解决方案:分布式锁
The most common fix is to add a distributed lock so only one request can fetch from the database at a time. Here’s how to adjust your pseudocode:
if(redis->hasKey('content')) { return content; } else { // Acquire a distributed lock with a timeout (prevents deadlocks if the request fails) if(redis->lock('content_lock', 5)) { try { // Double-check Redis—another request might have filled the cache while we waited for the lock if(redis->hasKey('content')) { return content; } // Only this request hits the DB content = get_content_from_database(); cache_content_in_redis(); content_expire(10); return content; } finally { // Always release the lock, even if something goes wrong redis->unlock('content_lock'); } } else { // Didn't get the lock? Wait a tiny bit and retry the request sleep(100); // 100ms delay—adjust based on your DB response time return handle_request(); // Recurse to re-run the cache check } }
How this works:
- Only one request gets the lock and fetches from the database
- All other requests wait a short time, then retry—by that point, the cache will be populated, so they’ll hit Redis instead of the DB
- The double-check of Redis after acquiring the lock is critical: if another request filled the cache while we were waiting for the lock, we don’t want to hit the DB unnecessarily
Other Optional Fixes
If distributed locks feel overkill for your use case, here are a few alternatives:
- Cache Warmup: If you know a page is going to get traffic (e.g., a product launch), pre-load the content into Redis before the spike hits
- Never Expire + Async Updates: Set the cache to never expire, and have a background job periodically refresh the content from the database (good for data that doesn’t need to be 100% real-time)
- Local Caching: Add a local cache (like Guava Cache or Caffeine) to each application instance—this reduces the number of requests hitting Redis, and further cuts down on DB calls during spikes
内容的提问来源于stack exchange,提问作者MiKE

