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

面试基础--Redis 缓存穿透、缓存击穿、缓存雪崩深度解析

Redis 缓存穿透、缓存击穿、缓存雪崩深度解析:布隆过滤器与热点数据预热

引言

在高并发的互联网应用中,缓存是提升系统性能的重要手段。Redis 作为最流行的内存数据库之一,广泛应用于缓存场景。然而,缓存系统也会面临缓存穿透、缓存击穿和缓存雪崩等问题。本文将深入探讨这些问题的成因及解决方案,结合实际项目案例和源码分析,帮助读者深入理解其实现原理。

1. 缓存穿透

缓存穿透是指查询一个不存在的数据,导致请求直接落到数据库上,从而压垮数据库。

1.1 缓存穿透的成因

  • 恶意攻击:攻击者故意查询不存在的数据。
  • 数据误删:缓存中的数据被误删,导致查询直接落到数据库。

1.2 解决方案:布隆过滤器

布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中。布隆过滤器可以快速判断一个数据是否存在于缓存中,从而避免缓存穿透。

1.2.1 布隆过滤器的工作原理

布隆过滤器通过多个哈希函数将元素映射到一个位数组中。查询时,如果所有哈希函数对应的位都为 1,则元素可能存在;如果有一个位为 0,则元素一定不存在。

元素
哈希函数1
哈希函数2
哈希函数3
位数组
1.2.2 布隆过滤器的源码分析

以下是 Java 中布隆过滤器的实现示例:

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterExample {
    public static void main(String[] args) {
        BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 1000, 0.01);
        bloomFilter.put(1);
        bloomFilter.put(2);
        bloomFilter.put(3);

        System.out.println(bloomFilter.mightContain(1)); // true
        System.out.println(bloomFilter.mightContain(4)); // false
    }
}

1.3 实际项目案例

在一个电商平台的商品搜索系统中,攻击者可能通过构造不存在的商品 ID 进行恶意查询。通过布隆过滤器,可以快速过滤掉不存在的商品 ID,避免缓存穿透。

public class ProductService {
    private BloomFilter<String> bloomFilter;

    public ProductService() {
        bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), 1000000, 0.01);
    }

    public Product getProduct(String productId) {
        if (!bloomFilter.mightContain(productId)) {
            return null;
        }
        // 查询缓存
        Product product = cache.get(productId);
        if (product == null) {
            // 查询数据库
            product = database.get(productId);
            if (product != null) {
                cache.put(productId, product);
            }
        }
        return product;
    }
}

2. 缓存击穿

缓存击穿是指一个热点数据在缓存中过期,导致大量请求直接落到数据库上,从而压垮数据库。

2.1 缓存击穿的成因

  • 热点数据过期:热点数据在缓存中过期,导致大量请求直接落到数据库。
  • 高并发访问:大量请求同时访问同一个热点数据。

2.2 解决方案:热点数据预热

热点数据预热是指在热点数据过期前,提前将其加载到缓存中,从而避免缓存击穿。

2.2.1 热点数据预热的实现

通过定时任务或消息队列,提前将热点数据加载到缓存中。

定时任务 缓存 数据库 查询热点数据 返回热点数据 加载热点数据 定时任务 缓存 数据库
2.2.2 热点数据预热的源码分析

以下是 Java 中热点数据预热的实现示例:

public class HotDataPreheat {
    private Cache cache;
    private Database database;

    public HotDataPreheat(Cache cache, Database database) {
        this.cache = cache;
        this.database = database;
    }

    public void preheat() {
        List<String> hotDataIds = database.getHotDataIds();
        for (String id : hotDataIds) {
            Data data = database.getData(id);
            cache.put(id, data);
        }
    }
}

2.3 实际项目案例

在一个新闻推荐系统中,热点新闻的访问量非常高。通过热点数据预热,可以提前将热点新闻加载到缓存中,避免缓存击穿。

public class NewsService {
    private Cache cache;
    private Database database;
    private HotDataPreheat hotDataPreheat;

    public NewsService(Cache cache, Database database) {
        this.cache = cache;
        this.database = database;
        this.hotDataPreheat = new HotDataPreheat(cache, database);
    }

