缓存:热点key重建优化。

简介: 缓存:热点key重建优化。

e7b15ccc0c0f4e94afe2e938c54e58b5.png

开发人员使用”缓存+过期时间“的策略既可以加速数据读写,又保证数据的定时更新,这种模式基本满足绝大部分需求。但是有两个问题如果同时出现,可能就会对应用造成致命的危害:

  • 当前key是一个热点key(例如一个热门的娱乐新闻),并发量非常大。
  • 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的SQL、多次IO、多个依赖等。

在缓存失效的瞬间,有大量线程来重建缓存(如下图所示),造成后端负载加大,甚至可能会让应用奔溃。

要解决这个问题也不是很复杂,但是不能为了解决这个问题给系统带来更多的麻烦,所以需要制定如下目标:

  • 减少重建缓存的次数。
  • 数据尽可能一致。
  • 较少的潜在危险。

互斥锁(mutex key)

此方法只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可,整个过程如下图所示。

下面代码使用Redis的setnx命令实现上述功能:

String get(String key) {
    // 从Redis中获取数据
    String value = redis.get(key);
    // 如果value为空,则开始重构缓存
    if(value == null) {
        // 只允许一个线程重构缓存,使用nx,并设置过期时间ex
        String mutexKey = "mutext:key:" + key;
        if(redis.set(mutexKey, "1", "ex 180", "nx")) {
            // 从数据源获取数据
            value = db.get(key);
            // 回写Redis,并设置过期时间
            redis.setex(key, timeout, value);
            // 删除key_mutex
            redis.delete(mutex_key);
        }
        // 其他线程休息50毫秒后重试
        else {
            Thread.sleep(50);
            get(key);
        }
    }
    return value;
}
  • 从Redis获取数据,如果值不为空,则直接返回值;否则执行下面2和3步骤。
  • 如果set(nx和ex)结果为true,说明此时没有其他线程重建缓存,那么当前线程执行缓存构建逻辑。
  • 如果set(nx和ex)结果为false,说明此时已经有其他线程正在执行构建缓存的工作,那么当前线程将休息指定时间(例如这里是50毫秒,取决于构建缓存的速度)后,重新执行函数,直到获取到数据。

永远不过期

“永远不过期”包含两层意思:

  • 从缓存层面来看,确实没有设置过期时间,所以不会出现热点key过期后产生的问题,也就是“物理”不过期。
  • 从功能层面来看,为每个value设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存

整个过程如下图所示。

从实战看,此方法有效地杜绝了热点key产生的问题,但唯一不足的就是重构缓存期间,会出现数据不一致的情况,这取决于应用方是否容忍这种不一致。下面代码使用Redis进行模拟:

String get(final String key) {
    V v = redis.get(key);
    String value = v.getValue();
    // 逻辑过期时间
    long logicTimeout = v.getLogicTimeout();
    // 如果逻辑过期时间小于当前时间,开始后台构建
    if(v.logicTimeout <= System.currentTimeMillis()) {
        String mutexKey = "mutex:key:" + key;
        if(redis.set(mutexKey, "1", "ex 180", "nx")) {
            // 重构缓存
            threadPool.execute(new Runnable(){
                public void run() {
                    String dbValue = db.get(key);
                    redis.set(key, dbvalue, newLogicTimeout);
                    redis.delete(mutexKey);
                }
            });
        }
    }
    return value;
}

作为一个并发量较大的应用,在使用缓存时有三个目标:

  • 加快用户访问速度,提高用户体验。
  • 降低后端负载,减少潜在的风险,保证系统平稳。
  • 保证数据“尽可能”及时更新。

下面将按照这三个维度对上述两种解决方案进行分析。

  • 互斥锁(mutex key):这种方案思路比较简单,但是存在一定的隐患,如果构建过程出现问题或者时间较长,可能会存在死锁和线程池阻塞的风险,但是这种方法能够较好的降低后端存储负载,并在一致性上做的比较好。
  • “永远不过期”:这种方案由于没有设置真正的过期时间,实际上已经不存在热点key产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。

