详细介绍:分布式缓存的正确姿势:Cache-Aside、更新策略与分布式锁在 Java 微服务中的实战

news/2025/10/3 10:02:18/文章来源:https://www.cnblogs.com/yxysuanfa/p/19124357

分布式缓存的正确姿势:Cache-Aside、更新策略与分布式锁在 Java 微服务中的实战

实际场景引入

想象你正在开发一个大型电商平台的商品详情微服务。前端频繁访问同一张商品详情页,数据库压力骤增,搜索/浏览的峰值时常导致热点数据数据库命中率下降,用户体验下降。为了提升响应速度与稳定性,我们需要把热点数据放到分布式缓存中。但缓存并不能简单地等同于数据库,缓存失效、数据不一致、击穿/雪崩等问题都可能让系统变得乱套。本文以城市交通信号网的比喻,逐步揭开分布式缓存背后的设计要点,帮助你在实际系统中落地。

比喻解释:把缓存比作“路网中的路牌”——它能迅速提供路况信息,但若路牌信息过时(缓存失效),你仍需要回到路口的信号灯系统(数据库)来核对最新信息。正确的缓存设计,是在路牌信息过时时,能够智能、快速地回源并更新路牌,确保后续路况信息的正确性与时效性。

核心知识点1:Cache-Aside 模式的实现与落地

在分布式场景中,最常用的缓存模式是 Cache-Aside( Cache-Aside/Cache-Aside 机制)。读请求先尝试命中缓存;如果缓存未命中(Miss),再回源数据库获取数据,同时将数据写回缓存。这样可以最大限度地降低数据库压力,同时避免缓存写入时序问题。

场景示例:商品详情
  • 热点商品查询,通过缓存快速返回。
  • 数据更新时,主动清理缓存,确保后续查询更新为最新数据。
代码示例 1:基于 RedisTemplate 的 Cache-Aside 实现
@Service
public class ProductService {private static final String CACHE_KEY = "product:";@Autowiredprivate RedisTemplate redis;@Autowiredprivate ProductRepository repo;public Product getProduct(Long id) {String key = CACHE_KEY + id;// 1) 先从缓存命中Product cached = (Product) redis.opsForValue().get(key);if (cached != null) {return cached;}// 2) 缓存未命中,回源数据库Product fromDb = repo.findById(id).orElse(null);if (fromDb != null) {// 3) 将新数据写回缓存,设定合理 TTLredis.opsForValue().set(key, fromDb, 10, TimeUnit.MINUTES);}return fromDb;}public void updateProduct(Product p) {// 更新数据库repo.save(p);// 删除缓存,确保下次读取回源获取最新数据redis.delete(CACHE_KEY + p.getId());}
}

该实现的核心点:先读缓存、再回源、最后回写缓存;更新数据时清理缓存,避免缓存与数据库之间的数据不一致。若你需要强一致性,可以在更新后再回写缓存,但要承担写放大和双写一致性的问题,这里主要介绍清晰简单的缓存一致性路径。

#### 代码分析
- 优点:实现简单、对大多数写多读少场景友好;降低数据库压力。
- 缺点:在高并发的尖峰时段,仍可能出现缓存穿透或缓存击穿,需要结合分布式锁或其他策略做优化。

核心知识点2:更新策略、失效与幂等性

当商品数据发生变更时,如何有效地让缓存尽快过期或回写,避免老数据继续流传?常见做法有两类:

