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

Redis与本地缓存的协同使用及多级缓存策略

为什么在使用Redis的同时还需要本地缓存?

即使已经使用了Redis这样的分布式缓存,仍然需要本地缓存的原因包括:

  1. 减少网络开销:本地缓存避免了与Redis服务器之间的网络往返,对于高频访问的数据尤其重要
  2. 降低Redis负载:热点数据在本地缓存后,可以减少对Redis的请求压力
  3. 应对Redis故障:当Redis不可用时,本地缓存可以作为后备方案
  4. 极致性能需求:某些场景下,内存访问比网络访问快几个数量级
  5. 成本考虑:减少Redis集群规模可以节省成本

多级缓存策略的性能优势

多级缓存通常采用"本地缓存 → 分布式缓存 → 数据库"的层级结构:

  1. 响应时间优化:
    • 本地缓存:纳秒级访问

    • Redis:毫秒级访问

    • 数据库:毫秒到秒级访问

  2. 吞吐量提升:
    • 90%以上的请求可能被本地缓存处理

    • 剩余大部分被Redis处理

    • 只有极少量请求会到达数据库

  3. 资源利用率提高:
    • 每层缓存处理适合其层级的数据

    • 避免将所有压力集中在单一缓存层

典型应用场景

  1. 电商系统:
    • 本地缓存:商品基本信息、配置信息

    • Redis:库存信息、促销活动

    • 数据库:订单详情、用户信息

  2. 内容分发系统:
    • 本地缓存:热门内容

    • Redis:个性化推荐内容

    • 数据库:全量内容库

  3. 社交网络:
    • 本地缓存:用户基础资料

    • Redis:好友关系、动态信息

    • 数据库:完整用户数据

实现注意事项

  1. 缓存一致性:
    • 需要设计合理的过期和失效策略

    • 考虑使用发布/订阅模式同步多级缓存

  2. 容量控制:
    • 本地缓存大小需合理设置,避免内存溢出

    • 使用LRU等淘汰策略管理缓存项

  3. 监控与调优:
    • 监控各级缓存命中率

    • 根据实际访问模式调整缓存策略

多级缓存架构能够显著提升系统性能,但也增加了系统复杂性,需要根据实际业务需求和性能指标进行合理设计和调优。

多级缓存在分布式系统中的实践优化指南

多级缓存是提升分布式系统性能的关键技术,下面我将详细介绍从设计到落地的完整实践方法。

多级缓存架构设计

  1. 典型三级缓存架构

客户端缓存 → 应用本地缓存 → 分布式缓存(Redis) → 持久层数据库

  1. 各层级缓存选型建议
缓存层级推荐技术数据特点过期时间
客户端Cookie/LocalStorage用户个性化配置长(天级)
本地缓存Caffeine/Guava Cache热点数据、配置信息中(分钟-小时)
分布式缓存Redis/Memcached共享数据、业务数据短(秒-分钟)

具体实施步骤

  1. 数据分类与缓存策略制定

// 示例:使用注解定义多级缓存策略

@Caching(cacheable = {@Cacheable(cacheNames = "local", key = "#id", unless = "#result == null"),@Cacheable(cacheNames = "redis", key = "'user:'+#id")},put = {@CachePut(cacheNames = "local", key = "#result.id"),@CachePut(cacheNames = "redis", key = "'user:'+#result.id")}
)
public User getUserById(Long id) {// 数据库查询
}
  1. 本地缓存实现(以Caffeine为例)
