【系统架构师案例题-知识点】数据库与缓存设计

作者:roman_日积跬步-终至千里日期:2026/4/28

本文聚焦系统架构师案例题中的数据库与缓存设计,重点说明关系型数据库设计、NoSQL 选型、分库分表、读写分离、缓存策略、缓存故障模式以及缓存与数据库一致性问题,并结合电商、支付、内容平台、搜索系统、推荐系统等真实软件行业场景说明这些技术为什么会出现、各自解决什么问题、工程上该如何取舍。

阅读时可以按三个层次把握:先理解数据为什么会成为瓶颈,再理解数据库和缓存分别解决哪一类问题,最后把题干中的业务信号翻译成卷面表达。

一、先建立整体认识

数据库与缓存设计的核心,不是“会不会背名词”,而是看清系统到底在为什么付成本。

一个系统最早往往只有一套关系数据库,数据正确、事务清晰、开发也直接。但随着业务增长,问题会逐步出现:

  • 读请求越来越多,数据库开始扛不住查询压力
  • 写请求越来越集中,单库容量和吞吐接近上限
  • 表越来越大,索引越来越重,慢 SQL 和锁冲突开始频繁出现
  • 页面越来越复杂,一个接口需要拼很多表,查询响应时间持续升高
  • 业务变化越来越快,某些数据结构已经不适合硬塞进固定表结构

所以,数据库与缓存设计本质上是在解决五类问题:

  • 如何在数据正确和查询效率之间平衡
  • 如何根据数据特点选择合适的存储模型
  • 如何把单库单表的容量和性能瓶颈拆开
  • 如何用缓存扛住热点流量
  • 如何在引入缓存之后控制数据不一致和故障扩散

二、关系型数据库设计

1. 为什么要规范化

怎么理解: 规范化的目标不是为了考试好记,而是为了让数据结构更稳定、冗余更少、更新更不容易出错。

最典型的问题是:如果一份用户信息被冗余在订单表、地址表、营销表很多地方,那么用户改一次手机号,就可能有多处要一起改;如果漏改一处,系统就会出现“同一个用户在不同页面看到不同手机号”的异常。这就是典型的数据冗余和更新异常。

常见范式可以这样记:

范式重点要求直观理解
1NF字段原子化一个字段里不要再塞列表、对象、组合值
2NF消除部分依赖非主属性不能只依赖联合主键的一部分
3NF消除传递依赖非主属性不要通过别的非主属性间接依赖主键
BCNF决定因素都是候选键比 3NF 更严格,进一步消除异常

行业里的理解方式: 用户、订单、商品、支付这些核心业务表之所以要认真建模,就是因为它们承载的是长期稳定的数据边界。关系库在这里最大的价值,是结构清晰、约束明确、事务能力强。

2. 为什么又会反规范化

如果规范化一直做到极致,查询就会越来越依赖多表关联。对高并发系统来说,这会带来新的问题:读链路变长、SQL 更复杂、索引更难维护、接口响应更慢。

所以工程上经常会走到第二步:在核心数据仍保持规范化的前提下,对读路径做反规范化

怎么理解: 反规范化不是“设计变差了”,而是在承认一个现实:很多业务更怕查询慢,而不是多存一份可控冗余。

常见做法有:

手段在行业里的典型表现
冗余列订单表直接冗余用户名、商品快照、收货地址快照
派生列订单总金额、评论数、点赞数直接存结果,不每次实时聚合
预聚合表报表系统提前汇总日活、GMV、转化率
拆大字段把正文、图片、扩展 JSON 从主表拆到扩展表

什么时候适合做: 读多写少、查询性能敏感、页面拼装复杂的场景最常见。例如电商订单详情页、内容平台文章页、BI 看板、运营后台统计页。

代价是什么: 一旦引入冗余,就必须接受同步更新、补偿修复和一致性校验的成本。所以规范化解决“数据对不对”,反规范化解决“数据查得快不快”。


三、NoSQL 与搜索系统

1. 为什么关系数据库不够用

很多人第一次接触 NoSQL,会误以为它是在“替代 MySQL”。其实更准确的理解是:NoSQL 是在补关系库不擅长的那一部分能力

