Redis Cache Mastery: Real-World Scenarios & Distributed Lock Solutions for Java
This article examines common Redis caching challenges in Java high‑performance architectures—such as lock loss, cache hot‑spot rebuild, cache penetration, and avalanche—through eleven real‑world scenarios, and provides practical solutions including RedLock alternatives, Zookeeper, DCL, tryLock, multi‑level caching, and Lua‑based read‑write locks.
Scenario 1: Small‑to‑Medium Company Redis Cache Architecture and Live Issues
Thread A acquires a lock on the master, but the master crashes before syncing data to the slave. The slave is promoted to master, and Thread B acquires the lock successfully, causing multiple threads to hold the same lock.
Solution
RedLock is often cited, but it requires a majority of Redis nodes to succeed, similar to Zookeeper's ZAB algorithm, and can degrade Redis availability by turning its AP model into CP, which contradicts Redis's design.
Zookeeper provides CP guarantees and implements ZAB, ensuring consistency even when a node fails, and generally offers higher efficiency for distributed locks than Redis.
Scenario 2: Large‑Scale Product Cache Hot‑Cold Separation in a Big Company
Problem
In high‑concurrency environments, keeping all cache data alive can waste memory and increase maintenance cost.
Solution
Implement hot‑cold separation: refresh the expiration time on each read for hot data so it stays in cache, while cold data expires automatically.
Process Analysis
Hot items are accessed frequently; each access updates the TTL, keeping them cached.
Cold items expire, freeing memory, and are fetched from the database when needed, ensuring up‑to‑date data and reducing maintenance.
Scenario 3: Using DCL to Solve Hot Cache Rebuild Contention
Problem
When cold data suddenly becomes hot, a surge of requests can cause massive cache rebuild pressure.
Solution
Simple locking.
Double‑checked locking (DCL): first check cache, if miss acquire lock, check again inside lock, then load from DB and populate cache, reducing DB hits.
public Product get(Long productId) {
Product product = null;
String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;
// DCL: first read from cache
product = getProductFromCache(productCacheKey);
if (product != null) {
return product;
}
// Distributed lock to avoid hot rebuild
RLock hotCreateCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_CREATE_PREFIX + productId);
hotCreateCacheLock.lock();
try {
product = getProductFromCache(productCacheKey);
if (product != null) {
return product;
}
// Read‑write lock for cache‑DB consistency
RReadWriteLock productUpdateLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
RLock rLock = productUpdateLock.readLock();
rLock.lock();
try {
product = productDao.get(productId);
if (product != null) {
redisUtil.set(productCacheKey, JSON.toJSONString(product), genProductCacheTimeout(), TimeUnit.SECONDS);
} else {
// Empty cache to prevent penetration
redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);
}
} finally {
rLock.unlock();
}
} finally {
hotCreateCacheLock.unlock();
}
return product;
}Scenario 4: Massive Lock Contention During Flash‑Sale Peaks
Problem
When 100,000 threads wait for a lock, each thread may need to compete multiple times, increasing Redis lock overhead.
Solution
Use tryLock with a timeout so threads attempt to acquire the lock for a limited period; if the lock is released early, waiting threads can proceed without additional contention.
Scenario 5: Cache Penetration Causing Database Overload
Problem
Simultaneous requests for non‑existent keys bypass cache and hit the database, overwhelming it under high concurrency.
Solution
When setting expiration, add a random offset to each key so they do not expire simultaneously.
private Integer genProductCacheTimeout() {
// Randomized timeout to mitigate cache avalanche
return PRODUCT_CACHE_TIMEOUT + new Random().nextInt(5) * 60 * 60;
}Scenario 6: Cache Penetration Leading to Database Crash
Problem
Attackers repeatedly query nonexistent parameters, forcing the system to hit the database continuously.
Solution
Gateway rate limiting (Nginx, Sentinel, Hystrix).
Multi‑level cache: Bloom filter as first‑level, Guava cache as second, Redis as third.
Use Bloom filter to quickly reject definitely‑absent keys.
Scenario 7: Live‑Stream Sales Spike Causing System Collapse
Problem
A sudden surge of requests for a product that was previously cold can overwhelm the database if the cache has expired.
Solution
Apply tryLock with timeout as in Scenario 4 to serialize cache rebuild while allowing parallelism.
Scenario 8: Redis Distributed Lock for Cache‑DB Write Consistency
Solution
Re‑entrant lock to ensure safety.
Read‑write lock for read‑heavy, write‑light workloads; keep lock keys consistent.
Canal to listen to binlog and update cache proactively.
Lua Script for ReadWriteLock
local mode = redis.call('hget', KEYS[1], 'mode');
if (mode == false) then
redis.call('hset', KEYS[1], 'mode', 'read');
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('set', KEYS[2] .. ':1', 1);
redis.call('pexpire', KEYS[2] .. ':1', ARGV[1]);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end
if (mode == 'read' or (mode == 'write' and redis.call('hexists', KEYS[1], ARGV[3]) == 1)) then
local ind = redis.call('hincrby', KEYS[1], ARGV[2], 1);
local key = KEYS[2] .. ':' .. ind;
redis.call('set', key, 1);
redis.call('pexpire', key, ARGV[1]);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end
return redis.call('pttl', KEYS[1]);Scenario 9: Segment Locks to Reduce Distributed Lock Contention
Solution
Divide a single lock into multiple segment locks (similar to ConcurrentHashMap) and distribute inventory across them, allowing concurrent access and improving throughput.
Scenario 10: Multi‑Level Cache to Prevent Cache Avalanche
Problem
When Redis fails or is overloaded, a flood of requests hits the database.
Solution
Gateway rate limiting.
Multi‑level cache: Bloom filter → Guava → Redis, with Bloom filter preventing massive miss storms.
Scenario 11: Viral Social Media Event Causing System Crash
Problem
A sudden spike of millions of requests can saturate a single Redis node, leading to a cascade failure.
Solution
Apply the same multi‑level cache and rate‑limiting strategies described in Scenario 10.
Scenario 12: Enterprise‑Grade Hot‑Data Handling
Solution
Use publish/subscribe or MQ to propagate hot‑data updates across nodes.
Adopt a dedicated hot‑data cache system that applications listen to, reducing coupling and maintenance.
Instrument data‑access points with AOP to feed hot‑data metrics for analysis.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
