【场景面试题】五、分布式、平台与稳定性(40~50)
2026/7/13 13:45:14 网站建设 项目流程

文章目录

    • 五、分布式、平台与稳定性(40~50)
    • 40. 下单同时扣库存、建订单、发券,分布式事务怎么选?
      • 具体示例
    • 41. 定时任务实例宕机后不能丢,如何设计分布式调度?
      • 具体示例
    • 42. Redis 分布式锁怎样才相对可靠?何时不该用?
      • 具体示例
    • 43. 分布式 ID 如何唯一、趋势递增并处理时钟回拨?
      • 具体示例
    • 44. 设计大文件/视频上传与异步转码系统。
      • 具体示例
    • 45. 设计每秒百万条的日志采集与检索系统。
      • 具体示例
    • 46. 设计搜索联想/自动补全,要求 50ms 内返回。
      • 具体示例
    • 47. 高风险功能如何做配置中心、灰度发布和安全回滚?
      • 具体示例
    • 48. 核心服务如何做同城双机房或异地多活?
      • 具体示例
    • 49. RPC 超时后该不该重试?如何避免重试风暴?
      • 具体示例
    • 50. 线上大面积报错:Oncall 如何止血、定位并复盘?
      • 具体示例

五、分布式、平台与稳定性(40~50)

40. 下单同时扣库存、建订单、发券,分布式事务怎么选?

先缩事务。同库操作优先本地事务;库存预占和订单创建是核心,发券、通知等派生动作可通过 Outbox 最终一致。不要一上来选择 2PC。

跨服务。使用 Saga/TCC:每个参与者提供 Try/Confirm/Cancel 或正向动作与补偿;协调器持久化状态并重试。所有阶段携带 global_tx_id 幂等,超时由扫描任务继续推进。

不可补偿。券已使用、通知已送达等动作不能简单“回滚”,需要业务状态和人工处理路径。补偿也会失败,因此要有重试上限、隔离队列和对账。

选择依据。2PC 适合少参与者、延迟可接受且基础设施成熟的强一致场景;Saga 可用性更好但会暴露中间状态。高质量答案应明确一致性价值,而不是背协议名词。

具体示例

具体设定。用户下单后要扣库存、建订单、赠送优惠券,发券失败不应让库存长期占用。

落地例子。库存预占和订单创建作为核心流程:订单服务写订单和 outbox,库存服务 Try 预占;支付成功 Confirm,超时 Cancel。发券作为订阅订单已支付事件的最终一致动作,失败进入重试和补偿,不阻塞下单事务。

面试可讲。先缩小强一致边界。库存和订单需要较强一致,通知/发券通常可以最终一致。不要一上来 2PC,把所有副作用绑死。分布式事务比较常用的方案:使用MQ + 本地事务表

41. 定时任务实例宕机后不能丢,如何设计分布式调度?

持久化。任务保存 next_trigger_time、payload_ref、status、version、retry_count;调度器按时间索引扫描。领取任务时写 owner 与 lease_expire_time,执行者续租,宕机后租约过期可被重新领取。

分层。远期任务留在数据库/延迟队列,近未来任务加载到时间轮;避免每毫秒扫描大表。任务按 shard 分配所有权,扩容时迁移 shard。

语义。系统提供至少一次执行,业务以 task_id 幂等。状态条件更新防止两个实例同时完成;失败指数退避,超过阈值转人工。长任务必须续租并限制最大运行时间。

异常。处理时钟漂移、迟到、重复、调度惊群和批量同一时刻到期。仅用进程内 Cron 无法覆盖重启和多实例。

具体示例

具体设定。订单 30 分钟未支付要自动关闭,调度实例可能重启。

落地例子。任务表存task_id, next_trigger_time, owner, lease_expire, status, version。实例领取任务时条件更新 owner 和租约;执行成功后状态改 DONE。实例宕机后租约过期,其他实例可重新领取。关闭订单用task_id/order_id幂等。

面试可讲。分布式调度一般提供至少一次执行,所以业务必须幂等。进程内 Cron 无法覆盖重启、多实例和任务迁移。

42. Redis 分布式锁怎样才相对可靠?何时不该用?

