想象你受聘为一座新城设计图书馆。馆长不会让你直接画图纸,而是抛出一连串问题:读者是谁?每天来多少人?五年后要存多少书?停电了怎么办?这正是系统设计面试中数据库设计部分的真实写照——面试官就是那位“馆长”,他真正关心的是:面对模糊需求,你能否一步步设计出有容量、有速度、有保障的数据存储方案。 本文用一个贯穿始终的“建造图书馆”比喻,把数据库设计的知识串成一条线:设计原则是图书分类法,需求分析是读者画像,容量规划是书库面积,架构模式是分馆建设,性能优化是找书路径,一致性是账本准确,高可用是逃生通道,面试策略是汇报技巧。学完这篇,你就能像一位资深图书馆建筑师一样,从容回应任何数据库设计考题。

岗位关联

对 AI 从业者而言,数据库设计是系统设计面试的必考项,掌握它能让你用工程化语言与团队协作、科学评估方案取舍,是通往高级工程师与架构师的关键一步。

目录
  1. 可视化演示
  2. 设计原则:图书分类法
  3. 需求分析:读者画像
  4. 容量规划:书库面积
  5. 架构模式:分馆设计
  6. 性能优化:找书速度
  7. 一致性保障:账本准确
  8. 高可用设计:永不关门
  9. 面试策略:向馆长汇报
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示ER模型实体关系图、数据库范式理论逐级规范化与分库分表策略,含表结构设计与性能指标对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

ER模型实体关系图 实体用矩形表示属性用椭圆表示 关系用菱形连接标注基数类型 Entity → Relation → Entity ER模型是数据库概念设计的核心工具

设计原则:图书分类法

建一座图书馆,第一步不是买书,而是设计分类体系。杜威十进制分类法把人类知识分成十大类,每本书据此获得唯一索书号,读者才能按图索骥。没有分类法的图书馆,书再多也只是堆积如山。数据库的表结构设计,就是数据的“分类法”。

范式(Normal Form)是一套组织字段的规则,核心思想是让每个数据只存一次、每张表职责单一。第一范式要求字段原子化,第二范式消除部分依赖,第三范式消除传递依赖——这套规则能消除重复、避免更新异常,就像图书馆不会把同一本书的副本散落在十个书架。但面试官真正想听的,是你知道范式之后如何反范式。

反范式不是错误,而是性能工具:高频查询需要 join 多张表时,刻意在表中保留冗余字段反而省去一次关联。主键策略上,自增 ID 简单直观,UUID 适合分布式生成,雪花 ID 则兼顾趋势递增与全局唯一。索引相当于图书目录卡,但不是每本书都值得建卡——区分度太低的字段建了也白建。

分类法决定图书馆的骨架。把范式当默认选择、反范式当性能工具、主键当索书号、索引当目录卡——这套“分类学”想清楚,后续的建筑面积、分馆规划才有意义。

Selectivity=COUNT(DISTINCT column)COUNT(*)\text{Selectivity} = \frac{\text{COUNT(DISTINCT column)}}{\text{COUNT(*)}}
索引选择性越接近 1,说明该字段区分度越高,索引价值越大;越接近 0,说明字段取值单一,索引几乎无效。
  1. 统计不同取值统计该列共有多少种不同的值,相当于图书目录中登记的不同书名种类。COUNT(DISTINCT column)\text{COUNT(DISTINCT column)}
  2. 统计总行数统计整张表的全部记录行数,相当于图书馆的全部藏书总量。COUNT(*)\text{COUNT(*)}
  3. 两者相除取比值比值结果越接近 1,字段区分度越高,索引越高效;比值很低时索引性价比极低。COUNT(DISTINCT column)COUNT(*)\frac{\text{COUNT(DISTINCT column)}}{\text{COUNT(*)}}

需求分析:读者画像

馆长问:我们的读者是社区居民还是高校师生?早上人多还是晚上人多?借小说多还是教材多?不做读者画像,你就不知道书架该按热门排行还是按学科细分。数据库设计同理,任何脱离业务场景的“最优方案”都是空谈。

