缓存击穿(Cache Breakdown)是指缓存中某个热点数据在某一时刻失效,大量并发请求同时查询这个数据,由于缓存失效,这些请求会直接打到数据库,可能导致数据库瞬间负载过高,甚至崩溃。与缓存穿透不同,缓存击穿是针对一个特定的热点数据在高并发场景下的失效问题。
解决缓存击穿的方法
-
互斥锁(Mutex)
- 当缓存失效后,通过加锁机制确保只有一个请求可以访问数据库并重建缓存,其他请求等待缓存重建完成后再从缓存读取数据。
import redis.clients.jedis.Jedis; public class CacheBreakdownExample { private static final String LOCK_KEY = "lock:key"; public static void main(String[] args) { Jedis jedis = new Jedis("localhost"); String key = "hotspot_key"; String value = jedis.get(key); if (value == null) { // 获取分布式锁 while (jedis.setnx(LOCK_KEY, "1") == 0) { try { // 锁等待时间 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // 再次检查缓存,防止重复重建缓存 value = jedis.get(key); if (value == null) { // 从数据库查询数据 value = getFromDatabase(key); // 更新缓存并设置过期时间 jedis.setex(key, 300, value); } // 释放锁 jedis.del(LOCK_KEY); } System.out.println("Value: " + value); jedis.close(); } private static String getFromDatabase(String key) { // 模拟数据库查询 return "database_value"; } }
-
预热缓存
- 在缓存即将过期或失效前,提前主动刷新缓存,确保热点数据始终在缓存中,避免大量并发请求直接打到数据库。
import redis.clients.jedis.Jedis; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class CachePrewarmExample { private static final String KEY = "hotspot_key"; private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public static void main(String[] args) { Jedis jedis = new Jedis("localhost"); // 初始化缓存 String value = getFromDatabase(KEY); jedis.setex(KEY, 300, value); // 定时任务预热缓存 scheduler.scheduleAtFixedRate(() -> { Jedis jedisInner = new Jedis("localhost"); String newValue = getFromDatabase(KEY); jedisInner.setex(KEY, 300, newValue); jedisInner.close(); }, 250, 250, TimeUnit.SECONDS); // 模拟访问 String cachedValue = jedis.get(KEY); System.out.println("Cached Value: " + cachedValue); jedis.close(); } private static String getFromDatabase(String key) { // 模拟数据库查询 return "database_value"; } }
-
设置合理的过期时间和使用随机过期时间
- 为不同的缓存数据设置不同的过期时间,避免大量缓存同时失效。
- 使用随机过期时间可以防止缓存雪崩(大量缓存同时失效导致的缓存击穿)。
import redis.clients.jedis.Jedis; public class RandomExpireExample { public static void main(String[] args) { Jedis jedis = new Jedis("localhost"); String key = "hotspot_key"; String value = getFromDatabase(key); // 设置带有随机性的过期时间 int expireTime = 300 + (int) (Math.random() * 100); jedis.setex(key, expireTime, value); System.out.println("Value: " + value); jedis.close(); } private static String getFromDatabase(String key) { // 模拟数据库查询 return "database_value"; } }
总结
通过使用互斥锁、预热缓存、设置合理的过期时间以及使用随机过期时间等方法,可以有效防止缓存击穿。结合具体的业务场景,选择适合的方法来预防和解决缓存击穿问题,确保系统在高并发情况下的稳定性和性能。
标签:缓存,String,Redis,击穿,value,jedis,key,Jedis From: https://blog.csdn.net/hui_zai_/article/details/139317133