关系型数据库最擅长的是:

  • 结构清晰的数据建模
  • 事务一致性
  • 复杂条件查询
  • 强约束和强关系

但它不一定擅长:

  • 极高并发热点访问
  • 灵活多变的半结构化数据
  • 海量时序写入
  • 复杂图关系遍历
  • 全文搜索和多维检索

所以 NoSQL 的本质,不是“更先进”,而是“更偏科”。选型时要先看你的问题是什么。

2. 常见类型怎么选

类型解决什么问题代表产品软件行业里的典型场景
键值型极快读写、简单查找Redis、Memcached缓存、Session、验证码、排行榜
文档型结构灵活、对象聚合存储MongoDB、CouchDB内容管理、画像标签、表单系统
列族型海量写入、宽表、时序/日志类数据HBase、Cassandra埋点、监控、日志、设备上报
图数据库关系遍历和路径查询Neo4j、JanusGraph社交关系、风控关联、知识图谱
搜索引擎全文搜索、多字段检索、排序聚合Elasticsearch商品搜索、文章搜索、日志检索

怎么考: 题干如果强调的是“关系复杂、需要路径查询”,优先想到图数据库;如果强调“全文检索、分词、高亮、相关度排序”,优先想到搜索引擎;如果强调“高频读写、热点缓存、排行榜”,优先想到 Redis。

3. MongoDB 为什么常见

MongoDB 适合的不是“所有数据”,而是结构经常变化、对象聚合明显、字段不固定的数据。

比如内容平台的文章文档、低代码平台的动态表单、用户画像标签、地图 POI 信息,都很适合文档模型。因为这些数据天然就是“一个对象一坨信息”,硬拆成很多关系表反而让开发和查询都变复杂。

它常被看中的能力包括:

  • 文档模型天然贴近 JSON 接口
  • schema 相对灵活,适合快速迭代
  • 支持分片,水平扩展能力较好
  • 聚合能力和地理空间查询能力较强

在地理空间场景里,MongoDB 支持 GeoJSON2dsphere 索引,这也是它常出现在地图、门店、轨迹类系统中的原因。

4. Redis 为什么不只是缓存

Redis 经常被简单描述成“缓存”,但它真正强的地方在于:内存级性能 + 多种数据结构 + 简单直接的操作语义

常见数据结构可以这样记:

结构最常见用途
String缓存值、计数器、分布式锁
Hash用户属性、对象字段缓存
List最新列表、简单队列
Set去重、标签集合、共同关注
ZSet排行榜、优先队列、延迟任务

为什么 ZSet 很常考: 因为它同时有“集合”和“排序”两种能力。像积分榜、热搜榜、直播间贡献榜,本质上都很适合用 member + score 表示。

常见命令通常围绕这几个点考:

  • ZADD:写入成员和分数
  • ZRANGE / ZREVRANGE:按排序取区间
  • ZSCORE:查单个成员分数
  • ZRANK / ZREVRANK:查排名
  • ZCOUNT:统计区间数量

四、分库分表与读写分离

1. 为什么数据库会走到拆分

数据库拆分不是“上来就该做”的架构动作,而是当单库单表真的扛不住时才有意义。

常见触发点有:

  • 单表数据量太大,索引和查询明显变慢
  • 单库磁盘、CPU、IO 接近瓶颈
  • 某些热点业务流量远高于其他业务
  • 不同业务模块的生命周期和扩容需求完全不同

2. 分库分表到底在拆什么

水平拆分 是按行拆,把不同记录分散到不同库或表;垂直拆分 是按业务边界或字段类型拆,把不同功能的数据分开放。

常见分片策略可以这样理解:

策略适合怎么理解主要问题
范围分片按 ID、时间区间分布容易形成热点,如最新时间段写入过热
哈希分片尽量打散,追求均匀扩容时数据迁移成本高
一致性哈希为了降低节点增减时迁移范围设计和实现更复杂

行业里的例子:

  • 订单表按用户 ID 或订单 ID 分片,是典型水平分片
  • 用户库、订单库、支付库分开,是典型垂直分库
  • 日志表按天或按月分表,是时间维度分片的常见形态