需求分析阶段要回答四组问题。第一,业务场景:是交易系统还是数据分析平台?第二,读写比例:是刷帖为主的读多写少,还是计费日志式的写多读少?第三,数据量级:单表能否扛住未来三年的数据规模?第四,延迟预算:用户能容忍 200ms 还是必须 20ms?

这四组答案直接决定技术选型。读多写少适合“读写分离加缓存”;写多读少要考虑消息队列削峰与批量合并;数据量过亿基本需要分库分表;延迟敏感场景要上本地缓存与索引优化。面试中,先于方案抛出这些问题,足以体现你的架构视野。

读者画像做扎实,书库面积和书架布局就有了依据。同样,一份清晰的需求清单,能让后续的容量计算与架构选择不再是拍脑袋。

Tend=Tnetwork+Tqueue+Tprocess+TstorageT_{\text{end}} = T_{\text{network}} + T_{\text{queue}} + T_{\text{process}} + T_{\text{storage}}
端到端延迟等于网络、排队、处理、存储四段耗时之和。先定总目标,再逐段分配预算。
  1. 总延迟预算用户从发出请求到收到响应的总耗时,是需求分析中最核心的指标之一。TendT_{\text{end}}
  2. 网络传输时间数据在客户端、应用服务器和数据库之间往返传输的耗时。TnetworkT_{\text{network}}
  3. 排队等待时间请求在服务器队列里等待处理的时间,高峰期会显著拉长,如同读者在借书台前排长队。TqueueT_{\text{queue}}
  4. 处理与存储时间应用计算加数据库读写磁盘的耗时,慢查询的瓶颈集中体现在这一项。Tprocess+TstorageT_{\text{process}} + T_{\text{storage}}

容量规划:书库面积

馆长问:五年后这里能容纳多少本书?你回答之前,得知道平均每本书多厚、每月进书多少、旧书要不要销毁。容量规划就是计算“书库面积”——存储空间、请求压力与未来冗余缺一不可。

存储容量估算从三条数据入手:单条记录大小、每日新增记录数、数据保留天数。三者相乘,再乘上索引与日志带来的冗余系数,就得到存储下限。访问压力则用 QPS 衡量:日活跃用户乘以人均请求数再除以一天的秒数(86400),就能估算每秒请求量。

别忘了峰值。大促的 QPS 可能是日常的 10 倍,按峰值设计成本爆炸,按均值设计必然崩溃。工程上常见做法是预留 30% 至 50% 余量,再用弹性扩容兜底。容量规划不是一次性工作,每半年要根据实际增长曲线校准一次“书库扩建计划”。

把书库面积从“大概够用”升级为“精确可算”,你就具备了用数字说服馆长的能力——系统设计面试恰恰看重这种可校准的量化思维。

Capacity=Srow×Ndaily×Dretention×(1+Roverhead)\text{Capacity} = S_{\text{row}} \times N_{\text{daily}} \times D_{\text{retention}} \times (1 + R_{\text{overhead}})
存储总容量等于单条大小、每日新增量、保留天数三者的乘积,再乘上冗余开销系数。
  1. 单条记录大小一条记录平均占用的字节数,相当于一本书的厚度;字段越多,单条越大。SrowS_{\text{row}}
  2. 每日新增记录数每天写入的增量记录量,相当于每天新采购入库的图书数量。NdailyN_{\text{daily}}
  3. 数据保留天数业务要求保存数据的时间长度,相当于图书从入库到下架的生命周期。DretentionD_{\text{retention}}
  4. 冗余开销率索引、预写日志、副本带来的额外空间,相当于编目卡片与备份藏书的占位。1+Roverhead1 + R_{\text{overhead}}

架构模式:分馆设计

当主馆藏书逼近容量、借阅排队超过忍耐限度时,馆长开始考虑开分馆。数据库架构演进的本质,就是从一个“全功能主馆”走向“多个专科分馆”的过程。

最先做的是读写分离:主库承担所有写请求,通过复制把数据同步到只读从库,读请求分流到从库,如同“总馆管入库、分馆管借阅”。接着做分库分表:按业务边界拆库称为垂直拆分,把一张大表按分片键拆成多片称为水平拆分。分片后的路由由分片键的哈希取模决定,保证数据均匀散落。

