当前位置: 首页 > news >正文

Redis场景问题2:缓存击穿

Redis 缓存击穿是指在缓存系统中,大量请求(高并发访问)同时访问一个不存在于缓存中(一般是因为缓存过期或者数据未被加载到缓存)但在数据库中存在的热点数据,从而导致这些请求直接穿透缓存层,涌向数据库,可能造成数据库压力过大甚至崩溃的情况。以下是关于它的详细介绍:

  • 产生原因:一般是由于缓存中的某个热点数据过期,而此时有大量并发请求过来访问该数据。这些请求在发现缓存中没有对应数据后,都会去数据库中查询,从而形成对数据库的集中式访问压力。例如电商网站中某个热门商品的信息缓存过期,而此时大量用户同时访问该商品详情页,就可能引发缓存击穿问题。
  • 解决方案
    • 逻辑过期:不设置TTL,之前所说导致缓存击穿的原因就是该key的TTL到期了,所以我们在这就不设置TTL了,而是使用一个字段,例如:expire表示过期时间(逻辑上的)。当我们想让它 “ 过期 ” 的时候,我们可以直接手动将其删除(热点key,即只是在一段时间内,其被访问的频次很高。这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。
    • 加互斥锁(Mutex Lock):在缓存数据过期后,当第一个请求来访问时,先获取一个互斥锁,然后去数据库加载数据并更新到缓存中,其他请求则等待该锁释放后再从缓存中获取数据。通过这种方式,可以确保在同一时刻只有一个请求去数据库查询数据,避免大量请求同时穿透到数据库。例如在 Java 中可以使用 ReentrantLock 来实现互斥锁。以下是一个简单的示例代码:
import java.util.concurrent.locks.ReentrantLock;

public class CacheBreakthroughHandler {

    private static final ReentrantLock lock = new ReentrantLock();
    private static final RedisCache redisCache = new RedisCache(); // 假设这是一个操作Redis缓存的类

    public static Object getData(String key) {
        Object data = redisCache.get(key);
        if (data == null) {
            try {
                lock.lock();
                // 再次检查缓存,防止在获取锁的过程中其他线程已经更新了缓存
                data = redisCache.get(key);
                if (data == null) {
                    // 从数据库获取数据
                    data = getDataFromDatabase(key);
                    if (data != null) {
                        // 将数据放入缓存
                        redisCache.set(key, data);
                    }
                }
            } finally {
                lock.unlock();
            }
        }
        return data;
    }

    private static Object getDataFromDatabase(String key) {
        // 这里是从数据库获取数据的逻辑,根据实际情况实现
        return null;
    }
}

这两种解决方案对比:

  • 使用分布式锁(如 Redisson):在分布式系统环境下,可以使用分布式锁来替代互斥锁。分布式锁能够在多个节点之间实现互斥访问,确保同一时刻只有一个节点去数据库加载数据。例如使用 Redisson 框架来实现分布式锁,它基于 Redis 实现了各种分布式锁的功能,以下是一个简单的示例代码:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class CacheBreakthroughHandler {

    private static final RedissonClient redissonClient;
    private static final RedisCache redisCache = new RedisCache(); // 假设这是一个操作Redis缓存的类

    static {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        redissonClient = Redisson.create(config);
    }

    public static Object getData(String key) {
        Object data = redisCache.get(key);
        if (data == null) {
            RLock lock = redissonClient.getLock("lock:" + key);
            try {
                lock.lock();
                // 再次检查缓存,防止在获取锁的过程中其他线程已经更新了缓存
                data = redisCache.get(key);
                if (data == null) {
                    // 从数据库获取数据
                    data = getDataFromDatabase(key);
                    if (data != null) {
                        // 将数据放入缓存
                        redisCache.set(key, data);
                    }
                }
            } finally {
                lock.unlock();
            }
        }
        return data;
    }

    private static Object getDataFromDatabase(String key) {
        // 这里是从数据库获取数据的逻辑,根据实际情况实现
        return null;
    }
}

  • 缓存预热:在系统启动或者数据初始化阶段,提前将一些热点数据加载到缓存中,避免在系统运行过程中因为缓存未命中而导致缓存击穿。可以通过定时任务或者在系统启动时执行的初始化方法来实现缓存预热。例如,在电商系统启动时,将热门商品的信息提前加载到缓存中。
  • 二级缓存:采用二级缓存架构,例如在应用服务器内存中设置一级缓存,在 Redis 中设置二级缓存。当请求访问数据时,先从一级缓存中查找,如果未命中再从二级缓存中查找,若二级缓存也未命中,则去数据库查询并将数据依次放入二级缓存和一级缓存。这样可以在一定程度上减轻 Redis 缓存的压力,降低缓存击穿的风险。
http://www.dtcms.com/a/99729.html

相关文章:

  • VMware笔记之windows的vmware17pro中的ubuntu22.04调整系统空间大小
  • C#里实现C#脚本单步执行的信息提示
  • 算法 之 求解有向图和无向图的环的长度
  • CSS学习笔记4——盒子模型
  • IO模型之于并发编程模型、并发模型之于架构模式
  • 破界·共生:生成式人工智能(GAI)认证重构普通人的AI进化图谱
  • SpringCould微服务架构之Docker(6)
  • 【C#】C#字符串拼接的6种方式及其性能分析对比
  • Axure项目实战:智慧运输平台后台管理端-货主管理(中继器)
  • 21 python __name__ 与 __main__
  • Java中的String类
  • 智能巡检机器人:2025年企业安全运维的“数字哨兵“
  • Vue 3 中 slot插槽的使用方法
  • 最大子序和 买股票的最佳时机|| 跳跃游戏
  • 【计算机网络】深入解析TCP/IP参考模型:从四层架构到数据封装,全面对比OSI
  • 面经-项目
  • 革新测试管理 2.0丨Storm UTP统一测试管理平台智能化升级与全流程优化
  • HCIP之VRRP
  • 晶晨S905L3A(B)-安卓9.0-开启ADB和ROOT-支持IPTV6-支持外置游戏系统-支持多种无线芯片-支持救砖-完美通刷线刷固件包
  • memtest86检测内存
  • Anaconda Jupyter 默认启动位置修改
  • 矩阵中对角线的遍历问题【C++】
  • JavaScript运算符与逻辑中断
  • 从零到前沿:2025年人工智能系统性学习路径与最新技术融合指南
  • LangChain 基础系列之文档加载与分割详解:从非结构化数据到知识图谱的关键一步
  • ubuntu24 部署vnc server 使用VNC Viewer连接
  • vLLM 实现加速的原理及举例; vLLM 与 TensorRT 的区别
  • C#里使用C#语言作为脚本运行的方法
  • HarmonyOS NEXT——鸿蒙神策埋点(二)
  • 了解遗传算法的Matlab程序的奥妙之处