  1. 缓存失效+主动刷新:触发数据库更新后,显式删除缓存条目,后续请求触发回源并重新缓存;
  2. 异步刷新:在发布变更后,后台任务异步刷新缓存,缩短前端用户看到的过期时间。
代码示例 2:被动清理 + 异步刷新
@Service
public class ProductService {// 省略注入字段// 直接清理缓存,使用异步任务进行回填public void updateProduct(Product p) {repo.save(p);redis.delete(CACHE_KEY + p.getId());// 异步回填(示意),实际可使用消息队列或任务调度器AsyncExecutor.submit(() -> {Product refreshed = repo.findById(p.getId()).orElse(null);if (refreshed != null) {redis.opsForValue().set(CACHE_KEY + p.getId(), refreshed, 10, TimeUnit.MINUTES);}});}
}

要点是尽量让热数据的缓存命中在数据变更后保持快速且可控,同时对更新路径强调幂等性:多次更新不会导致数据错乱或重复计算。实际生产中常结合消息队列来实现幂等性与可追踪性。

核心知识点3:分布式锁与防止缓存击穿/雪崩

当缓存失效时,单机锁并不足以保护并发请求回源造成数据库压力剧增的情况。这时需要引入分布式锁,确保同一时间只有一个请求去回源更新缓存,其他请求等待锁释放后再读取缓存,从而避免击穿与雪崩。

代码示例 3:Redisson 分布式锁
@Service
public class ProductServiceWithLock {@Autowired private RedisTemplate redis;@Autowired private RedissonClient redisson;@Autowired private ProductRepository repo;public Product getProduct(Long id) {String key = "product:" + id;Product cached = (Product) redis.opsForValue().get(key);if (cached != null) return cached;RLock lock = redisson.getLock("lock:product:" + id);try {lock.lock();// 双重检查cached = (Product) redis.opsForValue().get(key);if (cached != null) return cached;Product fromDb = repo.findById(id).orElse(null);if (fromDb != null) {redis.opsForValue().set(key, fromDb, 10, TimeUnit.MINUTES);}return fromDb;} finally {lock.unlock();}}public void updateProduct(Product p) {repo.save(p);redis.delete("product:" + p.getId());}
}

要点如下:

  • 使用分布式锁避免并发穿透,保证同一时间只有一个请求触发回源获取与缓存刷新;
  • 结合双重检查,提升并发下的缓存命中率;
  • 需要合理的锁粒度与超时设置,防止死锁与资源浪费。

实际场景中的落地要点

  • 系统设计阶段,明确热点数据、写多读少还是读多写多,选择合适的缓存策略与一致性要求;
  • 缓存键的命名规范与版本控制,便于数据回滚和历史数据定位;
  • 监控缓存命中率、击穿/雪崩指标,及时扩容和调优。
  • 使用分布式锁时,务必结合超时机制与崩溃保护,避免锁意外泄露。

要点总结