缓存是“前台热门书架”,把 80% 的高频请求挡在数据库之前;冷热分层则把低频历史数据搬到更便宜的存储,如同旧刊送入地库。每一层架构都引入新复杂度:缓存有击穿风险,分库有跨片事务难题。优秀的设计不是组件越多越好,而是刚刚够用且清楚代价。

分馆建起来后,读者借书不再挤总馆。数据库架构的核心不是“用了什么组件”,而是“什么信号驱动了下一层升级”——记住这条决策链,面试时就不会被追问到哑口无言。

shard_id=hash(shard_key) mod N\text{shard\_id} = \text{hash}(\text{shard\_key}) \bmod N
每条记录归属哪个分片,由分片键的哈希值对分片总数取模决定。
  1. 分片键决定记录归属哪一组的业务字段,如用户 ID,相当于读者借书卡上的分馆编号。shard_key\text{shard\_key}
  2. 哈希函数把分片键字符串打散成一个整数,避免相似数据聚集到同一分片。hash(shard_key)\text{hash}(\text{shard\_key})
  3. 取模运算哈希结果对总片数取余,保证每条记录唯一落到一个分片,且分片之间大致均衡。 mod N\bmod N
  4. 分片编号计算出的最终落位编号,告诉路由层“这本书该送去几号分馆”。shard_id\text{shard\_id}

性能优化:找书速度

图书馆建好了,读者抱怨“找一本书要十分钟”。馆长急了——请求超时导致的流失天天发生。性能优化,就是让读者的找书路径最短,而不是抱怨读者眼神不好。

最常见的瓶颈在索引。建立一个选择性高的索引,查找从全馆翻书变成按目录直取;但索引不是越多越好,每次写入都要同步维护索引,如同每本新书都要多抄几份目录卡。SQL 优化同样重要:避免 SELECT *、避免在索引列上做函数运算、合理利用覆盖索引,都是低成本高回报的习惯。

连接池是数据库的“借书窗口”。窗口太少读者排队,窗口太多挤占空间。经验公式把连接数定为 QPS 与平均响应时间(毫秒)的乘积。慢查询治理则要建立监控:把耗时超阈值的 SQL 捞出来,用 EXPLAIN 分析执行计划,像给每位读者规划一条个性化推荐路径。

找书速度提上去了,读者满意度自然回来。性能优化没有银弹,只有从索引、SQL、连接池、慢查询四个方向逐一做减法,把每次查询的“找书动线”压缩到最短。

Connections=QPS×RTavg1000\text{Connections} = \frac{\text{QPS} \times \text{RT}_{\text{avg}}}{1000}
连接池大小约等于每秒请求数乘以单次请求平均耗时(毫秒)再除以 1000,结果再留少量余量。
  1. 每秒查询数系统峰值时的请求频率,相当于高峰期每秒进入图书馆的读者人数。QPS\text{QPS}
  2. 平均响应时间单次请求从发出到返回的平均耗时(毫秒),相当于每位读者在借书窗口前停留的时间。RTavg\text{RT}_{\text{avg}}
  3. 单位换算与相乘把毫秒换算为秒后与 QPS 相乘,得到同一秒内并发进行中的请求数量。QPS×RTavg1000\frac{\text{QPS} \times \text{RT}_{\text{avg}}}{1000}
  4. 连接数计算结果即连接池大小的起点,再按经验乘上 1.2 至 1.5 的余量系数。Connections\text{Connections}

一致性保障:账本准确

图书馆最怕什么?系统显示“在馆”,书架上却没有;读者还了书,系统还挂着“未还”。账本错乱会让整个图书馆失去信任。数据库的一致性保障,就是保证这本“大账本”永远算得清。

单机数据库靠 ACID 保底:原子性让一组操作要么全成要么全败,持久性保证提交后不丢,隔离性让并发事务互不干扰,一致性让数据始终满足约束。隔离级别从“读未提交”到“串行化”,防护逐渐升级但代价逐渐升高,如同图书馆从口头借阅到书后登记卡再到统一前台登记。