正确姿势。SET key token NX PX ttl;释放用 Lua 校验 token 再删除。长临界区受控续租并设置最大持有时间;获取失败随机退避。业务本身仍需幂等。

Fencing Token。客户端可能因 GC 暂停超过 TTL,醒来后继续写。锁服务每次发放递增 fencing token,下游只接受更大 token,才能拒绝旧持有者。

限制。Redis 主从切换可能在锁未复制时产生双持有者;极强一致使用共识锁服务。账户余额、库存等优先用数据库唯一约束和条件更新,不要把锁当数据正确性的唯一防线。临界区不要包含不可控远程慢调用。

具体示例

具体设定。多实例同时刷新同一个热点商品缓存,只允许一个实例回源 DB。

落地例子。使用SET lock:product:123 token NX PX 3000,释放时 Lua 校验 token 再删。若刷新超过 3 秒,要么受控续租,要么返回旧值,不让旧持有者无限写。对库存扣减这种强正确场景,仍用 DB 条件更新,不把 Redis 锁当唯一防线。

面试可讲。Redis 锁适合降低重复工作,不适合作为资金/库存正确性的唯一保证。要讲 token、TTL、Lua 释放和 fencing token。

43. 分布式 ID 如何唯一、趋势递增并处理时钟回拨?

Snowflake。64 位拆为时间戳、机房/节点、同毫秒序列;节点号通过租约分配,防止重复。计算位数能支撑的年限、节点数和每毫秒吞吐。

回拨。小幅回拨等待;大幅回拨切逻辑时钟、备用节点号或停止生成并告警。必须避免两个进程拿同一节点号。趋势递增不等于严格连续,也不应把连续性作为业务正确性。

号段。需要更强单调性时,数据库一次分配一段,服务本地递增并双缓冲预取;数据库压力是每个号段一次。外部展示 ID 可再编码或加随机扰动,防止暴露业务规模。

具体示例

具体设定。订单 ID 要全局唯一、趋势递增,支持 1000 台机器,每台每毫秒最多 4096 个。

落地例子。Snowflake 拆成时间戳 + 机房/机器号 + 毫秒内序列。机器号由注册中心租约分配,防止两个进程拿同一 worker_id。时钟回拨 5ms 内等待,超过阈值切逻辑时钟或停止发号告警。

面试可讲。趋势递增不是严格连续。ID 方案要回答位数容量、worker_id 分配、时钟回拨和暴露业务规模的问题。

44. 设计大文件/视频上传与异步转码系统。

上传。客户端先申请 upload_id 和分片签名,直接分片上传对象存储;服务端不代理大流量。每片带序号、大小和 checksum,支持并发、断点续传与幂等 Complete。

处理。合并完成写媒体记录和 Outbox,转码任务进入 MQ;Worker 按资源需求调度,输出多码率文件、封面和元数据。任务状态机记录阶段、attempt 和产物版本,失败可从最近阶段重试。

分发。CDN 回源对象存储,播放地址鉴权与防盗链。新旧转码版本用 manifest 原子切换;原文件按合规策略保留或清理。

安全。上传前后做类型、病毒、内容安全和配额检查;用户声明的 MIME 不可信。追问需覆盖超大分片、重复 Complete、转码长尾和热点视频缓存。

具体示例

具体设定。用户上传 2GB 视频,网络中断后要续传,上传完生成 480p/720p/1080p。

落地例子。客户端先申请upload_id和分片签名,直接上传 TOS/S3。每片带part_no和 checksum,Complete 时服务校验所有分片并写media(status=UPLOADED),随后 outbox 发转码任务。转码产物写 manifest,完成后原子切换play_manifest_version

面试可讲。服务端不代理大文件流量,只负责签名、状态和任务编排。转码是长任务,要有阶段状态、重试、产物版本和内容安全检查。

45. 设计每秒百万条的日志采集与检索系统。

容量。先算带宽:1KB/条、100 万条/秒约 1GB/s 原始流量,必须批量压缩、分区和分层存储。检索、告警、审计的时效与保留期不同。