// 高性能本地缓存配置
LoadingCache<Long, User> localCache = Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(5, TimeUnit.MINUTES).refreshAfterWrite(1, TimeUnit.MINUTES).build(key -> {// 当缓存失效时,先尝试从Redis获取User user = redisTemplate.opsForValue().get("user:" + key);if (user == null) {user = userRepository.findById(key);redisTemplate.opsForValue().set("user:"+key, user, 10, TimeUnit.MINUTES);}return user;});
  1. 多级缓存读取流程

  2. 请求入口:检查客户端缓存(如HTTP ETag)

  3. 本地缓存:使用内存缓存查询

  4. 分布式缓存:本地未命中时查询Redis

  5. 数据库查询:所有缓存未命中时回源

  6. 回填缓存:异步更新各级缓存

缓存更新策略

// 使用发布订阅模式保持缓存一致性@EventListener
public void handleCacheEvictEvent(CacheEvictEvent event) {// 1. 先更新数据库userRepository.update(event.getUser());// 2. 删除Redis缓存(避免脏读)redisTemplate.delete("user:"+event.getUser().getId());// 3. 广播消息通知其他节点清除本地缓存redisTemplate.convertAndSend("cache:evict", event.getUser().getId());
}

// 订阅频道处理缓存失效

@RedisListener(channel = "cache:evict")
public void onCacheEvict(Long userId) {localCache.invalidate(userId);
}

三、高级优化技巧

  1. 热点数据发现与预加载

使用滑动窗口统计热点Key

def track_key_access(key):# Redis执行原子计数counter = redis.incr(f"access:{key}")# 达到阈值加入预热队列if counter > HOT_THRESHOLD:redis.zadd("hotkeys", {key: time.time()})

后台任务处理热点Key

def preload_hot_keys():hot_keys = redis.zrangebyscore("hotkeys", time.time()-300, time.time())for key in hot_keys:load_to_local_cache(key)
  1. 缓存雪崩防护
// 二级缓存+互斥锁防止雪崩
public User getWithDoubleCheck(Long id) {// 第一级检查User user = localCache.getIfPresent(id);if (user != null) return user;// 获取分布式锁Lock lock = redisson.getLock("lock:"+id);try {lock.lock();// 第二级检查(防止并发请求都未命中缓存)user = localCache.getIfPresent(id);if (user != null) return user;// 查询Redisuser = redisTemplate.opsForValue().get("user:"+id);if (user == null) {// 回源数据库user = userRepository.findById(id);// 异步更新Redisexecutor.submit(() -> updateRedisCache(user));}// 更新本地缓存localCache.put(id, user);return user;} finally {lock.unlock();}
}
  1. 监控指标体系建设

需要监控的关键指标:

  1. 各层缓存命中率(本地/Redis)
  2. 缓存响应时间P99
  3. 回源数据库QPS
  4. 热点Key分布
  5. 内存使用情况

四、典型场景实践案例

案例1:电商商品详情页

func GetProductDetail(productID int64) (*Product, error) {// 1. 检查本地缓存if item, ok := localCache.Get(productID); ok {return item.(*Product), nil}// 2. 检查Redis集群product, err := redisClient.Get(ctx, fmt.Sprintf("product:%d", productID)).Result()if err == nil {// 异步回填本地缓存go func() { localCache.Set(productID, product) }()return product, nil}// 3. 回源数据库+多级回填product, err = db.GetProduct(productID)if err != nil {return nil, err}// 异步更新各级缓存go updateAllCaches(productID, product)return product, nil
}

案例:社交网络关系链

def get_user_friends(user_id):# 1. 检查内存缓存cache_key = f"friends:{user_id}"if cache_key in local_memoize:return local_memoize[cache_key]# 2. 检查Redis布隆过滤器if not redis_bloom.contains('recent_active', user_id):return []# 3. 查询Redis主缓存friends = redis.get(cache_key)if friends is not None:local_memoize[cache_key] = friendsreturn friends# 4. 查询数据库并重建缓存friends = db.query_friends(user_id)pipeline = redis.pipeline()pipeline.set(cache_key, friends, ex=3600)pipeline.execute()return friends

避坑指南

  1. 缓存污染:实现有效的缓存淘汰策略
  2. 脑裂问题:分布式锁需要设置合理的超时时间
  3. 监控缺失:必须建立完善的监控告警系统
  4. 过度缓存:避免缓存大量不常访问的数据
  5. 版本管理:缓存数据结构变更时要考虑兼容性
http://www.dtcms.com/a/315261.html

相关文章:

  • 【定位设置】Mac指定经纬度定位
  • Spring--04--2--AOP自定义注解,数据过滤处理
  • Easysearch 集成阿里云与 Ollama Embedding API,构建端到端的语义搜索系统
  • Shell第二次作业——循环部分
  • 【科研绘图系列】R语言绘制解释度条形图的热图
  • 中标喜讯 | 安畅检测再下一城!斩获重庆供水调度测试项目
  • 松鼠 AI 25 Java 开发 一面
  • 【慕伏白】Android Studio 配置国内镜像源
  • Vue3核心语法进阶(Hook)
  • selenium4+python—实现基本自动化测试
  • PostgreSQL——数据类型和运算符
  • MySQL三大日志详解(binlog、undo log、redo log)
  • C语言的指针
  • 拆解格行随身WiFi技术壁垒:Marvell芯片+智能切网引擎,地铁22Mbps速率如何实现?
  • mysql 数据库系统坏了,物理拷贝出数据怎么读取
  • 深入剖析通用目标跟踪:一项综述
  • 关于如何自定义vscode(wsl连接linux)终端路径文件夹文件名字颜色的步骤:
  • 自学嵌入式 day 42 串口通信
  • 力扣热题100——双指针
  • 应⽤层协议HTTP
  • 个推1.0版本
  • 自动化测试工具Katalon之BDD
  • 鸿蒙开发-端云一体化--云数据库
  • Qt 自动无法加载数据库为空
  • 知识管理中的人工智能:概述、主要功能和管理工具
  • 专访 Giants Protocol 创始人:新加坡主权基金正式押注进军 RWA
  • 深入理解 mmap:原理、用法与实战全解析
  • Linux 进程间通信底层原理(1):匿名与命令管道
  • LLM Prompt与开源模型资源(4)提示词工程进阶指南
  • Node.js高并发接口下的事件循环卡顿问题与异步解耦优化方案