分布式场景下,跨库事务极其昂贵。两阶段提交像馆长召集分馆主管开会:各馆先报告“准备完毕”,再统一宣布“执行”,有人缺席就全员回滚。TCC、本地消息表和 Saga 则是更务实的补偿思路:先把能做的都做了,谁失败就做反向操作抹平账目,接受短暂的“最终一致”。

账本准确是图书馆的生命线。设计时先问业务“这个账能不能晚几秒对上”——能,就用最终一致换性能;不能,就上隔离与分布式事务。这种对一致性的精确分层,正是初级与资深设计师的分水岭。

Teventual=Tcommit+TreplicateT_{\text{eventual}} = T_{\text{commit}} + T_{\text{replicate}}
最终一致的时间等于主库提交时刻加上副本同步延迟,这段时间内用户可能读到旧数据。
  1. 主库提交时间事务在主库完成提交、写成功的时刻,相当于一号分馆完成登记备案。TcommitT_{\text{commit}}
  2. 副本同步延迟主库变更复制到从库所耗费的时间,相当于分馆之间传递登记卡片的时间。TreplicateT_{\text{replicate}}
  3. 最终一致窗口主从同步完成前,用户读到旧数据的最大时间范围,业务能容忍它即可用最终一致方案。TeventualT_{\text{eventual}}

高可用设计:永不关门

对读者而言,图书馆最大的灾难不是书乱,而是“今天闭馆、明天未知”。系统设计里的高可用,就是让数据库这家“图书馆”无论遭遇停电、火灾还是施工,都尽量少关门、晚关门,关门了也能快速重开。

高可用的第一道防线是主从复制:主库负责写,数据实时复制到备库,主库挂了备库顶上。第二道防线是故障转移:通过哨兵或协调组件自动检测心跳,几秒内把流量切到新主库。恢复时间用 RTO 刻画,数据损失用 RPO 刻画。第三道防线是备份:全量备份打底、增量备份收尾,像每晚闭馆后抄录一遍全馆书目。

再往上是容灾架构:同城双活(两个机房同时服务)、两地三中心(本地加异地灾备)、甚至异地多活。每升一级,成本成倍增加。面试中不必张口就是多活,而是根据业务重要度选匹配的可用性目标——99.9% 的可用性意味着一年最多故障 8.76 小时,高可用不是越高越好,而是“贵得其所”。

图书馆的承诺不是“永不关门”,而是“知道什么时候能重新开门”。高可用设计的价值,是明确 RTO 与 RPO 并用分级架构去兑现,把不可控的“天灾”变成可控的“预算”。

Availability=UptimeUptime+Downtime×100%\text{Availability} = \frac{\text{Uptime}}{\text{Uptime} + \text{Downtime}} \times 100\%
可用性等于可用时间除以总时间再乘 100%。0.1% 的不可用时间换算到一年约等于 8.76 小时。
  1. 正常运行时长系统在统计周期内持续提供服务的时间,相当于图书馆正常开放时长。Uptime\text{Uptime}
  2. 中断时长系统不可用的累计时间,相当于图书馆临时闭馆的累计时长。Downtime\text{Downtime}
  3. 计算可用性两者比值乘 100% 得到年度可用率,用于把抽象 SLA 换算成可感知的故障预算。UptimeUptime+Downtime×100%\frac{\text{Uptime}}{\text{Uptime}+\text{Downtime}} \times 100\%

面试策略:向馆长汇报

你终于要对馆长汇报图书馆设计方案了。候选人 A 上来就画分馆图纸;候选人 B 先问读者规模、预算与借阅峰值,再给出分阶段规划——面试官更愿意听完 B 的方案。系统设计面试考察的不是唯一答案,而是你在模糊问题下的决策能力。