怎么考: 题干如果强调“单表过大、单机容量不足、热点明显、业务边界清晰”,通常就是在考分片思路。

3. 读写分离解决什么问题

如果系统的主要压力来自读多写少,最直接的办法不一定是分片,而是先做读写分离。

怎么理解: 主库负责写,从库负责读。这样写路径仍然集中控制,读路径则可以横向扩展。

它常见于:

  • 商品详情、文章详情、配置查询这类高频读取场景
  • 后台报表、运营查询、历史记录查询
  • 读流量明显高于写流量的业务

真正要注意的点:

  • 主从复制存在延迟,刚写完马上读可能读到旧值
  • 不是所有读都能走从库,强一致读通常还要回主库
  • 应用层、中间件层都需要处理读写路由

所以读写分离提升的是吞吐,但它不会自动解决一致性问题。


五、缓存设计

1. 为什么系统离不开缓存

缓存的本质,不是“让系统更高级”,而是把那些重复、高频、热点的读请求挡在数据库前面。

最常见的行业场景包括:

  • 商品详情页、文章详情页反复访问同一份数据
  • 首页推荐、热榜、用户画像反复被读取
  • 秒杀、大促、热点活动在极短时间内打爆某些 key

如果这些请求全都落到数据库,数据库很快就会成为瓶颈。所以缓存设计的核心问题是:哪些数据值得缓存、缓存多久、更新时怎么处理、故障时怎么兜底。

2. 常见缓存策略怎么理解

2.1 Cache-Aside:最常见,也最像真实工程

这几乎是大多数业务系统的默认方案。

读流程:

  1. 先查缓存
  2. 命中直接返回
  3. 未命中则查数据库
  4. 把结果写回缓存

写流程:

  1. 先更新数据库
  2. 再删除缓存

为什么不是“先更新数据库再更新缓存”: 因为缓存不是数据的最终来源,它只是数据库前面的一层加速层。看起来“更新数据库后顺手更新缓存”很自然,但在并发写的情况下,较早请求的旧值有可能在后面又把缓存覆盖掉,结果缓存里反而变成旧数据。相比之下,更新数据库后删除缓存更稳,因为后续读请求会从数据库重新加载最新值。

为什么是“先更库,再删缓存”: 如果先删缓存,数据库还没更新完时来了一个读请求,就会把旧数据重新写回缓存。这是缓存不一致最经典的来源之一。

2.2 Read-Through:把读回源交给缓存层

应用只跟缓存打交道,缓存自己决定未命中时如何加载数据库。它适合那些希望把缓存逻辑从业务代码里抽出去的场景,但实际业务里最常见的仍然是 Cache-Aside。

2.3 Write-Through:同步把缓存和数据库都写掉

它的好处是写完之后缓存和数据库天然更一致,缺点也很直接:写延迟会上升,因为数据库必须一起成功。

这类模式更适合对透明性要求高、写路径较可控的场景,不是大多数互联网业务的默认选项。

2.4 Write-Behind:先写缓存,稍后异步落库

这是典型的“用一致性换性能”方案。它适合那些极端追求写吞吐、能接受缓存层承担更多持久化责任的场景,但一旦缓存故障,数据丢失风险会更高。

3. 怎么选,怎么考

策略一致性性能更像什么场景
Cache-Aside最终一致通用互联网业务
Read-Through最终一致希望统一缓存回源逻辑
Write-Through更强一致较低写路径可控、强调同步落库
Write-Behind弱一致特定高吞吐写场景

考试里如果没有特别说明,优先写 Cache-Aside,因为它最符合真实系统的常见实现。


六、缓存的三类典型问题

缓存问题很容易混,真正好记的方法不是死背名词,而是看数据库被打穿的原因分别是什么。

1. 缓存穿透:查的是根本不存在的数据

怎么理解: 请求的 key 在缓存里没有,在数据库里也没有,于是每次都直接打到数据库。

这类问题常见于恶意攻击、错误参数、枚举式探测。比如不断请求不存在的商品 ID、用户 ID、订单号。

常见解决方法有:

方案适合怎么理解
缓存空值查不到也缓存一个短期空结果,避免反复打库
布隆过滤器先做一次“可能存在性”判断,不可能存在的直接拦掉
参数校验对非法 ID、非法格式、明显异常请求先拦截

布隆过滤器为什么常考: 因为它的特点很鲜明。它能高效判断“一个元素一定不存在”,但不能保证“一定存在”,会有误判。

2. 缓存击穿:查的是很热,但刚好过期的数据

怎么理解: 一个热点 key 平时都被缓存挡住了,但它一过期,瞬间大量并发一起打到数据库。

比如一个爆款商品详情、一个热搜词、一个首页配置 key,在某个时间点突然失效,数据库就会被这一个热点点位打爆。

常见方案有:

方案适合怎么理解
互斥锁只允许一个线程去回源加载,其他线程等待
逻辑过期数据先返回旧值,后台异步刷新新值
热点 key 永不过期对极热点数据人工管理生命周期

3. 缓存雪崩:不是一个点炸,是一大片一起炸

怎么理解: 大量 key 同时过期,或者整个缓存集群不可用,结果本该被缓存挡住的请求一起冲向数据库。

这比击穿更危险,因为击穿通常是一个热点 key,雪崩是整个缓存层失效。

常见方案有:

方案适合怎么理解
过期时间加随机值把集中失效打散
多级缓存本地缓存 + Redis 缓存,降低同一层全部失效的冲击
缓存高可用哨兵、Cluster、主从等,减少单点故障
限流降级数据库前再做最后一层保护
缓存预热系统上线前先把热点数据准备好

4. 三者怎么区分,怎么考

问题核心特征一句话区分
穿透数据本来就不存在缓存和数据库都没有
击穿单个热点 key 失效一个点被并发打爆
雪崩大量 key 同时失效或缓存层故障一大片流量同时压库

答题时最好先判断是哪一种,再写对应方案,避免把三类问题的答案混着写。


七、缓存与数据库一致性

1. 为什么这件事总会出问题

缓存和数据库是两个独立系统,对它们的更新不是一个原子操作。只要存在并发,只要有时间窗口,就可能出现短暂不一致。

最经典的冲突场景是:

  • 线程 A 更新数据库
  • 线程 A 删除缓存之前
  • 线程 B 恰好读缓存未命中,于是回源数据库
  • 如果 B 读到的是旧值,再把旧值写回缓存,就形成了脏缓存

所以缓存一致性问题的根源,不是某一行代码写错了,而是缓存和数据库天然是两个时钟、两套状态、两次操作

2. 工程上通常怎么做

方案核心思路更适合什么场景
先更库再删缓存最经典的 Cache-Aside 写法通用业务系统
延迟双删删除缓存两次,尽量覆盖并发窗口能接受实现更复杂的场景
MQ 异步删缓存更新数据库后发消息删缓存,可重试强调可靠删除
Binlog 驱动刷新监听数据库变更后同步缓存统一化、平台化治理
读写锁/强约束用更强同步手段换一致性少量强一致场景
Write-Through把写入统一放到缓存层特定框架或特定存储模型

最常见的标准答案: 如果没有特别苛刻的强一致要求,通常优先采用 先更新数据库,再删除缓存。它不是绝对零不一致,而是在复杂度和效果之间非常均衡。

3. 软件行业里怎么取舍

  • 商品详情、文章详情、用户主页这类读多写少场景,通常接受短暂不一致
  • 库存、价格、账户余额这类对错误特别敏感的场景,会更谨慎,甚至降低缓存比例
  • 平台化团队会更偏向 MQ、Binlog、统一缓存框架来收敛一致性治理

考试里如果问“为什么不能完全一致”,答题重点要落在:性能、复杂度、并发窗口和业务容忍度之间的权衡


八、答题模板

1. 数据库设计题

对于题目中的 [业务场景],如果重点是保证数据结构稳定、减少冗余和更新异常,应优先采用 规范化设计;如果重点是提升查询效率、减少关联查询成本,则可在核心数据正确的前提下进行 适度反规范化,例如 [冗余列/派生列/预聚合]