链路。Agent 本地批量、压缩并落盘缓冲;接入层无状态,把日志写持久 MQ;下游分为实时解析告警、近期检索索引、长期对象存储。按服务/租户分区并设置配额,坏格式进入隔离流。

可靠性。批次有 sequence/checksum,Agent 根据 ACK 清本地 WAL;消费者使用 offset 和幂等批次。背压时优先保留审计和错误日志,对 debug 日志采样或丢弃。

成本。在线索引只保留近期高价值字段,原文压缩存冷层;查询旧数据触发异步回载。日志系统自身要限制自日志,避免反馈风暴。

具体示例

具体设定。每条日志平均 1KB,100 万条/s 就是约 1GB/s 原始流量。

落地例子。Agent 本地批量压缩,失败时写 WAL;接入层写 Kafka/Pulsar;下游分三路:实时告警、近 7 天检索索引、长期对象存储。租户配额限制 debug 日志,故障时优先保留 error 和审计日志。

面试可讲。日志系统首先是流量和成本问题。要讲批量、压缩、背压、分层存储、采样和坏日志隔离,而不是只说 ELK。

46. 设计搜索联想/自动补全,要求 50ms 内返回。

数据结构。静态词典可用 Trie/FST;每个前缀节点保存 Top K 候选,避免请求时遍历整个子树。候选分数综合搜索频率、点击率、新鲜度和个性化。

更新。离线构建全量版本,流式统计热词做增量 overlay;新版本在内存中加载完成后原子切换。拼写纠错和同义词归一化在候选召回前完成,但要限制组合爆炸。

部署。数据按语言/前缀分片,热点前缀多副本;服务本地内存查询,远程只取个性化特征并设严格预算。返回前做敏感词、地域和权限过滤。

为什么。50ms 目标决定主路径不能依赖复杂数据库查询。预计算 Top K 是典型用空间换低延迟;追问要讨论版本内存峰值、冷启动和空前缀热点。

具体示例

具体设定。用户输入“bei”,50ms 内返回“北京天气、北京地铁、北京大学”等 10 个候选。

落地例子。离线构建 Trie/FST,每个前缀节点预存 Top 20 候选和分数。热词增量用 overlay 覆盖主词典;服务本地内存查询,个性化特征最多花 10ms,超时就返回非个性化结果。

面试可讲。50ms 决定不能请求时扫数据库或遍历整棵树。候选要预计算,线上只是前缀查找、轻量重排和敏感词过滤。

47. 高风险功能如何做配置中心、灰度发布和安全回滚?

解耦。代码发布、配置开启、数据变更分开。Feature Flag 默认关闭,按内部用户、单机房、1%、5%、20% 放量;稳定用户键分桶,避免来回跳组。

配置。配置带 schema、版本、owner、审批、有效期和变更审计;客户端本地缓存最后正确版本,配置中心故障时使用安全默认。推送通知后客户端仍按版本拉取,防止漏事件。

护栏。每阶段观察错误率、P99、CPU、依赖水位和业务指标;超过阈值自动关开关。数据库采用 expand-contract,先加兼容结构、再切读写、最后删除旧结构。

回滚。回滚代码不等于回滚数据。不可逆写入必须提前设计补偿或双格式兼容。任何自动发布都要有单一总开关和明确操作人。

具体示例

具体设定。新推荐算法可能影响首页点击率,要从内部用户逐步放到全量。

落地例子。Feature Flagnew_feed_rank默认关,按hash(user_id) % 10000稳定分桶:内部用户、1%、5%、20%、50%、100%。每档观察错误率、P99、点击率和投诉;异常自动关总开关。配置带 schema、owner、审批和版本。

面试可讲。发布、配置、数据变更要解耦。回滚代码不等于回滚数据,所以不可逆写入要提前设计兼容和补偿。

48. 核心服务如何做同城双机房或异地多活?

目标。先定义 RTO、RPO、故障域和一致性。多活不仅是服务两份,还需要数据、MQ、配置、密钥、限流和第三方依赖形成完整单元。

架构。无状态服务跨机房部署并就近路由;用户或租户有 home region,写请求回归属单元,减少双写冲突。强一致小数据用共识复制,普通数据主写异步复制,派生索引通过事件同步。