一个完整的回答闭环包含四步。第一步需求澄清:确认读写比例、数据量级、一致性要求。第二步方案权衡:给出两三个可选方案,比较容量、延迟、成本与复杂度。第三步渐进设计:从单库加索引出发,在容量或延迟触发时逐步引入缓存、读写分离、分库分表。第四步深度复盘:追问“数据量再翻十倍怎么办”“某个分片挂了怎么办”,把纵深话题提前想透。

这里有一个可视化思维:把每次权衡画成一张评估表,按权重打分。例如“读性能权重 0.4、写入一致性权重 0.3、可运维性权重 0.3”,每个方案加权得分就是你的推荐。用分数说话,比“我觉得”更有说服力。

馆长记住的不是你画了多豪华的图,而是你是否像一位真正的图书馆建筑师:问对问题、算出容量、给出取舍、讲清风险。面试通关,本质上就是完成一次漂亮的方案汇报。

Score=∑i=1nwi×si\text{Score} = \sum_{i=1}^{n} w_i \times s_i
方案总分等于每项评估指标的权重乘以该项得分的累加和,权重由业务目标决定。
  1. 确定指标权重根据业务目标给每个评估维度分配权重,总和为 1,相当于馆长对“成本、体验、交付速度”的重视程度。wiw_i
  2. 方案逐项打分为每个候选方案在某维度上打分(如 1 至 5 分),相当于你为不同设计分别打的印象分。sis_i
  3. 累计求总分各项权重与得分相乘后累加,总分最高者即为推荐方案,让决策透明可复用。∑i=1nwi×si\sum_{i=1}^{n} w_i \times s_i

AI 应用场景

电商订单系统的读写分离与分库设计

阶段:系统设计与数据架构

解决的问题:大促期间订单查询量飙升,单库连接打满、读延迟从 80ms 涨到 2s。传统“一台主库扛全部流量”的方式无法水平扩展。借助读写分离,把 95% 的查询请求分流到只读从库;订单库再按用户 ID 哈希分片,每片挂一组主从,容量与并发同时扩展。

具体案例:某跨境电商在 618 大促前评估:日活 500 万、人均读请求 20 次,估算峰值读 QPS 约 5.8 万。按连接池公式配置 3 个只读从库与每库 200 连接,压测 P99 稳定在 120ms 内,主库负载保持在 60% 以下。

AI 特征存储的冷热分层

阶段:特征工程与数据存储

解决的问题:推荐系统的特征数据每天新增数十亿条,全部放 Redis 成本不可承受。运用数据库设计的冷热分层思路:近 3 天的热特征放 Redis,近 3 个月的温特征放 SSD 存储,历史冷特征归档到对象存储,训练时按时间窗回放。

具体案例:某短视频推荐团队将特征按热度分桶写入两级缓存,热特征命中率 98%,存储成本下降 60%,特征回放延迟从小时级降到分钟级。

Python 演示

python34 行
import hashlib  class ShardRouter:    """模拟分库路由器:将记录的 key 哈希后均匀映射到多个分片。"""     def __init__(self, shard_count):        self.shard_count = shard_count        self.shards = [0] * shard_count  # 每个分片的数据条数     def route(self, key):        """计算 key 应落入的分片编号,并累计计数。"""        digest = hashlib.md5(key.encode('utf-8')).hexdigest()        shard_id = int(digest, 16) % self.shard_count        self.shards[shard_id] += 1        return shard_id     def summary(self):        total = sum(self.shards)        return {            'total': total,            'shards': self.shards,            'balance': max(self.shards) - min(self.shards) if total else 0,        }  if __name__ == '__main__':    router = ShardRouter(4)    for user_id in range(10000):        router.route('user_{}'.format(user_id))    result = router.summary()    print('总记录数:', result['total'])    print('各分片记录数:', result['shards'])    print('最大偏差:', result['balance'])
目的:模拟一个基于 MD5 哈希的分库路由,将 1 万条用户数据均匀分配到 4 个分片,并输出各分片负载用于评估分片均衡度。
  1. self.shards = [0] * shard_count给每个分片准备一个计数器,用来观察数据是否均匀分布。
  2. digest = hashlib.md5(key.encode('utf-8')).hexdigest()对分片键计算 MD5 值,把字符串映射成散列摘要。
  3. shard_id = int(digest, 16) % self.shard_count将摘要转整数后对分片数取模,得到最终落库位置。
  4. self.shards[shard_id] += 1对应分片计数加一,模拟一条数据写入该分片。
  5. 'balance': max(self.shards) - min(self.shards)最大分片与最小分片的差值,差值越小说明路由越均衡。