    public void startPreheatTask() {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(hotDataPreheat::preheat, 0, 1, TimeUnit.HOURS);
    }
}

3. 缓存雪崩

缓存雪崩是指大量缓存数据在同一时间过期,导致大量请求直接落到数据库上,从而压垮数据库。

3.1 缓存雪崩的成因

  • 缓存数据同时过期:大量缓存数据在同一时间过期,导致大量请求直接落到数据库。
  • 高并发访问:大量请求同时访问数据库。

3.2 解决方案:缓存数据过期时间随机化

通过将缓存数据的过期时间随机化,避免大量缓存数据在同一时间过期。

3.2.1 缓存数据过期时间随机化的实现

在设置缓存数据的过期时间时,增加一个随机值,使得缓存数据的过期时间分散。

客户端 缓存 数据库 查询数据 返回数据 设置缓存数据,过期时间随机化 查询数据 返回数据 客户端 缓存 数据库
3.2.2 缓存数据过期时间随机化的源码分析

以下是 Java 中缓存数据过期时间随机化的实现示例:

public class CacheService {
    private Cache cache;
    private Database database;

    public CacheService(Cache cache, Database database) {
        this.cache = cache;
        this.database = database;
    }

    public Data getData(String id) {
        Data data = cache.get(id);
        if (data == null) {
            data = database.getData(id);
            if (data != null) {
                int expireTime = 3600 + new Random().nextInt(600); // 过期时间随机化
                cache.put(id, data, expireTime);
            }
        }
        return data;
    }
}

3.3 实际项目案例

在一个电商平台的商品详情页中,大量商品信息缓存在 Redis 中。通过缓存数据过期时间随机化,可以避免大量缓存数据在同一时间过期,导致缓存雪崩。

public class ProductService {
    private Cache cache;
    private Database database;

    public ProductService(Cache cache, Database database) {
        this.cache = cache;
        this.database = database;
    }

    public Product getProduct(String productId) {
        Product product = cache.get(productId);
        if (product == null) {
            product = database.getProduct(productId);
            if (product != null) {
                int expireTime = 3600 + new Random().nextInt(600); // 过期时间随机化
                cache.put(productId, product, expireTime);
            }
        }
        return product;
    }
}

4. 总结

在高并发的互联网应用中,缓存穿透、缓存击穿和缓存雪崩是常见的缓存问题。通过布隆过滤器、热点数据预热和缓存数据过期时间随机化等解决方案,可以有效避免这些问题,提升系统性能。

在实际项目中,深入理解这些问题的成因及其解决方案,结合源码分析和实际案例,可以帮助我们更好地设计和优化缓存系统。

希望本文能为你在实际项目中优化 Redis 缓存提供帮助。


参考文献:

  • Redis 官方文档
  • Google Guava 布隆过滤器
  • MyBatis 源码

http://www.kler.cn/a/579529.html

相关文章:

  • LLM论文笔记 17: Program of Thoughts Prompting (PoT)
  • 在 Ubuntu 20.04 上交叉编译 Qt 5 应用,使其可在 Windows 运行
  • Elasticsearch如何删除字段
  • Linux系统基于ARM平台的LVGL移植
  • clickhouse 频繁刷新
  • 算法与数据结构(最长回文子串)
  • PTA L2一些题目
  • 学习网络安全需要哪些基础?
  • ubuntu直接安装mobaxterm
  • 大模型最新面试题系列:训练篇之模型监控与调试
  • CarPlanner:用于自动驾驶大规模强化学习的一致性自回归轨迹规划
  • 串口助手的C#编写以及有人串口服务器USR-DR301的使用
  • 《用Python+PyGame开发双人生存游戏!源码解析+完整开发思路分享》
  • QT——对象树
  • 数据分析和可视化课程实验报告一(数据分析基础)
  • 如何通過安裝輕量性圖形界面減少Linux服務器壓力
  • c#面试题整理7
  • 利用pdf.js+百度翻译实现PDF翻译,创建中文PDF
  • SpringBoot 自定义异常处理
  • SparkAi系统体验