【2025下半年系统架构设计师案例分析】电商平台 MySQL + Redis 与缓存击穿治理

文章目录

题目背景

某电商平台为提升系统性能,采用 MySQL 作为主数据库,同时引入 Redis 作为缓存存储热点数据。系统主要支持商品信息查询、库存管理及订单存储等业务:

业务 特点
商品详情 访问频繁,需快速响应
库存 需实时准确,支持高并发修改
订单 需长期保存,查询频率较低

数据分工:

  • Redis:热门商品基本信息(名称、价格等)、实时库存数量、热销商品排名等。
  • MySQL:完整商品数据、库存明细、订单全量信息及用户数据。

本题围绕热点数据在缓存失效时可能出现的 缓存击穿(热点 Key 失效瞬间大量请求直达数据库) ,分别考察 互斥锁方案(王工)逻辑过期方案(李工) 的流程填空与方案对比。


概念速记:缓存击穿

缓存击穿 :某个 Key 非常热点,在某一时刻被超高并发访问;当该 Key 失效的瞬间,持续的大并发"穿透"缓存直接请求数据库,导致数据库压力骤增甚至故障。

互斥锁思路 :缓存过期后,大量并发中只有 首个成功拿到锁 的线程去查库并回写缓存;其余线程 等待/重试,待缓存就绪后从缓存读取,从而避免同一热点多次打库。

逻辑过期思路 :在缓存值中附带 逻辑过期时间 ,物理上可不依赖 Redis TTL 立即删键;过期后 异步 从数据库刷新,期间仍可 先返回旧数据,以换取低延迟与高吞吐(容忍短暂不一致)。


题目 2.3.1【问题1】(6分)

针对热点数据可能发生的缓存失效问题,架构师 王工 提出基于 互斥锁 的缓存实现方案。请补充架构图(序列图「互斥锁」)中的空白 (1) ~ (6)

图意说明(线程1 / 线程2)

  • 线程1:缓存未命中后,作为成功拿到锁的线程,完成「查库 → 回写 → 释放锁」。
  • 线程2 :缓存未命中后,在循环内 抢锁失败则休眠再重试 ,直到缓存被线程1写好,最终 缓存命中

下列序列图按真题常见 左右两泳道(线程1 / 线程2) 绘制:左侧线程1 自顶向下完成「未命中 → 加锁 → 查库 → 回写 → 释锁」;右侧线程2 在 循环 内完成「未命中 → 抢锁失败 → 休眠重试」,退出循环后 缓存命中 。填空 (1)~(6) 与图中序号一致。为贴近试卷排版,不单独拉出 Redis/MySQL/锁对象泳道,访问缓存与数据库体现在步骤说明中。

读图说明: 时间线上线程1 与线程2 并发 进入;线程2 在循环中多次「失败 → 休眠 → 再查缓存」,直到线程1 写完缓存后某次查询 命中 跳出循环。若需在图中显式画出对 Redis/MySQL 的消息,可在备考时另画带 缓存/数据库 参与者的展开图;答卷填空以本题五步 + 线程2 循环三步为准。

参考答案(填空)

序号 内容
(1) 获取互斥锁成功
(2) 查询数据库
(3) 更新缓存
(4) 释放锁
(5) 获取互斥锁失败
(6) 休眠一会,重试

解析

缓存击穿场景中,互斥锁是常用手段:只有首个线程 在缓存失效后访问数据库并更新缓存,其他线程 暂停等待 ;首线程释放锁后,其余线程 直接从缓存读取,避免对数据库的并发冲击。


题目 2.3.2【问题2】(8分)

针对同类问题,架构师 李工 提出 逻辑过期 方案。请补充架构图(含「逻辑过期时间」、多线程协作)中的空白 (1) ~ (8)

参考答案(填空)

注:真题图中锁的表述可能写作「分布式锁」或「互斥锁」,语义一致:同一热点更新互斥即可。

序号 内容
(1) 获取分布式锁成功
(2) 开启新线程
(3) 返回旧数据
(4) 查询数据库
(5) 更新缓存
(6) 释放锁
(7) 获取分布式锁失败
(8) 返回旧数据

解析

逻辑过期通过在缓存数据中 额外存储逻辑过期时间 ;过期后 不立即删除缓存 ,而是 启动异步任务 从数据库加载最新数据并写回缓存;刷新期间 仍返回旧数据 ,避免请求阻塞。代价是可能出现 短时不一致