常见误区

误区同学面试时说“我的表全部达到了第三范式”,面试官连连点头。那我把所有表都设计成 3NF,是不是就是高手了?

正解范式消除冗余,但代价是查询要 join 更多表。3NF 是默认起点,不是终点。在热点查询上适度反范式(冗余关键字段)能大幅减少 join,这是“以空间换时间”。正确姿势是:默认 3NF,遇到读瓶颈时定向反范式,并评估写入侧的维护成本,就像图书馆在热门书架放多本副本一样。

为何常见:教材把范式当作理论规范,学生容易把“规范”理解成“越高越好”,忽略了数据库设计的本质是性能与可维护性的权衡。

误区查询慢?给涉及的字段都加上索引不就行了?索引就是个目录,多建几本目录总有好处。

正解每条索引都会拖慢写入:插入时要更新索引、可能触发页分裂。索引过多还会让优化器选错执行计划。建索引前先看选择性(区分度),性别这类低区分度字段建了也不生效。另外要避免重复索引,优先使用组合索引与覆盖索引。

为何常见:教程常常强调“索引加速查询”,却很少在入门阶段讲清索引的写放大成本,导致“越多越好”的直觉。

误区数据量过百万我就开始设计分库分表;面试中一开口就说“我们用了分库分表”,觉得特别高级。

正解分库分表是复杂度放大器:跨节点事务、全局主键、跨分片聚合、数据迁移都随之而来。正确升级顺序是:单库索引/SQL 优化 → 加缓存 → 读写分离 → 单表拆表 → 分库分表。只有当单库容量或 QPS 逼近极限时才动手。

为何常见:互联网大厂面经把分库分表当“标配”,让候选人盲目追热点,忽视了决策需要数据支撑。

误区数据必须实时一致,任何读到旧数据的系统都是有 bug 的。最终一致?不存在的。

正解强一致依赖分布式事务,代价高、吞吐低。点赞数、排行榜、阅读量这类场景,秒级延迟用户可以接受,最终一致是理性选择。判断标准是业务能否容忍不一致窗口及其时长,而不是“听起来是否正确”,这与图书馆允许“借阅记录晚几分钟同步到分馆”同理。

为何常见:单机数据库的 ACID 先入为主,让人把“最终一致”误解为“数据会出错”。

误区“我们系统可用性 100%,从来没有挂过。” 面试这么答很硬气吧?

正解严肃的可用性设计一定有量化指标:RPO 控制丢多少数据、RTO 控制恢复多久,以及年度可用性百分比对应的故障预算。99.99% 可用性等于每年约 52 分钟故障时间。高可用本质是预算分配问题,必须换算成业务语言。

为何常见:人容易把“可靠性”当成非黑即白的概念,而可用性本质是概率与成本的权衡。

面试题

Q1. 范式设计中,第三范式主要消除的是哪种依赖?

单选题 · easy

  • A.函数依赖
  • B.传递依赖← 正确答案
  • C.多值依赖
  • D.部分依赖

答案:B。第二范式消除部分依赖,第三范式消除传递依赖,多值依赖属于第四范式范畴。可类比图书分类:传递依赖好比“书→书架号→楼层”,第三范式要求书直接记录楼层,而不是依赖书架号再去推楼层。

Q2. 典型读多写少场景下的读写分离方案是?

单选题 · easy

  • A.全部写入从库
  • B.读从库写主库← 正确答案
  • C.只读主库一份
  • D.主备库轮流写

答案:B。读写分离的核心是把写请求集中到主库,读请求分流到从库。若反向把写放从库,会导致数据同步回主库时冲突,如同备分馆只负责借书而不负责采书。

Q3. 某系统年度可用性为 99.9%,全年最多可接受的故障时间约是?

