【Redis】什么是缓存击穿 ? 怎么解决

缓存击穿(Cache Breakdown)是指缓存中某个热点数据在某一时刻失效,大量并发请求同时查询这个数据,由于缓存失效,这些请求会直接打到数据库,可能导致数据库瞬间负载过高,甚至崩溃。与缓存穿透不同,缓存击穿是针对一个特定的热点数据在高并发场景下的失效问题。

解决缓存击穿的方法

  1. 互斥锁(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";
        }
    }
    
  2. 预热缓存

    • 在缓存即将过期或失效前,提前主动刷新缓存,确保热点数据始终在缓存中,避免大量并发请求直接打到数据库。
    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";
        }
    }
    
  3. 设置合理的过期时间和使用随机过期时间

    • 为不同的缓存数据设置不同的过期时间,避免大量缓存同时失效。
    • 使用随机过期时间可以防止缓存雪崩(大量缓存同时失效导致的缓存击穿)。
    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";
        }
    }
    

总结

通过使用互斥锁、预热缓存、设置合理的过期时间以及使用随机过期时间等方法,可以有效防止缓存击穿。结合具体的业务场景,选择适合的方法来预防和解决缓存击穿问题,确保系统在高并发情况下的稳定性和性能。

  • 31
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值