两种解决方法对比如下表所示。

解决方案 优点 缺点
简单分布式锁 思路简单 保证一致性 代码复杂度增大 存在死锁的风险 存在线程池阻塞的风险
永远不过期 基本杜绝热点key问题 不保证一致性 逻辑过期时间增加代码维护成本和内存成本

目录
相关文章
|
11月前
|
存储 机器学习/深度学习 缓存
性能最高提升7倍?探究大语言模型推理之缓存优化
本文探讨了大语言模型(LLM)推理缓存优化技术,重点分析了KV Cache、PagedAttention、Prefix Caching及LMCache等关键技术的演进与优化方向。文章介绍了主流推理框架如vLLM和SGLang在提升首Token延迟(TTFT)、平均Token生成时间(TPOT)和吞吐量方面的实现机制,并展望了未来缓存技术的发展趋势。
3645 12
性能最高提升7倍?探究大语言模型推理之缓存优化
|
9月前
|
SQL 缓存 监控
MySQL缓存机制:查询缓存与缓冲池优化
MySQL缓存机制是提升数据库性能的关键。本文深入解析了MySQL的缓存体系,包括已弃用的查询缓存和核心的InnoDB缓冲池,帮助理解缓存优化原理。通过合理配置,可显著提升数据库性能,甚至达到10倍以上的效果。
|
8月前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
1535 3
|
缓存 并行计算 PyTorch
PyTorch CUDA内存管理优化:深度理解GPU资源分配与缓存机制
本文深入探讨了PyTorch中GPU内存管理的核心机制,特别是CUDA缓存分配器的作用与优化策略。文章分析了常见的“CUDA out of memory”问题及其成因,并通过实际案例(如Llama 1B模型训练)展示了内存分配模式。PyTorch的缓存分配器通过内存池化、延迟释放和碎片化优化等技术,显著提升了内存使用效率,减少了系统调用开销。此外,文章还介绍了高级优化方法,包括混合精度训练、梯度检查点技术及自定义内存分配器配置。这些策略有助于开发者在有限硬件资源下实现更高性能的深度学习模型训练与推理。
2370 0
|
9月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
1614 5
|
10月前
|
缓存 运维 安全
WordPress安全加速:Cloudflare + Nginx缓存优化方案
本文介绍如何通过Cloudflare与Nginx优化WordPress网站性能,涵盖静态资源长期缓存、动态页面智能缓存及敏感路径保护,提升加载速度并保障后台安全。适用于使用Cloudflare与Nginx环境的WordPress站点。
436 0
|
存储 缓存 NoSQL
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
361 2
|
缓存 监控 前端开发
在资源加载优化中,如何利用浏览器缓存提升性能?
通过以上这些方法,可以有效地利用浏览器缓存来提升资源加载的性能,减少网络请求次数,提高用户体验和应用的响应速度。同时,需要根据具体的应用场景和资源特点进行灵活调整和优化,以达到最佳的效果。此外,随着技术的不断发展和变化,还需要持续关注和学习新的缓存优化方法和策略。
678 159
|
缓存 JavaScript 中间件
优化Express.js应用程序性能:缓存策略、请求压缩和路由匹配
在开发Express.js应用时,采用合理的缓存策略、请求压缩及优化路由匹配可大幅提升性能。本文介绍如何利用`express.static`实现缓存、`compression`中间件压缩响应数据,并通过精确匹配、模块化路由及参数化路由提高路由处理效率,从而打造高效应用。
688 111
|
缓存 监控 测试技术
如何利用浏览器的缓存来优化网站性能?
【10月更文挑战第23天】通过以上多种方法合理利用浏览器缓存,可以显著提高网站的性能,减少网络请求,加快资源加载速度,提升用户的访问体验。同时,要根据网站的具体情况和资源的特点,不断优化和调整缓存策略,以适应不断变化的业务需求和用户访问模式。
885 63