单选题 · mid

  • A.约 8.76 小时← 正确答案
  • B.约 87.6 小时
  • C.约 0.88 小时
  • D.约 876 小时

答案:A。年度总时间为 365×24=8760 小时,不可用比例 0.1%,即 8760×0.001=8.76 小时。这相当于图书馆一年里最多闭馆一天。其余选项是 10 倍数量级偏差。

Q4. 水平分库分表时,决定一条记录归属哪个分片的依据是?

单选题 · mid

  • A.主键自增轮询
  • B.哈希取模分片← 正确答案
  • C.随机均匀分配
  • D.写入时间切割

答案:B。常规做法是分片键哈希后对分片总数取模,保证分布均匀且可重复路由。自增轮询无法按用户聚合,随机分配导致重复读不稳定,时间切割易造成数据倾斜。

Q5. ACID 特性中的 I(隔离性)指的是什么?

单选题 · mid

  • A.原子性 Atomicity
  • B.一致性 Consistency
  • C.隔离性 Isolation← 正确答案
  • D.持久性 Durability

答案:C。ACID 分别对应原子性、一致性、隔离性、持久性。隔离性保证并发事务互不干扰,就像两位读者同时借同一本书,系统也要给出有序且不冲突的结果。

Q6. 某系统设计目标是 RPO=0、RTO=30 分钟,以下理解正确的是?

单选题 · hard

  • A.允许丢失最近 30 分钟数据
  • B.故障恢复最多需 30 分钟← 正确答案
  • C.数据最多回退到 30 分钟前
  • D.每年可用性达到 99.999%

答案:B。RPO=0 表示故障时数据零丢失,RTO=30 分钟表示恢复服务最多需要半小时。A 与 C 描述的是 RPO=30 分钟的效果,D 与 RTO/RPO 无直接换算关系,如同要求“账本一分不差且闭馆最多半小时”。

Q7. 系统峰值 QPS=2000,平均响应时间 50ms,按公式估算数据库连接池至少需要多少连接?

单选题 · hard

  • A.约 40 个连接
  • B.约 100 个连接← 正确答案
  • C.约 400 个连接
  • D.约 1000 个连接

答案:B。连接数约等于 QPS×RT(毫秒)÷1000,即 2000×50÷1000=100。好比高峰期每秒 2000 位读者、每人停留 50 毫秒,就需要 100 个借书窗口同时工作。

Q8. 关于反范式设计,下列说法正确的是?

多选题 · mid

  • A.减少多表关联查询← 正确答案
  • B.写入需维护冗余← 正确答案
  • C.读快写慢需要权衡← 正确答案
  • D.彻底消除所有数据冗余

答案:ABC。反范式通过冗余字段减少 join,读快但写代价高,需要在二者间权衡。D 错误,反范式不仅不消除冗余,反而主动引入冗余。可理解为在热门书架上多放副本,借书更快,但规整书架要多花功夫。

Q9. 选择水平分片键(Shard Key)时应考虑哪些因素?

多选题 · hard

  • A.数据分布均匀避免热点← 正确答案
  • B.同业务数据尽量同片← 正确答案
  • C.哈希分片天然支持范围扫描
  • D.分片键需要长期保持稳定← 正确答案

答案:ABD。A 避免单分片过热,B 让 join 尽量在片内完成,D 因为分片键一旦选定难以更改。C 错误,哈希分片会把有序数据打散,范围扫描往往要跨片聚合,如同按姓氏拼音分馆方便同姓归架,但按主题批量查书就要多馆跑。

Q10. 下列哪些属于常见的分布式事务实现方案?

多选题 · extreme

  • A.两阶段提交协议← 正确答案
  • B.TCC 补偿事务← 正确答案
  • C.本地消息表方案← 正确答案
  • D.单库串行执行方案

答案:ABC。两阶段提交、TCC、本地消息表和 Saga 都是标准分布式事务方案;单库串行执行只是单机兜底手段,无法解决跨库原子性问题。类比协调多个分馆统一盘点:要么全员同时确认,要么有人失败就全员回滚或事后补偿。