2. NoSQL 选型题

该场景适合选用 [产品或类型]。因为其主要业务特征是 [高并发/结构灵活/海量写入/复杂关系/全文检索],而 [产品能力] 恰好能够满足这一需求,具体表现为 [1~2 条能力]

3. 缓存流程题

写 Cache-Aside 时,标准顺序通常是:

  1. 读先查缓存
  2. 未命中再查数据库
  3. 查询结果回填缓存
  4. 写时先更新数据库
  5. 再删除缓存

4. 缓存问题分析题

题目中的问题属于 [穿透/击穿/雪崩]。其根本原因是 [不存在数据反复访问/热点 key 过期/大量 key 同时失效]。可采用 [2~3 个方案] 进行治理,分别从 [拦截不存在请求/保护回源加载/避免大面积同时失效] 等方面降低数据库压力。

5. 一致性分析题

缓存与数据库不一致的根本原因在于两者属于独立存储,对数据库更新与缓存操作之间不存在原子性保证。在并发场景下,更新库和删缓存之间存在时间窗口,因此通常采用 先更新数据库,再删除缓存 作为折中方案,并结合 延迟双删、MQ 重试、Binlog 同步 等机制进一步降低不一致概率。


【系统架构师案例题-知识点】数据库与缓存设计》 是转载文章,点击查看原文


相关推荐


Visual Studio 与 Visual Studio Code 区别
日更嵌入式的打工靓仔2026/4/20

特性Visual Studio (VS)Visual Studio Code (VS Code)本质类型集成开发环境 (IDE)轻量级源代码编辑器核心定位大型、复杂的项目开发(Windows、游戏、企业级应用)快速编辑、脚本编写、Web/云开发主要平台Windows、macOS (功能有差异)Windows、macOS、Linux占用空间大 (安装需要几GB到几十GB空间)小 (安装包约100MB以下)性能/速度启动和加载大型项目较慢启动迅速,打开文件极快价格社区版免费;专业版/企业版付费完全免


OpenClaw Windows 安装详细教程
超低空2026/4/11

OpenClaw(前身为 ClawdBot)是一款本地托管的个人 AI 助手系统,可以通过网关控制平面连接到 WhatsApp、Telegram、Discord 等常见通讯软件,并在本地运行各种工作流。 由于 OpenClaw 深度依赖底层系统的进程管理和文件监听,直接在 Windows 原生环境下运行可能会遇到一些限制。因此,官方推荐使用 WSL2(Windows Subsystem for Linux) 或 Docker 来进行安装。以下是详细的安装教程和避坑指南。 安装方式优缺点对比 在


别让APP名字和图标毁了你的Toast!一招教你Android优化技巧
小码哥_常2026/4/3

别让APP名字和图标毁了你的Toast!一招教你Android优化技巧 为啥要去掉 Toast 里的 APP 名字和图标 在如今这个看脸的时代,APP 的颜值也至关重要。统一、美观的 UI 设计,就像给 APP 穿上了一件漂亮的外衣,不仅能提升用户体验,还能让 APP 在众多竞争对手中脱颖而出。 大家在使用 APP 的时候,应该都遇到过 Toast 消息提示吧。这是一种轻量级的消息提示框,通常出现在屏幕底部,用来告知用户一些操作结果或者系统状态。但是,不知道大家有没有注意到,在某些手机上,比如小


Bun v1.3.11 官方更新全整理:新增功能、关键修复与升级验证
iDao技术魔方2026/3/26

Bun v1.3.11 官方更新全整理:新增功能、关键修复与升级验证 摘要 Bun v1.3.11 不是“小修小补”版本,而是一次“功能新增 + 兼容修复 + 工程稳定性”集中迭代。很多团队升级后只跑了 bun test,却漏掉 Cron、ANSI 字符串裁切、测试路径忽略、Windows ARM64 shim 等高价值更新。本文按官方清单做工程化拆解:新增了什么、修了什么、会影响哪里、怎么快速验证,附可执行命令与排错建议。 大家好,我是 iDao。10 年全栈开发,做过架构、运维,也在落地


web网络安全-每日一练-Training-WWW-Robots
观书喜夜长2026/3/18