图意说明(典型三线程版本)

  • 线程1 :发现逻辑过期 → 加锁成功启动新线程(异步刷新)立即返回旧数据(不阻塞用户)。
  • 线程2(后台) :查库 → 更新缓存并重置逻辑过期时间释放锁
  • 线程3 :发现逻辑过期 → 加锁失败直接返回旧数据(与线程1抢锁失败时的处理一致,避免集体打库)。

完整流程还可包含 线程4 :逻辑时间已被线程2刷新后,缓存命中且未过期 ,返回新数据。图示常强调:不设置物理过期 / 逻辑过期高可用、性能好 ,但存在 短暂不一致


题目 2.3.3【问题3】(11分)

对比 王工(互斥锁方案)李工(逻辑过期方案) 各自的优缺点。

参考答案(要点)

互斥锁方案

优点:

  • 数据一致性高:同一时刻仅一个线程访问数据库并更新缓存,减少并发写缓存、读脏数据等问题,一致性较好。
  • 实现简单直观:过期后抢锁 → 查库 → 回写 → 释放,流程清晰。

缺点:

  • 线程阻塞 :持锁线程若查库/写缓存较慢,其他线程长时间等待,响应时间变差
  • 死锁风险 :异常路径若未释放锁,可能造成 死锁(需超时、finally、看门狗等治理)。
  • 性能问题 :高并发下大量线程阻塞等待锁,吞吐下降
逻辑过期方案

优点:

  • 非阻塞 :逻辑过期后仍可 立即返回(多为旧数据),请求不被长时间阻塞。
  • 性能好 :线程竞争与等待少,并发能力相对更强。

缺点:

  • 数据不一致 :刷新完成前用户可能读到 过期数据 ,难以保证强一致,多为 最终一致
  • 实现复杂 :需维护逻辑过期字段、异步刷新、锁与缓存更新的 原子性/协作,边界情况多。
  • 内存占用 :数据不随物理 TTL 立即淘汰时,可能 长期占内存,需额外淘汰或容量策略。

解析(综合)

互斥锁与逻辑过期都是应对 缓存击穿 的常见策略。

  1. 互斥锁 :缓存失效(未命中)时,首个请求线程尝试获取互斥锁;成功则查库、更新缓存并释放锁,失败则 等待 直至锁释放。通过 串行化数据库访问 保证一致性,但高并发下大量线程等待会 增加延迟、降低吞吐 ,更适合 一致性要求极高、并发相对可控 的场景。

  2. 逻辑过期 :缓存"过期"后仍可先 返回旧数据 ,同时 异步 查库更新;不阻塞主流程,并发能力与体验 更好,但存在 短暂不一致 ,更适合 高并发、可容忍短时不一致 的业务(如电商展示、资讯类读多写少场景)。


考点小结

  • 缓存击穿缓存雪崩缓存穿透 的区别(本题侧重热点 Key 失效瞬间)。
  • 互斥锁 :强一致、实现简单,代价是 阻塞与锁治理
  • 逻辑过期 :高可用、低延迟,代价是 一致性减弱与工程复杂度
相关推荐
沛沛rh4531 分钟前
用 Rust 实现用户态调试器:mini-debugger项目原理剖析与工程复盘
开发语言·c++·后端·架构·rust·系统架构
Bert.Cai41 分钟前
MySQL简介
数据库·mysql
devilnumber1 小时前
Redis 使用过程中可能遇到的常见问题或 “坑”
数据库·redis·缓存
摇滚侠1 小时前
Redis 和 MySQL 数据同步方案,ElasticSearch 和 MySQL 数据同步方案
java·redis·mysql
披着羊皮不是狼1 小时前
(9)批量生成文章并同步存入 MySQL 和 Redis
数据库·redis·mysql
七夜zippoe2 小时前
DolphinDB SQL查询:从简单到复杂
数据库·sql·mysql·查询·dolphindb
CDN3603 小时前
【踩坑实录】前端开发必看:一次由CSS缓存引发的线上事故与SEO反思
前端·css·缓存
wuyikeer3 小时前
如何在docker中的mysql容器内执行命令与执行SQL文件
sql·mysql·docker
y = xⁿ3 小时前
MySQL:事务机制
数据库·mysql
Aray12344 小时前
Redis Cluster 集群选举机制
数据库·redis·缓存