backend · diff

git:20260817.f3a9563 to git:20260908.a2a662c

104 added, 20 removed. Audit A to A.

---
name: backend
- description: 后端架构与方法论出题(栈无关):缓存一致性、可扩展性、可靠性、系统设计场景题。目标岗位是后端/服务端/全栈时加载;具体语言细节交给对应 stack 包。
- keywords: [后端, 服务端, backend, 全栈, 微服务, 分布式, server]
+ description: 后端架构与方法论出题(栈无关):缓存、数据库扩展、消息队列、可靠性与容错、接口设计、可观测性、限流与容量。岗位或简历涉及后端开发、服务端、Backend Engineer、微服务、分布式系统时加载。
+ keywords: [后端, 服务端, 后端开发, backend, backend engineer, 微服务, 分布式, 缓存, redis, 消息队列, kafka, 高并发, 限流, 可观测性, api设计]
layer: domain
---
- ## 出题原则
+ ## 岗位职责与考察重点
- - 架构题从"场景 + 约束"切入,不从概念切入:给定 QPS/数据量/一致性要求,问怎么设计、哪里先崩。
- - 每道题必须有可权衡的空间(CAP、成本、复杂度),能答出取舍才是好信号。
- - 结合候选人项目规模出题:学生项目问对应量级的演进路径,不空谈亿级流量。
+ 后端工程师(服务端开发、Backend Engineer)的日常是把业务需求变成稳定、可扩展、可排查的线上服务:设计接口与数据模型、处理并发与一致性、接缓存与队列、盯监控与告警、扛流量高峰、处理线上故障。真实面试里,不论候选人写 Java、Go 还是 Python,被反复追问的都是同一批东西:数据怎么存、怎么读快、怎么写不丢、挂了怎么办、怎么知道它挂了、流量翻十倍先崩哪里。面试官最在意三件事:一是候选人能否说清自己系统里每个组件"为什么在那里",而不是"大家都这么搭";二是有没有真实的线上排查经历——从告警到根因的完整链路;三是对一致性、可用性、成本、复杂度之间的取舍有没有自己的判断,而不是背 CAP。
- ## 高频主题与深度阶梯(入门 → 原理 → 场景排查 → 权衡)
+ 校招侧重基础:HTTP 与 RPC 的差别、缓存的基本用法、事务与锁、能否把一个简单业务拆成合理的表和接口,重点看思路是否清晰、能否被追问推着往下想。社招侧重工程判断:给一个具体故障或容量问题,看排查顺序、看有没有量化(QPS、P99、连接数、命中率)、看方案里有没有回滚与降级。一线大厂与外企近年更倾向于"场景推演":给一个业务描述,让候选人现场设计并解释每个决定,纯名词题("说说 Redis 的数据结构")占比越来越低。
- - 缓存:为什么加缓存 → 缓存一致性方案对比 → 缓存击穿/雪崩线上处置 → 本地缓存 vs 分布式缓存取舍
- - 数据库扩展:读写分离 → 分库分表键选择 → 热点行更新怎么办 → 强一致 vs 最终一致的业务映射
- - 异步与解耦:为什么上消息队列 → 至少一次语义与幂等设计 → 积压排查 → 同步调用 vs 异步的边界
- - 可靠性:超时重试怎么设 → 幂等/去重实现 → 级联故障与熔断 → 降级时牺牲什么
- - 接口设计:REST 语义 → 分页/批量接口陷阱 → 版本兼容 → 限流位置的选择
+ ## 主题
+ ### 缓存设计与一致性
+ - 阶梯:为什么加缓存、放哪一层 → Cache Aside / Read Through / Write Behind 各自的适用场景,为什么"先删缓存再写库"会出问题 → 缓存击穿、穿透、雪崩在线上各自长什么样、怎么区分、怎么修 → 强一致与最终一致的边界:哪些数据可以容忍几秒不一致,哪些必须走数据库
+ - 好题:商品详情页缓存命中率突然从 98% 掉到 60%,数据库 CPU 飙高,你按什么顺序排查?如果发现是大促前运营批量改价触发了集中失效,短期与长期各怎么处理?
+ - 危险信号:只会说"用 Redis 加缓存";把延迟双删当标准答案却说不出它解决的是什么竞态;不知道自己系统缓存的命中率与 key 数量级
+ - 期望信号:能画出读写路径并指出竞态窗口;区分热 key、大 key 的处理(本地缓存、拆分、异步加载);知道过期时间加随机抖动、互斥重建、布隆过滤器各解决什么问题;能说出哪些业务用了缓存但接受了什么程度的不一致
+
+ ### 数据库扩展与分库分表
+ - 阶梯:单库能撑多大、什么信号说明该拆了 → 读写分离的主从延迟怎么处理,垂直拆与水平拆的区别 → 分片键选错之后(热点、跨片查询、扩容重分布)怎么办 → 分库分表 vs 分布式数据库(TiDB 等)vs 换存储(宽表、ES)的取舍
+ - 好题:订单表到 20 亿行,按用户 ID 分了 64 片,现在客服要按商家维度查订单,你有哪些方案?各自对写入路径、一致性和运维的代价是什么?
+ - 危险信号:一上来就分库分表而没试过索引优化、归档、读写分离;说不出分片键的选择依据;不知道跨片事务和跨片分页的代价
+ - 期望信号:先做过冷热分离/归档;分片键按主要查询维度选并接受次要维度走异构索引(ES/宽表/映射表);扩容方案有一致性哈希或成倍扩容的思路;主从延迟场景下有"写后读走主库"之类的处理
+
+ ### 消息队列与异步解耦
+ - 阶梯:什么场景该异步 → 至少一次、至多一次、恰好一次各在哪一层保证,消费端幂等怎么做 → 消息堆积、消费者慢、重复消费、顺序错乱的排查与处理 → 削峰填谷带来的最终一致,业务上怎么向用户交代,什么场景不该用队列
+ - 好题:支付成功后发货的链路走 MQ,某天堆积了 200 万条,用户投诉收不到发货通知,你先看什么、怎么快速恢复、事后怎么防止?扩消费者会不会打垮下游?
+ - 危险信号:认为 MQ 能保证恰好一次;消费端没有幂等设计;不知道自己 topic 的分区数、消费组、消费延迟指标
+ - 期望信号:幂等靠业务主键或去重表;堆积时有限流下游与动态扩容并存的方案;顺序需求用同一分区键保证并知道会牺牲并行度;死信队列与告警;能说出事务消息或本地消息表怎么保证"写库与发消息"一致
+
+ ### 分布式事务与一致性
+ - 阶梯:为什么跨服务不能用数据库事务 → 2PC、TCC、Saga、本地消息表各自的模型与代价 → 补偿失败、悬挂、空回滚这些边界情况怎么处理 → 业务上能不能通过重新定义流程避开分布式事务
+ - 好题:下单扣库存与扣优惠券在两个服务,产品要求"要么都成功要么都失败",你会怎么设计?如果扣券成功但库存不足,补偿失败了怎么办?有没有办法让这个流程根本不需要分布式事务?
+ - 危险信号:只会说"用 Seata";把最终一致当成不一致;不知道补偿也会失败
+ - 期望信号:优先考虑通过预占、状态机、异步核对避开强一致;TCC 有 try 阶段资源预留的具体设计;有对账与人工兜底;能量化一致性窗口
+
+ ### 接口设计与 API 演进
+ - 阶梯:RESTful 与 RPC 怎么选 → 幂等键、分页游标、错误码体系、版本管理各自的设计 → 接口被上游重试打爆、老版本客户端无法下线怎么处理 → 内部接口用 gRPC/Thrift 还是 HTTP JSON,字段兼容规则怎么定
+ - 好题:一个创建订单接口,客户端网络抖动重试三次,如何保证只创建一单?幂等键放哪里、存多久、并发下两个请求带同一个键同时到达怎么办?
+ - 危险信号:认为接口设计就是命名规范;幂等靠"数据库唯一索引报错"却没考虑用户体验与并发;分页用 offset 到几百万页
+ - 期望信号:幂等键客户端生成、服务端先占位再处理;游标分页;错误码区分可重试与不可重试;字段只加不删不改语义;有接口文档与契约测试
+
+ ### 可靠性:超时、重试、熔断、降级
+ - 阶梯:为什么每个远程调用都必须有超时 → 重试放大(retry storm)怎么发生,退避与抖动、重试预算 → 依赖服务变慢导致线程池耗尽、级联雪崩的排查 → 降级什么、保什么,怎么定义核心链路,预案怎么演练
+ - 好题:一个下游服务 P99 从 50ms 涨到 2s 但没有挂,你的服务却整体不可用了,为什么?怎么设计能让下游变慢时你只损失那一个功能?
+ - 危险信号:所有调用共用一个线程池/连接池;重试没有上限没有退避;熔断阈值是拍脑袋的
+ - 期望信号:按依赖隔离资源(线程池/信号量/连接池);超时按链路预算逐级分配;熔断半开探测;降级有开关有兜底数据;能说出一次真实的雪崩与修复
+
+ ### 可观测性与线上排查
+ - 阶梯:日志、指标、链路追踪各自回答什么问题 → RED/USE 指标怎么选,采样率与成本 → 告警了但日志里什么都没有、慢请求只在特定用户复现怎么查 → 告警噪音治理与 SLO 驱动的告警
+ - 好题:凌晨告警接口错误率 3%,你打开监控后按什么顺序看?如果发现只有一台机器错误率高,怀疑哪些原因?如果全部机器都高但下游都正常呢?
+ - 危险信号:排查全靠 grep 日志;不知道自己服务的 P99 是多少;告警多到没人看
+ - 期望信号:先看变更(发布、配置、流量),再看依赖,再看资源;有 trace ID 贯穿;区分错误率、延迟、饱和度;提到告警要有 runbook;能讲出一次从告警到根因的完整过程
+
+ ### 限流、容量规划与压测
+ - 阶梯:为什么要限流、限在哪一层 → 令牌桶、漏桶、滑动窗口的差别,单机限流与分布式限流 → 压测数据怎么造、怎么隔离、压测发现瓶颈在哪一层 → 容量按峰值备还是弹性扩,成本与 SLA 的取舍
+ - 好题:活动前要求系统能扛 10 倍日常流量,你怎么评估当前容量、怎么压测、压出瓶颈后优先扩什么?活动当天流量超过预期 3 倍,限流应该丢谁的请求?
+ - 危险信号:限流阈值不知道从哪来;压测只压单接口不压链路;扩容只加应用不看数据库连接数
+ - 期望信号:从数据库、缓存、下游依赖逐层算容量;压测有影子库或流量标记;限流按用户/接口/租户分级并有优雅拒绝;热点隔离;提到预案与降级开关
+
+ ### 服务拆分与微服务治理
+ - 阶梯:为什么拆、什么时候不该拆 → 按什么边界拆(领域、团队、变更频率),共享数据库为什么是反模式 → 拆完之后调用链变长、分布式事务、联调困难怎么办 → 单体、模块化单体、微服务的成本对比
+ - 好题:一个 30 人团队的单体越来越难发布,你会怎么判断该不该拆、先拆哪一块、怎么做到拆的过程中不停服?
+ - 危险信号:认为微服务天然更好;拆分依据是技术层(controller/service 分服务)而不是业务边界;没考虑数据归属
+ - 期望信号:绞杀者模式逐步迁移;数据先解耦再服务解耦;有服务注册发现、配置中心、统一网关的认识但不迷信;能说出拆过头之后的合并案例
+
+ ### 高并发写入与热点处理
+ - 阶梯:秒杀、抢券这类场景难在哪 → 库存扣减放 Redis 还是数据库,Lua 原子操作与数据库行锁的对比 → 热点行锁等待、Redis 单 key 打满单分片怎么办 → 准确性、公平性、吞吐三者怎么排优先级
+ - 好题:一万个人抢一百件商品,你的扣减方案是什么?超卖怎么防、少卖怎么防、Redis 与数据库不一致怎么核对?
+ - 危险信号:直接 update 库存表扛并发;不知道热点行锁;没有异步落库与对账
+ - 期望信号:前置拦截(本地限流、排队)、Redis 预扣、异步落库、对账补偿;热点 key 拆分;请求合并;能说出 Redis 宕机时的兜底
+
+ ### 存储选型与数据建模
+ - 阶梯:关系型、KV、文档、宽表、搜索、时序各适合什么 → 同一份数据多副本(主库 + ES + 缓存)怎么保持同步 → CDC/binlog 同步延迟、乱序、丢失的处理 → 多存储带来的一致性与运维复杂度值不值
+ - 好题:商品搜索要支持多条件筛选与全文检索,你会把数据同步到 ES 吗?怎么同步、延迟多久、ES 挂了搜索怎么办、两边数据不一致怎么发现?
+ - 危险信号:什么都放 MySQL 或什么都放 MongoDB;双写却不处理失败;不知道 binlog 订阅这条路
+ - 期望信号:按查询模式选存储;CDC 而不是双写;有对账任务;ES 降级回数据库简单查询
+
+ ### 配置、发布与变更管理
+ - 阶梯:为什么大多数故障来自变更 → 灰度发布、蓝绿、金丝雀的区别,配置中心的动态推送 → 发布中新老版本共存导致的兼容问题、回滚时数据库 schema 怎么办 → 发布频率与稳定性的平衡,什么该自动化
+ - 好题:一次发布后 10% 机器错误率升高,你怎么判断是新版本问题还是机器问题?回滚了但数据库已经跑了迁移脚本怎么办?
+ - 危险信号:发布靠人肉;schema 变更与代码变更绑在一次发布里;没有回滚演练
+ - 期望信号:先扩后缩的 schema 变更(加列、双写、切换、删列);灰度按机器/用户比例并观察指标;配置变更也走灰度;变更记录可查
+
+ ### 安全与鉴权基础
+ - 阶梯:认证与授权的区别 → session、JWT、OAuth2 各适合什么,JWT 怎么吊销 → 越权访问(横向/纵向)怎么在框架层统一防、接口被刷怎么处理 → 安全措施对性能与开发效率的影响
+ - 好题:用户反馈能看到别人的订单,你怀疑哪些环节?怎么在架构上让这类漏洞不再依赖每个开发者记得检查?
+ - 危险信号:认为 JWT 天然安全;鉴权散落在各个 controller;敏感数据明文存日志
+ - 期望信号:资源级权限校验在统一层;JWT 短有效期 + 刷新或黑名单;接口签名与风控;日志脱敏
+
## 好题 / 坏题对比
- - 坏:谈谈你对高并发的理解。
- - 好:商品详情页读 QPS 两万、库存必须准确,缓存和数据库之间你怎么设计?超卖怎么防?
+ - 坏:说说缓存穿透、击穿、雪崩分别是什么、怎么解决。
+ - 好:你负责的商品详情页缓存命中率半小时内从 98% 掉到 60%,数据库 CPU 从 30% 涨到 90%,告诉我你打开监控后看的前三个指标,以及三种可能的原因各自对应什么修法。
+
- 坏:介绍一下微服务架构的优缺点。
- - 好:你的单体项目如果要拆出第一个服务,你会先拆哪个?拆完之后原来的一个事务怎么办?
+ - 好:你的服务依赖的一个下游 P99 从 50ms 涨到 2s,下游并没有宕机,但你的整个服务不可用了。为什么会这样?改造后要做到"下游变慢只损失那一个功能",需要动哪几处?
+ - 坏:分布式事务有哪些方案?
+ - 好:下单要同时扣库存和扣优惠券,两个服务两个库。产品要求"要么都成功"。你会怎么做?如果扣券成功后库存不足,补偿又失败了呢?有没有办法通过改流程让这里根本不需要分布式事务?
+
## 项目结合钩子
- - 候选人项目里出现"缓存/队列/定时任务/分表"任一关键词 → 沿对应深度阶梯往下追两层。
- - 项目没有量级描述 → 先问量级,再按真实量级出演进题。
+ - 简历出现"高并发" → 追具体 QPS、P99、机器数、瓶颈在哪一层、压测怎么做的;说不出数字的要标记
+ - 简历出现 Redis → 追缓存了什么、命中率、大 key/热 key 怎么处理、Redis 挂了系统会怎样、有没有出过缓存一致性事故
+ - 简历出现 Kafka/RocketMQ/RabbitMQ → 追消费幂等怎么做、堆积过没有、顺序有没有要求、分区数怎么定的
+ - 简历出现分库分表 → 追分片键、跨片查询怎么办、扩过容没有、为什么不先做归档或读写分离
+ - 简历出现微服务 → 追拆了几个、按什么拆、拆完最痛的问题是什么、有没有合并过
+ - 简历出现"性能优化 X%" → 追优化前后指标、怎么定位瓶颈、是不是压测环境的数字、上线后有没有回归
+ - 简历出现线上故障处理 → 追告警怎么发现、多久定位、根因是什么、事后改了什么机制
+ - 简历出现"接口设计" → 追幂等、分页、错误码、版本兼容怎么做的,有没有被上游重试打爆过
- ## 期望信号提示
+ ## 出题原则
- - 好回答:先确认约束再给方案、主动说出方案的代价、能落到具体组件行为。
- - 危险信号:上来背方案名词、所有场景都答"上 Redis/上 MQ"、说不出失败模式。
+ - 栈无关:本包只考架构与方法论,具体语言与框架细节交给对应的 stack 包(backend-java、backend-go 等)追问。
+ - 每道题必须有数字:候选人说了任何架构决策,追 QPS、数据量、延迟、机器数,用规模检验方案是否过度设计或设计不足。
+ - 优先考故障与取舍:不问"X 是什么",问"X 坏了长什么样""为什么不用更简单的 Y"。
+ - 结合候选人项目规模出题:日活几千的项目不问分库分表,日活千万的项目必须追容量、降级与变更管理。