练习题目:Training-WWW-Robots 练习网站(攻防世界):https://adworld.xctf.org.cn/ 解题步骤 1、打开题目场景 在这个小小的训练挑战中,你将了解Repbots_exclusion_standard。 robots.txt文件被网络爬虫用于检查它们是否被允许爬取和索引你的网站,或者只是网站的部分内容。 有时这些文件会暴露目录结构,而不是保护内容不被爬取。 祝你玩得开心! 2、利用Robots协议 发现根目录下有一个 f10g


Dockerfile构建SQL-Labs靶场及Docker安全管控
文刀竹肃2026/3/9

Dockerfile构建SQL-Labs靶场及Docker安全管控 一、前言 SQL注入是网络安全领域最常见的漏洞之一,SQL-Labs是一款经典的SQL注入练习靶场,包含了各种类型的SQL注入场景(联合查询、盲注、报错注入等),是网安学习、漏洞验证的必备环境。本文将通过Dockerfile自定义构建SQL-Labs靶场镜像,完整演示从基础镜像选择、依赖安装、靶场部署到镜像优化的全过程,同时重点讲解容器权限管控、安全风险规避要点,贴合网安靶场搭建的实际需求,与上一篇Docker基础、核心命令内容


Gateway—— 高级流量路由
离恨烟~2026/3/1

0 前言 Gateway API 是 Kubernetes 官方推出的下一代流量管理标准,旨在解决传统 Ingress 在协议支持、扩展性和多租户等方面的不足。它通过 GatewayClass、Gateway、HTTPRoute 等 CRD 实现流量治理的分层解耦,让基础设施和应用团队各司其职。本章将以若依项目为例,实战部署 Envoy Gateway 并实现高级流量路由。 1 Gateway的概念 1.1 什么是Gateway Gateway API 是 Kubernetes 官方下一代“流量


2026年,我的AI编程助手使用心得(纯个人体验,非评测)
PieroPc2026/2/21

今年在AI编程助手上摸索出一套自己的组合拳,分享给同样在路上的创作者们: 初稿设计 首选小米MiMo。不是因为它功能多强,而是因为它出设计稿真的快、真的漂亮。脑子里的想法扔给它,几分钟就能看到赏心悦目的初稿,这种“即时满足感”别的工具给不了。 aistudio.xiaomimimo.com/ 数据处理 遇到图片要转CSV或JSON,我会切到豆包Doubao。它的综合模型对图片支持确实好,识别准、转换快。不过坦白说,深入问下去就容易卡壳,这时候别硬撑。 www.doubao.c


从少年到父亲:我在异乡的第一个年
修己xj2026/2/13

今天是老历腊月二十五。写下“老历”这两个字时,忽然觉得它们只在过年时才被记起——像老家,那个我长到二十岁的地方。工作以后,也就过年才回了。 今年不同了。我的身份栏里添了“父亲”二字,恍惚间,竟像回到了我小时候。今天,想和家人们聊聊年味,聊聊这些年,我是有着怎样不一样的感受的。 🧨 童年的年:年是撒欢的鞭炮 小的时候,特别喜欢过年,那会的过年,从进入腊月开始,过年的氛围越来越浓了,那会我们每天兜里揣满鞭炮,满村子放鞭炮,今天给二大爷家的房顶上扔,明天炸五太爷家的果园,虽然因此也挨了不少的打,可


网络接口设备详解:从 `eth0`、`lo` 到 `br0`,看懂 Linux 网络的“器官”
哈里谢顿2026/2/4

在 Linux 系统中,执行 ip addr 或 ifconfig 命令时,你会看到形如 eth0、lo、br0、docker0、vethxxx 的网络接口。它们有的代表真实网卡,有的却是纯软件构造的“虚拟器官”。 这些接口共同构成了 Linux 强大而灵活的网络能力,支撑着从本地通信到容器化、虚拟化的复杂场景。 本文将以 eth0(物理接口)、lo(回环接口)、br0(网桥接口) 为例,深入解析各类网络接口的本质、作用及典型应用场景。 一、什么是网络接口(Network Interface)

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客