versioned async replication

Global Traffic

Region A Cell

Region B Cell

Service/Cache/DB/MQ

Service/Cache/DB/MQ

Control Plane + Quorum

切换。多信号故障检测 + 防抖,分阶段切流;使用仲裁和 fencing 防脑裂。切流前确认目标端数据水位,恢复后限速回切。定期做断网、依赖失败和整机房演练,验证真实 RTO/RPO。

追问。DNS 缓存和长连接使切流不瞬时;异步复制意味着 RPO 非零。不要用“加一个异地机房”掩盖数据冲突。

具体示例

具体设定。订单服务要求 RTO 5 分钟、RPO 30 秒,用户按归属地分配 home region。

落地例子。用户uid=888归属 Region A,写请求路由回 A,Region B 有异步复制副本。A 故障时,控制面确认复制水位后把这批用户切到 B,并 fencing 旧 A,避免恢复瞬间双写。派生索引用事件在两地同步。

面试可讲。多活不是服务部署两份,还包括 DB、MQ、缓存、配置、限流和第三方依赖。异步复制 RPO 非零,要明确故障切换时可能丢多少数据。

49. RPC 超时后该不该重试?如何避免重试风暴?

分类。只对幂等读或带幂等键的写重试;参数错误、权限错误和明确限流通常不重试。超时状态未知的写操作先查业务结果。

策略。有限次数、指数退避 + jitter;每次尝试受端到端 Deadline 约束。定义重试预算,例如额外流量不超过正常流量 5%;只在一层统一重试,避免三层各重试三次产生 27 倍放大。

保护。下游错误持续升高时熔断,半开小流量探测;并发隔离、有界队列和取消传播避免过期请求继续占资源。服务端读取 Deadline,来不及完成就尽早拒绝。

为什么。重试只适合瞬时故障,不是过载恢复方案。没有预算和退避的重试会让系统在最脆弱时承受更多流量。

具体示例

具体设定。A 调 B 超时 200ms,B 实际已经处理成功但响应丢了;上层网关、A、SDK 都配置 3 次重试。

落地例子。写接口必须带request_id,超时后先查业务结果或用同一幂等键重试。全链路只允许一层重试,总 deadline 500ms,最多 2 次,指数退避加 jitter,并设置重试预算,例如额外流量不超过 5%。

面试可讲。重试适合瞬时故障,不适合下游过载。没有预算和退避时,三层各重试三次会把流量放大到 27 倍。

50. 线上大面积报错:Oncall 如何止血、定位并复盘?

前 5 分钟。确认用户影响、错误率、地域和开始时间,宣布事故等级,指定指挥、操作和记录角色。检查最近发布、配置与依赖变更,建立共享时间线。

先止血。优先执行可逆动作:关开关、回滚、限流、降级、切流、摘除异常实例。每个操作记录时间、预期、结果和回退方式。不能为了找根因而让用户继续受损。

定位。从 RED 指标与 Trace 找故障 Span,再检查线程池、连接池、GC、网络、DB、Redis 和下游。按版本、机房、实例、调用方、参数对比。保留日志、慢查询和核心 dump,避免反复重启破坏现场。

复盘。重建因果链与防线失效链:为什么发生、为什么未被阻止、为什么未更早发现、为什么恢复慢。行动项必须有 owner、截止时间和验证方式,例如自动降级、容量测试、依赖隔离与演练;避免只写“加强监控”。

具体示例

具体设定。22:05 开始支付成功率从 99.9% 掉到 80%,影响全部用户。

落地例子。前 5 分钟先定级、建时间线、查最近发布、配置和依赖变更;如果 22:00 刚发版支付服务,先灰度回滚或关新开关止血。定位时按版本、机房、错误码、下游 Span 对比,保留日志和慢查询。复盘行动项写 owner 和截止时间,例如“支付依赖超时自动降级,7 月 20 日前完成演练”。

面试可讲。Oncall 答案要先止血,再定位,再复盘。不要为了找根因让用户继续受损;也不要复盘只写“加强监控”,要有可验证的防线改进。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询