  • Cache-Aside 是分布式缓存中最常用也是最易落地的模式,简单直接地实现了缓存与数据库的一致性。
  • 更新数据时要考虑缓存的失效、清理或回写策略,尽量让热数据在变更后尽快保持一致性,必要时使用异步刷新。
  • 缓存失效并发时,分布式锁是有效的保护手段,需合理设计锁粒度、超时与死锁防护。
  • 监控与告警是缓存架构的关键,缓存命中率、击穿、雪崩等指标应在生产环境中持续关注。

本文所示代码均为示例,实际生产应结合业务场景、数据一致性需求和团队技术栈做细粒度设计与性能调优。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/925751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业网站建设的文章太原市做网站

AI的归纳和演绎法分别是什么? AI的归纳和演绎法是两种常见的推理方法。 归纳法(inductive reasoning)是一种从特殊到一般的过程,在有限的实例观察中得出一般规律或原则。用简单的说法,就是从一些具体的事物或情况中总…

做网站大彩票网站开发搭建

《绝地求生》PCL秋季赛的战斗已经全部落下帷幕了,Team Razer 雷蛇战队成员,国内最具人气的4AM战队凭借着在季后赛的出色发挥,以316分的高分碾压全场,成功斩获本次PCL秋季赛的冠军,成为PCL联赛首个双冠队伍。随着金色的…

个人网站建设服务器aaa云主机怎么做网站

1、基本操作 1.1、进入HBase客户端命令行 前提是先启动hadoop集群和zookeeper集群。 bin/hbase shell 1.2、查看帮助命令 helphelp 查看指定命令的语法规则 查看 list_namespace 的用法(‘记得加单引号’) help list_namespace 2、namespace 我们…

信阳市网站建设自己做服装搭配的网站

Docker Compose是一种流行的技术,可以用来定义和管理你的应用程序所需的多个服务容器。通常在你的应用程序旁边创建一个 compose.yml 文件,它定义和配置服务容器。 使用 Docker Compose 的典型工作流程是运行 docker compose up,用它连接启动…

网站建设优化服务方案模板西安商城网站建设

前言 spring作为主流的 Java Web 开发的开源框架,是Java 世界最为成功的框架,持续不断深入认识spring框架是Java程序员不变的追求。 本篇博客介绍SpringBootApplicant注解的自动加载相关内容 其他相关的Spring博客文章列表如下: Spring基…

【实验报告】华东理工大学随机信号处理实验报告 - 详解

【实验报告】华东理工大学随机信号处理实验报告 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&q…

网站建设都包含哪些内容wordpress 更新网站

print() 在控制台输出 input() 获取控制台输⼊的内容 type() 获取变量的数据类型 len() 获取容器的⻓度 (元素的个数) range() ⽣成⼀个序列[0, n) 以上都是我们学过的函数,函数可以实现⼀个特定的功能。我们将学习⾃⼰如何定义函数, 实现特定的功能。 1.函数是什么…

页面置换算法

最佳置换算法opt 类似于cache 每次选择淘汰的页面将是以后永不使用的,或者在最长时间不会被访问的页面,这样可以保证最低的缺页率 缺页时未必发生页面置换,若还有可用的空闲内存块,就不用进行页面置换 缺页率=缺页…

Docker部署配置全流程(超详细——Windows和Linux) - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

推进电子设计革新:仿真(Emulation)如何引领下一代验证方式

在 IC 设计领域,硬件仿真(也称 FPGA 原型验证)已经跃升为不可或缺的关键技术。它以近乎真实硬件的形式重构目标设计,帮助工程师在硅片制造前进行高精度验证,为安全性、性能与功能保驾护航。1、为什么硬件仿真如此…

佛山网站建设兼职小型企业网站如何建设

为什么游戏公司选择物理服务器 1、选择的自由 选择游戏物理服务器可让我们根据需要自由选择和配置硬件。在完成设置和配置新获得的游戏服务器的所有艰苦工作后,请始终查看我们获得的价格,以确保自己拥有足够的带宽资源、端口容量和CPU核心,以…

AT_abc309_g [ABC309G] Ban Permutation

做这种排列计数题不是很明白. 首先考虑 \(x\) 很小,但它范围是扣掉一段区间,很不好. 我们容斥 \(j\) 个位置不满足条件去 DP,每次状压 \([i - x + 1, i + x - 1]\) 里数的用的情况即可,需要注意开头结尾的时候填不满.

在Mac上运行Windows 365的完整指南

本文详细介绍如何在Mac设备上部署和运行Windows 365云电脑,比较网页客户端与原生应用的功能差异,并逐步说明配置步骤和可用功能,包括多显示器支持和外围设备重定向等关键技术特性。如何在Mac上运行Windows 365 IT团…

完整教程:华为海思正式进入Wi-Fi FEM赛道?

完整教程:华为海思正式进入Wi-Fi FEM赛道?2025-10-03 09:26 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: bl…

现在做微信开发网站多少钱创新产品设计

只听名字的话会感觉对偶单纯形法和对偶问题关系很大,其实不然(想要了解对偶问题的话可以看我之前的文章)。对偶单纯形法在我看来和大M法以及两阶段法很像,都是用来补充纯粹的单纯形法无法解决特殊问题的缺陷。而且对偶单纯形法更加“强大”,因…

摩刻S10 动感单车 速度传感器故障及更换!

前段时间骑行过程中发现仪表盘上速度偶尔为0,时间停止不走的情况。之后就完全为0,时间也不增长了。 之后询问官方客服人员说是速度传感器故障,需要更换,但需到官方制定店铺购买备件。 测试方式是在拆下的传感器接…

Flink 架构组件、任务链路、Slot 资源与集群形态 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

盘锦网站建设广州建站网站

目录 一、项目概述 二、测试环境说明 三、测试方案 四、测试结果 五、结果分析 总结: 一、项目概述 1.1 编写目的 本次测试报告,为自动化测试框架性能测试总结报告。目的在于总结我们课程所压测的目标系统的性能点、优化历史和可优化方向。 1.2 …

包装材料网站建设廊坊建设部网站

Java NIO Files类读取文件流方式详解 Files类原理概述 java.nio.file.Files是Java标准库提供的一个工具类,用于操作文件和目录。它提供了一系列静态方法,可以用于创建、复制、删除、移动、重命名、读取、写入文件和目录等常见的文件系统操作。同时&…

贵阳市公共住宅投资建设集团官方网站北京网站优化排名

C语言中的函数指针是一种特殊的指针,它指向函数而不是数据。函数指针允许你在运行时动态地选择要调用的函数,这使得你可以根据需要在不同的函数之间切换,或者将函数作为参数传递给其他函数。函数指针的声明和使用如下: 声明函数指…