Skip to content

模块导航: 总入口 · 总纲与方法 · 后端场景 · AI与算法工程 · 国企与金融 · 网络问答


148. 1GB 视频上传失败要重传,体验太差(分片上传) ​

【考察内容】文件上传是通用高频设计题

【题目】用户上传 1GB 视频,传了 80% 网络断了,全部重来,用户骂街。怎么设计大文件上传:分片上传、断点续传、秒传(服务端已有相同文件直接跳过)、并发分片与校验?

【参考答案】

  1. 秒传:上传前客户端计算文件 MD5 → 服务端查重(已存在则直接返回“上传完成”,引用已有文件)——省流量;
  2. 分片上传:文件切成固定大小分片(如 5MB/片),客户端逐个上传(并发 3~5 片),服务端存分片(临时目录/OSS 分片)并记录每片状态;
  3. 断点续传:客户端查询已上传分片列表(uploadId 维度),只重传缺失分片;
  4. 合并:全部分片上传完成后,服务端发起合并(OSS CompleteMultipartUpload/自研合并),校验总大小与 MD5;
  5. 流程与状态:创建上传任务(返回 uploadId+分片数)→ 上传分片(每片带 uploadId+index,幂等:同 index 覆盖)→ 完成合并 → 文件入库(file_id、地址、大小);
  6. 存储:对象存储(OSS/S3)天然支持分片;权限(上传凭证:STS 临时凭证,防越权);
  7. 下载优化:CDN 加速、Range 请求(断点续传下载)、大文件流式返回;
  8. 清理:超时未完成的分片定时清理(防存储浪费);
  9. 容量估算:1GB / 5MB ≈ 200 片,并发 3~5 片;日上传假设 10 万文件×均 200MB → OSS 约 20TB/天;秒传命中率按 20% 估可省约 4TB 带宽与存储;
  10. 失败与降级:单片失败指数退避重试 3 次,仍失败则整任务标失败可续传;合并失败可对同一 uploadId 重试 Complete;分片元数据服务不可用时提示稍后续传,OSS 侧要自己兜:未 Complete 也未 Abort 的分片不会自动过期,会一直占存储并计费,只能靠生命周期规则或 AbortMultipartUpload 清理——不能指望「24 小时后系统自动回收」。

【原理溯源】

  • 为什么要分片? 大文件一次传输失败要全部重来,且无法并发。分片后单片小、可并发、失败只重传该片。这是把大任务拆成可重试的小任务,同分布式任务思想。
  • 为什么要有 uploadId? 标识一次上传会话,关联所有分片状态。客户端凭 uploadId 查询已传分片,实现断点续传;服务端凭 uploadId 管理临时分片与超时清理。
  • 为什么分片要幂等(同 index 覆盖)? 网络重试可能重复上传同片。同 index 直接覆盖,简单幂等,无需复杂去重。
  • 秒传的 MD5 为什么可行? 同一文件内容 MD5 相同。服务端维护“MD5→文件”映射,命中则无需真传,只建引用。省带宽与存储(去重)。代价是客户端要先算 MD5(大文件耗时),且不同用户共享同一物理文件需权限隔离。
  • 为什么要 STS 临时凭证? 直接给 AK/SK 危险。STS 是限定时间、限定 bucket/路径的临时凭证,客户端直传 OSS,服务端不承载文件流量。这是最小权限原则。

【选型判断树】

text
文件大小?
├─ 小文件(< 10MB)→ 直传
└─ 大文件 → 分片上传
    ├─ 秒传:先算 MD5 查重
    ├─ 断点续传:uploadId 查已传分片
    └─ 合并:CompleteMultipartUpload + MD5 校验

存储:OSS 分片 + STS 临时凭证
清理:超时未完成分片定时删

判断口诀: 小文件直传,大文件分片;MD5 秒传,uploadId 续传。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“大文件上传核心是分片+断点+秒传”
0:30–1:30分片切片、并发、uploadId
1:30–2:30断点续传查已传分片,只传缺失
2:30–3:30秒传MD5 查重,引用已有
3:30–4:30合并与安全合并校验;STS 临时凭证
4:30–5:00收尾“一句话:拆小可重试,MD5 可去重,uploadId 可续传”

【关键数字】

参数经验值说明
分片大小1~10MB平衡并发与请求开销
并发分片3~5带宽利用
秒传MD5 查重省流量
STS 有效期分钟~小时最小权限
分片清理需生命周期规则或 Abort(OSS 不会自动过期)否则碎片持续计费
1GB 分片数~200 片(5MB)续传粒度
日写入量(例)20TB(10 万×200MB)OSS 容量

【追问链】(三层)

L1|“并发上传分片乱序怎么办?” → 分片按 index 存储,合并时按 index 顺序组装,与上传顺序无关。

L2|“MD5 算大文件很慢怎么办?” → 可先算抽样哈希(首尾片段)做预筛,但抽样值不唯一,命中后服务端必须二次校验(首尾分片复核或全量 CRC64/SHA1)才允许秒传——否则同大小的不同文件会命中同一抽样值,把别人的物理文件引用发给当前用户;若改用分片级 MD5 拼接,注意它与整体 MD5 不是同一个值(对象存储的 multipart ETag 就是各分片 MD5 再拼接取 MD5+“-片数”),秒传索引必须按同一算法、同一分片大小建,不能与整体 MD5 混用;也可边传边算。秒传收益要覆盖计算成本,冷门文件可跳过预计算。

L3|“用户传了 99% 断网,再进来怎么续?” → 携带 uploadId 查询已传分片列表,只重传缺失片;进度条准确显示。

【评分标准】

档位答案特征
60 分知道要分片
80 分分片+断点续传+uploadId
95 分秒传 MD5、STS 安全、合并校验、超时清理、与 144 题短视频联动

【关联题】

  • 上游: 第 144 题(短视频)——上传是其第一步
  • 同构: 第 135 题(购物车)——用户状态管理
  • 安全: 第 154 题(权限)——STS 最小权限

【自测】

  1. 判断对错:大文件应该一次传完,简单可靠。 参考答案: 失败重传成本高;必须分片。
  2. uploadId 的作用? 参考答案: 标识上传会话,管理分片状态,支撑断点续传。
  3. 秒传原理? 参考答案: 文件 MD5 查重,命中则引用已有文件,无需真传。

149. 连续签到/补签卡/签到日历,怎么实现(签到系统) ​

【考察内容】签到是 BitMap 经典应用(Redis 场景题高频)

【题目】App 要做签到:每天签到领积分、连续签到 7 天额外奖励、支持补签卡、展示当月签到日历。连续天数怎么算(跨月/断签)?高并发(0 点大量签到)怎么扛?存储用什么?

【参考答案】

  1. 数据结构:Redis BitMap(key=sign:{userId}:{month},bit 位=日期,1=已签到)——内存极小(一个用户一月才 4 字节)、支持位运算统计(连续签到天数计算):
    • 签到:SETBIT(key, day-1, 1)(防重复:SETBIT 的返回值就是该位旧值,返回 1 即当日已签,一条命令原子判重;先 GETBIT 再 SETBIT 是 check-then-act,0 点并发/重试会重复);
    • 本月签到天数:BITCOUNT;
    • 连续签到:从今天往前位运算(BITFIELD 取一段,从「今天对应的 bit 位(day-1)」向低位数连续 1 的个数);
  2. DB 落库:签到记录表(user_id、date、唯一索引 user_id+date 防重复签到)——对账/历史查询;
  3. 奖励:连续签到 N 天发奖励(积分/优惠券)——签到事务后发奖励(MQ 异步+幂等:签到记录唯一);
  4. 时区/日期:按服务端日期计算(跨天:凌晨 0 点重置),避免客户端本地时间作弊;
  5. 补签卡:补签=SETBIT 补位+消耗补签卡(事务);
  6. 扩展:签到日历查询(BITFIELD 取出当月 bit 转前端日历)、签到提醒(定时任务/推送)、活动(翻倍签到);
  7. 容量估算:假设 DAU 1000 万,0 点前后 1 分钟集中签到,峰值 QPS 可达 5~10 万;BitMap 一人一月约 4 字节,但每用户一个 key 时 key 自身的结构开销(robj+SDS+dictEntry+过期项,约 60–80B)远大于 4B 值:1000 万 key ≈ 0.6–0.8GB,不是 40MB。要省内存须把多用户挤进同一 key(offset=userId×31,1e6 用户/key → 约 4MB/key、全量约 40MB),此时压力才主要在网卡与连接——省内存的代价是大 key 风险,要不要合并得按运维口径定:4MB 级 key 在 Cluster 迁移(按 slot 搬迁会长时间阻塞)、删除(必须 UNLINK/分批)、主从全量重传上都是隐患,12 个月历史就是约 120 个这种 key(每月 1e7÷1e6=10 个 × 12 个月);内存确实吃紧且能接受这些运维动作被拖慢才选合并,内存不紧张就保持「一人一月一 key」(0.6–0.8GB)。这笔取舍要在答题时说明,别默认「极轻」;0 点峰值怎么扛:入口令牌桶限流排队+客户端「今日已签」标记挡重复提交+SETBIT 用 pipeline 批量合并写;跨月连续天数:取上月尾部与本月头部的位串拼接,或直接维护「当前连续天数」计数器(断签归零、用补签卡改写那一天);
  8. 失败与降级:Redis 不可用时降级写 DB(唯一索引防重),接口限流排队;奖励 MQ 丢失靠「签到记录 vs 已发奖励」对账补发;客户端时间仅作展示,判定一律走服务端时钟。

【原理溯源】

  • 为什么用 BitMap 而不是 String/Set? 签到是“用户×日期”的布尔状态,BitMap 用 1 bit 表示一天,一月仅 4 字节。String 存日期列表浪费空间;Set 元素开销大。BitMap 还支持位运算(BITCOUNT、连续 1 统计),天然匹配“连续签到”计算。
  • 为什么连续签到要用位运算而不是循环查? 从今天往前逐天 GETBIT 是 O(N) 且多次网络往返。BITFIELD 一次取出当月 bit,在应用层从今日位(day-1)向低位数连续 1 的个数,O(字长)。这是用位运算把多次查询变成一次。
  • 为什么要服务端日期? 客户端可改本地时间作弊签到。以服务端时钟为准,跨天用服务器 0 点(或业务时区)重置。
  • 为什么奖励要 MQ 异步+幂等? 签到高并发(0 点),发积分/券若同步会拖慢签到接口。异步发放,用签到记录唯一索引保证幂等(同一天只发一次)。
  • 为什么按月 key? 一月 31 bit 刚好;跨月自然滚动新 key;历史月可归档。避免单 key 无限增长。

【选型判断树】

text
存储:Redis BitMap(sign:{uid}:{month})
签到:SETBIT + 防重复判断
连续:BITFIELD 取位,找连续 1
奖励:MQ 异步 + 唯一索引幂等
日期:服务端时钟
补签:SETBIT + 消耗卡(事务)

判断口诀: BitMap 省空间,位运算算连续,服务端日期防作弊。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“签到是 BitMap 经典场景,核心是空间与位运算”
0:30–1:30数据结构BitMap 按月 key;SETBIT/BITCOUNT
1:30–2:30连续计算BITFIELD+找连续 1;跨月
2:30–3:30防重与奖励唯一索引;MQ 异步幂等
3:30–4:30补签与日历补签事务;日历查询
4:30–5:00收尾“一句话:1 bit 一天,位运算算连续,服务端日期防作弊”

【关键数字】

参数经验值说明
一月内存~4 字节/用户BitMap
连续奖励7 天常见活动
补签卡有限次活动道具
时区服务端业务时区防作弊
奖励幂等(user_id, date) 唯一防重复发
0 点峰值 QPS5~10 万(DAU 千万)集中触发
BitMap 内存每用户一 key:1000 万 key ≈ 0.6–0.8GB(key 结构开销 60–80B 远大于 4B 值);合并成少量大 key:≈40MB(约 4MB/key × 10)「极轻」只在合并口径成立,代价是大 key 的迁移/删除/阻塞风险

【追问链】(三层)

L1|“BitMap 跨年怎么办?” → 按月 key,自然滚动;历史月归档或删除。

L2|“连续签到跨月怎么算?” → 取上月末尾几天与本月开头,拼接位串再算连续;或维护「当前连续天数」计数器,断签/补签时同步修正,避免每次查询都跨 key 拼接。

L3|“0 点大量签到打挂 Redis 怎么办?” → 本地缓存当日已签标记;批量/合并写;限流排队;BitMap 本身极轻只在「多用户合并进少量大 key」的口径下成立(约 40MB);按主方案每用户一 key 是 0.6–0.8GB(key 结构开销 60–80B)。合并虽省内存,但 4MB 级大 key 在迁移/删除/主从重传上都是隐患,这笔取舍要先表态,再说瓶颈通常在网络与连接。

【评分标准】

档位答案特征
60 分知道用 Redis 存签到
80 分BitMap、连续位运算、唯一索引
95 分跨月拼接、服务端日期、补签事务、MQ 奖励幂等、日历查询

【关联题】

  • 同构: 第 124 题(点赞计数)、第 162 题(计数)
  • 下游: 第 159 题(积分)——签到是积分的输入源(159 侧已把本题列为它的上游,方向以这里为准)
  • 存储: Redis BitMap 章节

【自测】

  1. 判断对错:签到可以用 String 存日期列表。 参考答案: 浪费空间且难算连续;应用 BitMap。
  2. 连续签到怎么高效计算? 参考答案: BITFIELD 取当月 bit,从今日位(day-1)向低位数连续 1 的个数。
  3. 为什么用服务端日期? 参考答案: 防客户端改时间作弊。

150. 大转盘抽奖:概率/防超发/防作弊,怎么设计(抽奖系统) ​

【考察内容】概率与并发控制设计

【题目】运营要做大转盘抽奖:不同奖品概率不同、总奖品数量有限(大奖可能只有 1 个)、要防并发超发、防用户刷次数。概率怎么配置?中奖判定放哪层?库存扣减怎么保证不超发?

【参考答案】

  1. 核心:概率控制
    • 固定概率:每个奖品一个概率权重,随机数落在哪个区间中哪个奖;
    • 总量控制:每个奖品有库存,抽中时扣库存(Redis DECR/DB 条件更新),扣到 0 则改中其他奖品(按剩余权重重算)或返回未中;中奖判定放哪层:概率抽取+库存扣减+保底判断全在抽奖接口的服务端完成(尽量收进一次 Lua),前端只拿结果展示——不下发概率、不接受前端上报中奖,否则改一次请求就能白拿大奖;
  2. 防超发:库存 Redis 预扣(原子 DECR,小于 0 拒绝)→ 中奖记录落库(唯一索引 user_id+活动+轮次防重复参与)→ 对账;
  3. 防作弊:登录态校验、频控(每人每天次数 Redis 计数)、参与前验证(邀请/消费条件)、设备指纹风控;
  4. 并发:抽奖请求 Redis 扣库存+Lua 原子(判断库存→扣减→返回结果),MQ 异步发放奖品(发券/发货,幂等);
  5. 中奖概率动态:概率均分/保底机制(抽 N 次必中)、库存越少概率越低(按剩余库存调整权重);
  6. 审计:抽奖记录(谁、何时、中什么)留痕,活动结束后可对账复盘;
  7. 容量估算:活动峰值假设 10 万 QPS 抽奖;Redis Lua 单实例约 10 万 ops,需 Cluster;但「按活动/奖品随意分 key」会让 Lua 跨 slot 直接报 CROSSSLOT:Redis Cluster 要求一个脚本内的所有 key 落同一 slot,而一次抽奖要同时动「奖品库存+用户次数+保底计数+中奖记录」多个 key,主招就失效了。要么用 hash tag 把同一活动的 key 绑到同一 slot(act:{1001}:stock、act:{1001}:times,分片粒度按活动而非奖品),要么改成「单 key Lua+应用层重算」或「多次单 key 原子操作+补偿」;奖品库存与次数计数内存可忽略,瓶颈在单活动热点 key;但不能按奖品把 key 拆到不同 slot(多 key Lua 会直接撞 CROSSSLOT),只能在同 slot 内做子 key 分片求和,或改成单 key Lua+应用层重算;
  8. 失败与降级:Redis 故障时活动页降级为「排队/稍后再试」,禁止穿透 DB 抽奖;发放 MQ 失败重试+死信人工;日终对账发现超发则冻结异常中奖并补偿运营。

【原理溯源】

  • 为什么概率判定不能只靠前端或纯随机? 前端可被改;纯随机不控总量会超发。必须服务端:先随机算出目标奖品,再原子扣库存;扣失败则重算或未中。概率与库存解耦再耦合——概率决定“想给谁”,库存决定“能不能给”。
  • 为什么要 Lua 原子? “判断库存→扣减→返回结果”多步操作在并发下会超发。Lua 脚本在 Redis 单线程内原子执行,保证不超发。
  • 为什么要保底机制? 纯概率可能大量用户不中,体验差、活动失败。保底(抽 N 次必中)用确定性换取用户满意度,需消耗保底奖品库存。
  • 为什么要防刷? 脚本可无限抽,耗尽库存或薅羊毛。频控(次数上限)+设备指纹+行为风控是标配。
  • 为什么奖品发放要异步? 发货/发券可能慢或失败。MQ 异步+幂等(中奖记录唯一),失败重试,保证“中了必发到”。

【选型判断树】

text
抽奖:
随机权重选奖品 → Lua 原子扣库存(Cluster 下多 key 脚本要用 hash tag 绑到同一 slot,否则 CROSSSLOT 直接报错) → 成功则记录+异步发放
扣失败 → 重算其他奖/未中

防超发:Redis DECR + DB 兜底 + 对账
防刷:频控 + 设备指纹 + 风控
保底:次数计数达阈值强制中

判断口诀: 权重选奖、原子扣库存、异步发放、保底救体验。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“抽奖是概率+库存+防刷三合一”
0:30–1:30概率与库存权重随机;总量控制
1:30–2:30并发Lua 原子扣;防超发
2:30–3:30防刷与保底频控;N 次必中
3:30–4:30发放与审计MQ 异步幂等;留痕
4:30–5:00收尾“一句话:概率决定给不给,库存决定能不能给”

【关键数字】

参数经验值说明
库存扣减Redis 原子防超发
频控每人每日 N 次防刷
保底抽 10 次必中体验
发放MQ 异步可靠
对账Redis vs DB兜底
抽奖峰值约 10 万 QPS活动开抢瞬间
保底计数Redis INCR达阈值强制中

【追问链】(三层)

L1|“先查库存再扣可以吗?” → 不行,非原子会超发;必须 Lua 原子或条件更新——Cluster 里一次抽奖要动库存/次数/保底/记录多个 key,脚本内所有 key 必须同 slot(act:{aid}:*),否则主招失效,只能改「单 key Lua+应用层重算」或多次单 key 原子+补偿。

L2|“保底怎么实现?” → 参与次数计数(Redis INCR),达阈值强制中奖,消耗保底库存;次数与「是否已中过大奖」联合判断,防止保底逻辑被用来无限薅指定奖;发放仍走同一异步管道。

L3|“大奖只有 1 个,怎么保证不超发?” → 原子扣减到 0 即止;后续请求按剩余权重重算或未中;对账兜底。

【评分标准】

档位答案特征
60 分知道要控概率和库存
80 分权重随机+原子扣+防刷
95 分保底机制、异步发放、对账、与 134/156/157 同构

【关联题】

【自测】

  1. 判断对错:抽奖概率可以只在前端控制。 参考答案: 前端可篡改;必须服务端判定+原子扣库存。
  2. 为什么用 Lua? 参考答案: 判断+扣减原子,防并发超发。
  3. 保底机制解决什么? 参考答案: 纯概率用户体验差;保底保证活跃用户必中。

151. 直播间 10 万人同时发弹幕,怎么实时展示(弹幕系统) ​

【考察内容】高并发实时消息架构

【题目】直播间 10 万人在线,弹幕峰值每秒几万条,要求实时上屏、不能卡顿、不能丢太多。弹幕通道怎么建(长连接/WebSocket)?广播(群发)怎么做?弹幕是否需要存储、存多久?

【参考答案】

  1. 链路:用户发弹幕 → 弹幕服务(校验+过滤)→ 广播到该直播间所有连接 → 客户端展示;
  2. 连接:WebSocket 长连接(客户端接入弹幕网关集群,按直播间路由);
  3. 广播方案:
    • Redis Pub/Sub:弹幕服务 publish 到直播间频道,弹幕网关订阅后推给本机连接——简单,但 Redis 单点消息能力有限(高并发下需分片);
    • Kafka:弹幕写入 topic(按直播间分区),网关消费后广播——可削峰、可回放,大直播主流;
    • 自研长连接网关:内存路由表(直播间→连接列表),直接内存广播(最快);
  4. 体验优化:弹幕限频+合并(同用户 1 秒最多 1 条;超高频弹幕抽样展示)、弹幕生命周期(窗口内展示后即从热存储丢弃;要留痕就异步落对象存储/归档,不进主链路,或只存抽样);
  5. 保障:弹幕丢失可容忍(不落库或异步落库);网关扩容(按直播间路由保证连接分布);全站弹幕洪峰(主播 PK 等)用限流+降级(只展示 VIP/部分弹幕);
  6. 扩展:弹幕审核(敏感词实时过滤)、礼物特效(高优消息通道);
  7. 容量估算:10 万人房间、弹幕峰值 3 万/s;若全量广播,fan-out 理论上可达 3 万×观看连接数,必须靠「限频+合并+抽样」把上屏量压到 UI 可承受(「几十条」要落成硬指标,不能停在形容词:设每连接上屏 R 条/s、消息体约 100B,则单机下行带宽=该机连接数×R×100B;10 万人分摊到 N=4 个网关时每机 2.5 万连接×30 条/s≈75MB/s≈600Mbps,千兆网卡≈125MB/s,按 80% 水位算每连接的额度=100MB/s÷2.5 万÷100B≈40 条/s(上面那个 30 条/s=600Mbps 本就在千兆网卡水位之内)——所以 R、N、网卡三者要一起给数并互相校验,否则「抽样」只是嘴上降级);Kafka 按房间分区时单房间恒落 1 个分区=1 个消费者,32~64 个分区只隔离房间之间、不给这个 10 万人房间提速;大房间要房间内二次散列(roomId+hash(uid)%N)或多分区扇出。同理「同房间连接落同一网关分片」与「网关按连接数水平扩」互斥,大房间须跨网关节点广播;
  8. 失败与降级:网关节点挂→连接按房间哈希漂移重连;Kafka 延迟升高→降级为只推高优礼物+合并弹幕;审核服务超时→临时只展示白名单/低风险词弹幕,宁可少不可违规。

【原理溯源】

  • 为什么弹幕和 IM 架构取向不同? IM 要求必达、有序、存储;弹幕高吞吐、可丢失、无需持久化。弹幕可以抽样展示、合并、丢弃,用“最终大部分用户看到大部分弹幕”换吞吐。这是产品语义决定架构取舍。
  • 为什么大直播用 Kafka 而不是 Pub/Sub? Redis Pub/Sub 无持久化、单点能力有限;Kafka 分区可水平扩展、可削峰、可回放(录播弹幕)。10 万人直播 Kafka 更稳。
  • 为什么要限频+合并? 防止刷屏与洪峰。同用户 1 秒 1 条;超高频时抽样/合并展示,保证 UI 不卡。这是用产品降级换系统稳定。
  • 为什么按直播间路由? 同一直播间的连接应落在同一网关分片,便于本机连接列表直接推(内存广播)。但这条与「网关按连接数水平扩」是互斥的:10 万人房间全落一个网关分片=单机连接数与带宽先被打满,所以大房间要按 roomId + hash(uid)%N 把同一房间的连接再散到 N 个分片,推送时跨节点扇出(Redis Pub/Sub 或网关间 RPC 广播)。口径分两档:小房间按房间集中=本地广播最省;大房间二次散列=跨节点扇出换扩展性。负载均衡时按房间一致性哈希只对前者成立。
  • 为什么要敏感词实时过滤? 合规要求。在弹幕服务入口同步过滤,延迟毫秒级;也可异步复审+撤回。

【选型判断树】

text
| 通道:WebSocket 长连接;小房间按直播间路由,大房间 roomId+hash(uid)%N 二次散列
广播:
├─ 小直播 → Redis Pub/Sub
└─ 大直播 → Kafka 分区
存储:可不落库或抽样;窗口内丢弃
治理:限频、合并、抽样、限流降级

判断口诀: 长连接接入,Kafka 削峰,限频合并,可丢失换吞吐。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“弹幕是高吞吐可丢失的实时广播”
0:30–1:30接入WebSocket 集群;小房间按房间路由、大房间房间内二次散列跨节点扇出
1:30–2:30广播Pub/Sub vs Kafka
2:30–3:30体验限频合并抽样;不卡 UI
3:30–4:30存储与治理可不落库;洪峰降级
4:30–5:00收尾“一句话:可丢失、可抽样、可合并,换十万并发”

【关键数字】

参数经验值说明
在线10 万单房间
弹幕峰值数万/秒需削峰
限频1 条/秒/人防刷屏
存储不落库或抽样可容忍丢
路由小房间按直播间哈希;大房间 roomId+hash(uid)%N 二次散列本地广播/跨节点扇出
Kafka 分区32~64(隔离房间之间)单房间恒落 1 分区=1 消费者,不给大房间提速;大房间靠房间内二次散列/多分区扇出

【追问链】(三层)

L1|“弹幕和 IM 消息区别?” → 弹幕高吞吐可丢、无需必达;IM 低吞吐必达、要存储。架构取向相反。

L2|“弹幕全落库行不行?” → 写入扛不住:3 万/s 持久化会打爆存储与主键索引;应不落库,或只抽样/高优礼物落库供回放。若合规要求留痕,异步进对象存储/冷存,不挡上屏链路。

L3|“主播 PK 全站弹幕洪峰怎么办?” → 限流+降级(只展示部分/VIP);Kafka 削峰;网关扩容。

【评分标准】

档位答案特征
60 分知道用 WebSocket
80 分广播方案、限频合并
95 分与 IM 对比、按房间路由(大房间须散列)、洪峰降级、敏感词、礼物高优通道

【关联题】

  • 对比: 第 129 题(IM)——必达 vs 可丢
  • 同构: 第 128 题(Feed 推模式)——同为大扇出广播;第 144 题是短视频(上传/转码/CDN 文件分发),机制不同
  • 上游: 第 128 题(Feed)——另一种推送

【自测】

  1. 判断对错:弹幕必须全部落库保证不丢。 参考答案: 可丢失;落库扛不住,抽样或不落。
  2. 为什么按直播间路由? 参考答案: 同房间连接集中便于本机内存广播(小房间适用);大房间 10 万人会让单机连接与带宽先打满,须 roomId+hash(uid)%N 二次散列、推送时跨节点扇出——与正文第 7 条口径一致。
  3. 大直播为什么用 Kafka? 参考答案: 分区扩展、削峰、可回放;Pub/Sub 能力有限。

152. 发帖/回答/点赞/热榜,社区系统全貌(社区系统) ​

【考察内容】内容社区综合设计

【题目】要做问答社区:用户发帖提问、回答问题、给回答点赞、关注话题、首页信息流与热榜。内容审核(敏感词)怎么做?热榜怎么算?海量内容的分页与检索怎么做?

【参考答案】

  1. 内容模型:帖子/问题表、回答表(问题下多条回答)、评论表(见评论系统);内容字段(正文、图片);
  2. 核心功能架构:
    • 发帖/回答:内容校验(敏感词)→ 落库 → 索引(ES 全文检索)→ 通知关注者(MQ);
    • 内容流(热榜/推荐):热度分=点赞×权重+评论×权重+时间衰减 → ZSet 存热榜 → 缓存榜单;
    • 关注话题流:关注话题的帖子 Feed(推拉结合,见 Feed 流);
    • 回答排序:按投票/时间/采纳(最佳答案置顶);
  3. 存储:内容表分表(按内容 ID)、正文大字段拆分(正文表/对象存储)、ES 建搜索索引;
  4. 高并发读:热帖详情缓存(Redis 整页缓存/JSON)、列表游标分页;
  5. 治理:审核(先审后发/先发后审)、举报处理、反垃圾(重复内容检测);
  6. 扩展:专栏、付费、私信(IM 能力);
  7. 容量估算:日发帖 10 万、回答 50 万、点赞 500 万;热榜每分钟重算一次 ZSet,单次扫描热池即可;ES 假设千万级文档,写入用 bulk,检索 P99 目标 < 200ms;详情读写比约 100:1,详情页强缓存;
  8. 失败与降级:ES 故障时搜索降级为「标签/最新列表」;热榜计算失败沿用上一期缓存并标延迟;审核服务不可用则 UGC 转先审后发或仅允许关注流,避免违规内容全站分发。

【原理溯源】

  • 为什么要拆正文大字段? 内容表若含长正文,行变大、索引变重、缓存浪费。拆出正文表或对象存储,列表页只查元数据,详情页才取正文。这是冷热字段分离。
  • 为什么热榜要时间衰减? 同 127 题:不衰减则历史内容永久霸榜,新内容无法冒头。
  • 为什么发帖要通知关注者? 关注关系驱动分发。发帖事件 MQ 推给粉丝(推拉结合),是社区活跃的关键回路。
  • 为什么要游标分页? 内容列表无限流,深分页慢。游标(最后一条 ID/时间)每次取 N 条,与页深无关。
  • 为什么要先审后发/先发后审? 合规与体验的权衡:高危场景(时政)先审后发;UGC 通常先发后审+机审过滤+举报兜底。

【选型判断树】

text
内容:元数据表 + 正文分离
检索:ES 倒排
热榜:ZSet + 时间衰减 + 缓存
Feed:推拉结合(见 128)
分页:游标
审核:机审+人审;先发后审或先审后发

判断口诀: 冷热分离、ES 检索、衰减热榜、游标分页、审核兜底。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“社区是内容模型+分发+治理的综合题”
0:30–1:30内容模型问题/回答/评论;正文分离
1:30–2:30分发热榜衰减;Feed 推拉;通知
2:30–3:30检索与分页ES;游标
3:30–4:30治理审核、举报、反垃圾
4:30–5:00收尾“一句话:模型支撑存储,衰减支撑热榜,审核支撑合规”

【关键数字】

参数经验值说明
正文拆分> 1KB 外置冷热分离
热榜 TTL10s~1min缓存
分页游标避免深分页
审核机审同步+人审异步平衡
去重编辑距离/指纹反垃圾
日发帖/回答10 万 / 50 万量级假设
点赞500 万/日热榜输入

【追问链】(三层)

L1|“帖子删除后缓存/搜索/热榜怎么同步?” → 删除事件 MQ 驱动清理缓存+ES+榜单;或读时状态过滤。

L2|“热榜无时间衰减会怎样?” → 老内容霸榜,新内容无法冒头,榜单失去分发价值;必须衰减(如 score × e^(-λt))或按时间窗重算。

L3|“海量内容怎么防重复灌水?” → 内容指纹(SimHash)、相似度检测、频控、举报。

【评分标准】

档位答案特征
60 分知道要发帖点赞评论
80 分内容模型、热榜、ES、游标
95 分正文分离、删除事件联动、审核策略、反垃圾、与 124/125/127/128 综合

【关联题】

【自测】

  1. 判断对错:帖子正文应存在内容主表。 参考答案: 大字段拖慢查询;应冷热分离。
  2. 热榜为什么要衰减? 参考答案: 防旧内容霸榜,让新内容有机会。
  3. 深分页怎么优化? 参考答案: 游标分页(last_id/时间戳)。

153. 30 分钟后执行/定时执行/失败重试,统一延迟任务平台(延迟任务系统) ​

【考察内容】任务调度平台设计

【题目】多个业务都要“延迟执行”:订单 30 分钟关单、红包 24 小时退回、任务定时重试。想做一个通用延迟任务平台(不绑死某个 MQ)。时间轮、Redis 有序集合、MQ 延迟消息、DB 轮询几种实现怎么选?任务持久化与失败重试怎么做?

【参考答案】

  1. 核心能力:任务注册(延迟时间/执行内容)、到点触发、失败重试、执行记录;
  2. 实现方案选型:
    • 时间轮(Netty HashedWheelTimer):内存级、毫秒精度,适合进程内短延迟任务;不支持持久化(重启丢失);
    • Redis ZSet:score=执行时间戳,扫描到点任务执行(见延时任务题);支持持久化、简单;
    • RabbitMQ 延迟队列/RocketMQ 延迟消息:可靠、有死信;
    • 数据库轮询(Quartz/XXL-JOB):任务表+定时扫描,支持持久化/分布式调度/失败重试记录;
  3. 平台化设计:
    • 任务表(task_id、执行时间、状态:待执行/执行中/成功/失败、重试次数、业务参数);
    • 分级存储:秒级任务用内存时间轮/Redis,分钟级以上落库扫描;
    • 触发:执行器(Worker 集群,分布式锁防重复执行,分片处理);
    • 重试:指数退避、最大次数、死信(人工处理);
    • 监控:任务成功率、积压、超时;
  4. 关键点:可靠性(任务持久化+执行记录+对账)、精准性(时间轮毫秒 vs 扫表分钟级,按 SLA 选)、幂等(执行器幂等,防重复触发);
  5. 容量估算:日任务假设 500 万,约 80% 为订单超时;均值到期约 58/s,整点可放大 10 倍至 500+/s;任务表按天/状态分片,Worker 按 task_id 哈希;
  6. 失败与降级(先分清哪一档能切,否则会切到空集合):按第 3 条分级存储,秒级任务在内存时间轮/Redis ZSet,分钟级以上只落库——DB 扫描挂时 Redis 里根本没有这批分钟级任务,直接「切 Redis ZSet」会让订单关单、红包退回全丢。所以:① 秒级档照常走 Redis;② 分钟级档改为延后扫描+积压告警+恢复后按「到期未执行」批量补偿,只有平时就双写(落库同时 ZADD,且仅对内存有余量的短任务)才允许热切 Redis;Worker 全挂则恢复后按「到期未执行」批量补偿;执行中状态超时回滚,避免卡死在执行中。

【原理溯源】

  • 为什么要分级存储? 秒级任务扫表精度不够且压力大;分钟级以上用时间轮内存又怕重启丢。分级:秒级 Redis/时间轮,分钟级 DB 扫描,各取所长。
  • 为什么执行器要幂等? 分布式调度可能重复触发(锁超时、重试)。任务本身必须幂等(业务唯一键),否则重复执行会出错(如重复关单、重复退款)。
  • 为什么用分布式锁防重复执行? 多 Worker 同时扫到同一任务会重复执行。锁保证同一任务同时只有一个 Worker 处理。
  • 为什么要死信? 重试到上限仍失败的任务不能无限重试,进死信队列人工处理,避免阻塞后续任务。
  • 为什么时间轮适合短延迟? 时间轮用循环数组+指针推进,插入 O(1),到期检测 O(1);但内存态,重启丢失。适合“连接超时”这类进程内短任务。

【选型判断树】

text
延迟精度?
├─ 毫秒级、进程内 → 时间轮
├─ 秒级、需持久化 → Redis ZSet
├─ 分钟级以上 → DB 扫表 / XXL-JOB
└─ 要可靠投递+死信 → RocketMQ 延迟消息

平台化:任务表+Worker 集群+分布式锁+重试死信
幂等:业务唯一键

判断口诀: 按精度选方案,分级存储;Worker+锁防重;死信兜底。

【口述骨架】(5 分钟;框架 C,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“延迟任务是精度、可靠性、幂等三角”
0:30–1:30四方案时间轮/ZSet/MQ/DB 对比
1:30–2:30分级秒级内存,分钟级 DB
2:30–3:30Worker集群+分布式锁+分片
3:30–4:30可靠性持久化、重试、死信、幂等
4:30–5:00收尾“一句话:精度定方案,幂等防重复,死信兜底”

【关键数字】

参数经验值说明
时间轮精度毫秒进程内
Redis ZSet秒级持久化
DB 扫描分钟级高可靠
重试3~5 次指数退避死信
订单超时15~30 分钟常见
日任务量数百万级平台化前提
到期峰值均值约 60/s,峰可 10 倍分桶分片

【追问链】(三层)

L1|“全部用扫表行不行?” → 秒级延迟不可控;高精度场景需时间轮/Redis。

L2|“任务量级大怎么办?” → 按执行时间分桶(减少扫描范围)、任务表分库分表、Worker 按 task_id 哈希分片;热点业务可独立队列,避免大促任务挤占普通延迟任务。

L3|“Worker 挂了任务会丢吗?” → 任务持久化在 DB/Redis,其他 Worker 可接管;执行中状态需超时回滚。

【评分标准】

档位答案特征
60 分知道延迟队列
80 分四方案对比、持久化、重试
95 分分级存储、分布式锁、幂等、死信、与 131 题超时关单呼应

【关联题】

【自测】

  1. 判断对错:延迟任务全部用内存时间轮最简单。 参考答案: 重启丢失;需持久化场景不能用。
  2. 为什么执行器要幂等? 参考答案: 分布式可能重复触发;幂等防业务错误。
  3. 死信的作用? 参考答案: 重试失败的任务人工处理,不阻塞后续。

154. 后台管理系统的菜单/按钮/数据权限,怎么建模(权限系统) ​

【考察内容】RBAC 是后台系统最高频设计题

【题目】后台管理系统要控制:谁能看哪个菜单、谁能点哪个按钮、谁能看哪些数据(比如大区经理只能看本大区)。RBAC 模型怎么建(用户-角色-权限)?数据权限(行级)怎么做?权限变更如何生效?

【参考答案】

  1. RBAC 模型:用户 ↔ 角色(多对多)↔ 权限(多对多);权限=资源+操作(如“订单-查看/导出”);
  2. 表设计:用户表、角色表、权限表(或菜单表)、用户-角色关联表、角色-权限关联表;
  3. 授权粒度:
    • 功能权限(菜单/按钮):用户登录后查权限列表 → 前端控制菜单显隐+按钮禁用,后端接口鉴权(注解/拦截器校验,防止绕过前端);
    • 数据权限(只能看本部门/本人数据):SQL 层注入条件(部门过滤、owner 过滤),或数据权限规则表;
  4. 鉴权实现:登录生成 Token(含用户 ID)→ 请求拦截器解析 → 查缓存(用户权限缓存 Redis,变更后失效)→ 校验接口所需权限 → 放行/403;
  5. 权限变更:改角色即生效(权限缓存失效);超级管理员(绕过校验);
  6. 扩展:组织架构(部门树)、用户组、权限继承(角色继承/组织继承)、操作审计(谁在何时做了什么);
  7. 安全:越权防护(水平越权:改 ID 查别人的数据——接口必须校验资源归属,不能只靠前端);
  8. 容量估算:用户假设 5 万、角色 200、权限点 2000;权限缓存约 5 万×2KB ≈ 100MB Redis,完全可全量缓存;鉴权拦截器单次 Redis GET 亚毫秒,不构成瓶颈;
  9. 失败与降级:权限服务挂→网关用本地最近成功缓存(短 TTL)短时放行低敏只读,写操作与敏感数据一律拒绝;分级口径见第 160 题——高越权风险的读默认 fail-closed;缓存击穿用互斥锁重建;变更风暴时批量失效而非逐用户删。

【原理溯源】

  • 为什么 RBAC 要“用户-角色-权限”三层而不是用户直接挂权限? 用户数量大、变动频繁;权限相对稳定。角色作为中间层,把“一组权限”打包,用户赋角色即可。千名员工只需维护少量角色,管理复杂度从 O(用户×权限) 降到 O(用户×角色)+O(角色×权限)。
  • 为什么前端隐藏按钮不够? 前端可被绕过(直接调 API)。必须后端接口鉴权,前端只是体验优化。安全必须在服务端强制。
  • 数据权限为什么难? 功能权限是“能不能调这个接口”,数据权限是“能看哪些行”。行级过滤要在 SQL 层注入条件(如 AND dept_id = ?),或用数据权限规则引擎。漏做会导致越权看别人数据。
  • 水平越权怎么防? 用户 A 改 URL 里的 ID 访问 B 的订单。接口内必须校验资源归属(order.user_id == current_user),不能只依赖“已登录”。这是 OWASP 越权漏洞的典型。
  • 权限变更为什么要失效缓存? 权限缓存在 Redis 提升性能;变更后不失效则用户仍持旧权限(离职员工仍可访问)。改角色时主动删缓存。

【选型判断树】

text
功能权限:RBAC 三表 + 后端接口鉴权
数据权限:SQL 注入条件 / 规则表
缓存:Redis 存用户权限,变更失效
越权:接口内校验资源归属
审计:操作日志

判断口诀: 角色打包权限,后端强制鉴权,数据行级过滤,归属校验防越权。

【口述骨架】(5 分钟;框架 C,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“权限是 RBAC+数据权限+越权防护”
0:30–1:30RBAC 模型三层多对多;表设计
1:30–2:30功能权限前端显隐+后端鉴权
2:30–3:30数据权限SQL 注入条件;部门过滤
3:30–4:30安全与生效缓存失效;水平越权;审计
4:30–5:00收尾“一句话:角色简化管理,后端强制安全,行级控制数据”

【关键数字】

参数经验值说明
角色数数十~数百(本题按 200 估)远少于用户
权限缓存Redis,变更失效性能
越权接口内校验归属安全底线
审计全操作留痕合规
权限缓存体积~100MB(5 万用户 × 约 2KB)Redis 全量放得下

【追问链】(三层)

L1|“只做前端菜单隐藏行不行?” → 不行,可绕过;必须后端接口鉴权。

L2|“水平越权怎么防?” → 接口内校验资源 owner 与当前用户/部门关系,或数据权限 SQL 兜底;不能只靠「已登录」。导出类接口同样要行级过滤,否则一键拖库。

L3|“权限改了用户要重新登录吗?” → 不需要;权限缓存主动失效,下次请求即新权限。

【评分标准】

档位答案特征
60 分知道用户-角色-权限
80 分RBAC 三层、后端鉴权
95 分数据权限行级、水平越权、缓存失效、审计、与 155/138 联动

【关联题】

【自测】

  1. 判断对错:前端隐藏按钮即可控制权限。 参考答案: 可绕过;必须后端鉴权。
  2. RBAC 三层的好处? 参考答案: 角色打包权限,简化用户-权限管理复杂度。
  3. 水平越权是什么?怎么防? 参考答案: 改 ID 访问他人数据;接口内校验资源归属。

155. 公司 10 个系统要一次登录全通(SSO) ​

【考察内容】SSO 是登录体系高级题

【题目】公司内部有 10 个系统(OA/财务/工单…),每个都登录太烦。要做 SSO:登录一次,所有系统免登。SSO 的流程(认证中心、票据、回调)怎么设计?跨域 Cookie 问题怎么处理?登出怎么全局生效?

【参考答案】

  1. 核心:统一认证中心(签发「用户是谁」的身份断言——CAS / SAML 2.0 / OIDC 这类 SSO 服务),各系统不再各自登录;裸 OAuth2 只是授权框架、不定义身份断言,不能当认证层(第三方授权≠统一认证),要用它就在授权码之上加 OIDC 的 ID Token;跨域 Cookie:不同主域的浏览器不共享 Cookie——能统一父域就把系统都挂到 *.company.com(Cookie 设 Domain 即可);跨主域时不要指望 Cookie 传递,认证中心只管自己域内的会话,业务系统之间靠 302 跳转带一次性 ticket、各自建本地会话;App 与第三方走 OAuth2 授权码;
  2. 流程(基于 Cookie+票据):
    • 用户访问系统 A(未登录)→ 302 跳转认证中心 → 认证中心展示登录页 → 用户登录成功 → 认证中心生成全局票据(TGT/Ticket)写入认证中心 Cookie(域 *.company.com 或认证中心域名)→ 302 带 ticket 回跳系统 A → 系统 A 拿 ticket 到认证中心校验(换取用户信息+本地会话)→ 放行;
    • 访问系统 B:B 同样跳转认证中心 → 认证中心发现已有 TGT(已登录)→ 直接发 ticket 回跳 B → B 校验放行——一次登录,处处通行;
  3. 会话管理:认证中心持有全局会话(Redis,可主动踢人);各系统持有本地会话(Token/短时);
  4. 登出:单点登出——认证中心发登出通知,各系统清会话(消息/回调);
  5. 安全:ticket 一次性(用后即毁)、短过期、HTTPS、防 CSRF(state 参数)、回调地址白名单;
  6. 变体:OAuth2(第三方授权,支持 App 扫码)、JWT 无状态 SSO(各系统验签,无中心会话但踢人难);
  7. 容量估算:公司 5 万员工、日活登录 2 万,认证中心登录峰值通常 < 100 QPS,集群冗余足够;各系统 ticket 校验:日活登录 2 万×人均跨系统访问约 20 次=40 万次/日,摊算 4.6/s、集中时段 8~14 倍也只有数十 QPS(比登录峰值高,是因为一次登录会在多个系统各校验一次);要报「千级」必须说明是按每系统各一套轮询/心跳算的。校验接口无状态易水平扩;
  8. 失败与降级:认证中心挂→新登录不可用,已签发的本地会话仍可短时工作;认证中心需集群+会话 Redis 高可用;可保留紧急本地登录白名单(仅运维),并告警。

【原理溯源】

  • 为什么要认证中心而不是各系统各自登录? 各自登录要维护多套账号体系、用户体验差、安全策略不一致。中心化后一处登录、统一策略、统一审计。这是身份认证的平台化。
  • TGT 和 Ticket 的区别? TGT(Ticket Granting Ticket)是认证中心的全局会话凭证,存在认证中心 Cookie;Ticket(Service Ticket)是发给具体业务系统的一次性凭证,业务系统拿它换用户信息。TGT 长期、Ticket 一次性。分层凭证降低泄露风险。
  • 跨域 Cookie 怎么处理? 认证中心与业务系统若不同主域,Cookie 无法共享。方案:① 统一父域(*.company.com);② 认证中心独立域,靠 302 重定向传递 ticket(不依赖 Cookie 共享);③ OAuth2 授权码模式。CAS 用 302+ticket 天然跨域。
  • 为什么 ticket 要一次性? 防重放。ticket 被截获后若可重复使用,攻击者可冒充。用后即毁+短过期,限制风险。
  • 单点登出为什么难? 认证中心清了 TGT,但各系统本地会话还在。需要通知所有系统清会话(回调/广播)。JWT 无状态方案更难踢人(token 有效期内无法作废,需黑名单)。

【选型判断树】

text
企业内部统一登录 → CAS/CAS-like(Cookie+Ticket)
第三方/App 授权 → OAuth2
无状态+可踢人难接受 → 需中心会话黑名单

安全:ticket 一次性、短过期、白名单、HTTPS
登出:中心通知各系统清会话

判断口诀: 中心管 TGT,业务管本地会话,ticket 一次性,登出要广播。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“SSO 是认证中心+票据+回跳校验”
0:30–1:30主流程302 跳转、TGT、ticket、校验
1:30–2:30第二系统免登已有 TGT 直接发 ticket
2:30–3:30跨域与安全父域/302;一次性;白名单
3:30–4:30登出与变体单点登出;OAuth2/JWT
4:30–5:00收尾“一句话:TGT 全局会话,ticket 一次性换本地会话”

【关键数字】

参数经验值说明
ticket 有效期1~10 分钟一次性
TGT 有效期小时~天全局会话
回调白名单必须防开放重定向
登出中心广播各系统清会话
登录峰值< 100 QPS(企业规模)易集群
ticket 校验千级 QPS 合计无状态可扩

【追问链】(三层)

L1|“把 JWT 当 SSO 全解行不行?” → JWT 无中心会话,踢人难;适合无状态 API,不适合需强制登出的企业 SSO。

L2|“ticket 被截获怎么办?” → 一次性+短过期+HTTPS+回调白名单;截获也难利用。再加:ticket 与目标系统绑定、校验失败即拉黑来源 IP、审计异常校验次数。

L3|“单点登出怎么实现?” → 认证中心通知各系统清本地会话(回调/消息);JWT 需黑名单辅助。

【评分标准】

档位答案特征
60 分知道要做统一登录
80 分认证中心+ticket 流程
95 分TGT/ticket 分层、跨域方案、单点登出、与 OAuth2/JWT 对比、与 130 题票据呼应

【关联题】

【自测】

  1. 判断对错:各系统可以共享一个 Cookie 完成 SSO。 参考答案: 跨主域无法共享;靠 302+ticket 或 OAuth2。
  2. TGT 和 ticket 区别? 参考答案: TGT 全局会话;ticket 一次性业务凭证。
  3. 为什么 ticket 要一次性? 参考答案: 防重放;用后即毁。

156. 群红包金额拆分、并发抢、退回,怎么设计(抢红包) ​

【考察内容】抢红包是 Redis 场景最高频设计题(腾讯系必考)

【题目】群里发一个总金额 100 元、50 个的红包,几百人同时抢:金额怎么拆分(每个人拿多少)、并发抢怎么保证不超领、24 小时未领完的钱怎么自动退回。数据库/Redis 方案怎么设计?

【参考答案】

  1. 发红包:金额校验(分存储)→ 生成红包记录(总额、个数、状态)→ 拆红包算法(发时预拆分 or 抢时动态拆分):
    • 二次均值法:每次抢剩余金额/剩余个数×2 为上限随机(保证分布均匀且不超);
    • 预拆分:发时把金额拆好存 Redis List(pop 即得);
  2. 抢红包流程(并发核心):用户抢 → Redis Lua 原子(库存判断:红包是否还有 + 是否已抢过(Set 去重)→ 从 List 弹一个金额;Cluster 下这些 key 必须用 hash tag(如 rp:{红包ID}:*)落同一 slot,否则一个脚本动多 key 直接 CROSSSLOT,见第 150 题 / 或动态拆一个 → 记录)→ 入账(余额/零钱,MQ 异步落账+幂等);
  3. 防重复抢:Redis Set(redpack:{id}:users)SADD 幂等,已抢返回“已抢过”;
  4. 24 小时退回:发红包时写延时任务(24h 后检查剩余金额)→ 未领完自动退回原账户(幂等:退回状态机,防重复退);
  5. 并发控制:全部用 Redis 原子操作(Lua),DB 只做异步落账与对账(Redis 记录 vs DB 明细核对);
  6. 防刷:每人限领一次、频控、风控(异常账号);
  7. 容量估算:单个红包几百人抢只有 10³ QPS 量级(500 人在 1 秒内点开=500 QPS,算上重试也到不了数万);会打到「数万 QPS 同一 key」的是全站级热点——除夕全站假设 50 万 QPS,同一时刻全民抢同类红包、按红包 ID 哈希后,同一刻被抢的热点红包若只有约 50 个,单个热点 key 才会聚到 5e5÷50=10⁴ QPS 量级(分母换成几百个热点,就掉回千级),这才是分片计数的动机;单红包 List/Set 体积可忽略,瓶颈在热点红包 key,可用分片计数或本地缓存挡重复请求;
  8. 失败与降级:Redis 挂→暂停领取并提示排队,禁止穿透 DB 抢;恢复后从 AOF/DB 明细对齐;入账 MQ 积压异步消化;退回任务失败则重试并告警,资金以状态机为准只退一次。

【原理溯源】

  • 为什么拆红包用“二次均值法”? 若每次在剩余金额内均匀随机,前面的人可能把钱拿光,后面全 0.01,体验差。二次均值法设上限为“剩余均值×2”,保证分布均匀且总额不超。这是用约束随机换公平感。
  • 为什么全程要 Redis Lua 原子? “判断是否有剩余→判断是否已抢→弹出金额→记录”多步操作,非原子会在并发下超发/重复抢。Lua 在 Redis 单线程内原子执行,天然无并发冲突。DB 无法承受这种高并发写。
  • 为什么 Set 能防重复抢? SADD 返回 1 表示新加入,返回 0 表示已存在。一次原子操作完成“去重+判断”,比先查再插可靠。
  • 为什么入账要异步? 抢红包峰值极高,同步写账户 DB 会挂。Redis 记录“谁抢了多少”即返回,MQ 异步入账+幂等(红包 ID+用户 ID 唯一),对账兜底。
  • 为什么要延时退回? 24 小时未领完的钱必须退回,否则资损。延时任务(见 153 题)到点检查剩余,状态机保证只退一次。这是资金闭环。

【选型判断树】

text
拆分:预拆分存 List(简单)或动态二次均值
抢:Lua 原子(剩余判断+去重+弹出+记录)
防重:Set SADD 幂等
入账:MQ 异步 + 唯一索引
退回:延时任务 24h + 状态机幂等
对账:Redis vs DB

判断口诀: Lua 保原子,Set 保不重,MQ 保异步,延时保退回。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“抢红包核心是 Lua 原子+拆分算法+资金闭环”
0:30–1:30拆分算法二次均值;预拆分 vs 动态
1:30–2:30抢流程Lua 原子;Set 防重
2:30–3:30入账MQ 异步;幂等
3:30–4:30退回与对账延时任务;状态机;对账
4:30–5:00收尾“一句话:原子抢、异步入账、延时退回、对账兜底”

【关键数字】

参数经验值说明
金额分(整数)精度
拆分上限剩余均值×2二次均值
退回24h常见
防重Set + 唯一索引双保险
对账Redis vs DB兜底
单红包峰值数百~数千 QPS(几百人 1 秒内点开)全站级热点聚合到同一 key 才到万级
全站除夕约 50 万 QPS 量级需 Cluster

【追问链】(三层)

L1|“为什么金额用分?” → 浮点精度问题;资金场景必须整数。

L2|“拆分算法为什么用 2 倍均值?” → 保证每次不超过剩余均值 2 倍,分布均匀且不会前面拿光;预拆分 List 则算法在发红包时定死,抢时只 pop,实现更简单。

L3|“Redis 挂了红包记录丢了怎么办?” → 持久化(AOF);DB 对账;业务上可容忍极端丢失(小额)或告警人工。

【评分标准】

档位答案特征
60 分知道要控库存防超发
80 分Lua 原子、Set 防重、MQ 异步
95 分二次均值拆分、延时退回、对账、金额分、与 134/150 同构

【关联题】

【自测】

  1. 判断对错:抢红包可以先查库存再扣减。 参考答案: 错。必须 Lua 原子,否则并发超发。
  2. 二次均值法解决什么? 参考答案: 防止前面的人拿光,保证分布均匀。
  3. 24 小时退回怎么保证不重复退? 参考答案: 状态机幂等;延时任务触发。

157. 先预约才有抢购资格,资格怎么发放和校验(预约资格系统) ​

【考察内容】秒杀的资格预审设计

【题目】秒杀改预约制:用户先预约,预约成功才获得抢购资格,秒杀开始后只有有资格的人能下单。资格怎么发放(限量)?秒杀时资格怎么校验(不穿透 DB)?资格能否转让/过期?

【参考答案】

  1. 需求:预约(活动前)→ 抢购(活动开始,有资格才能抢)→ 防“未预约直接抢”;
  2. 预约阶段:用户点预约 → 记录预约(Redis Set/DB 表,唯一索引 user_id+活动)→ 预约人数统计(活动预热参考);可设预约上限(限量预约);
  3. 资格生成:活动开始时(或预约成功时)给预约用户发放资格 token(存 Redis,key=quota:{activity}:{userId},value=资格凭证);
  4. 抢购校验:抢购请求必须携带资格 token → 服务端校验(Redis 存在且未使用)→ 原子消费资格(GETDEL,或 Lua 的 EXISTS+DEL;不能用 SETNX——它只表达「首次写入成功」的占位语义,删不掉已存在的 token,见第 110 题)→ 进入抢购流程(限流+预扣库存);
  5. 防绕过:资格校验在服务端(token 与用户绑定:Redis 存 userId 比对),前端无意义;
  6. 未预约用户:无资格 token,直接拒绝——减少抢购瞬间无效流量;
  7. 扩展:资格回收/过期清理(资格不可转让——token 绑 userId,服务端校验见第 5 条;若业务确实要「转赠」,只能走服务端重发资格+唯一索引改绑并全程留痕,绝不能在客户端换人)、预约提醒(推送)、资格使用统计;
  8. 容量估算:预约假设 100 万人,token 均 50B=50MB;但每人一个独立 key,还要加每 key 约 60–80B 的结构开销(见第 149 题),实际约 100–150MB Redis;秒杀瞬间有资格用户假设 10 万,校验+原子消费 QPS 10 万级,Redis Cluster 可扛,无需查 DB;
  9. 失败与降级:资格 Redis 不可用时降级查预热 DB(需限流);token 服务异常则暂停抢购入口;活动结束定时清理残留 token,并与预约表/订单表对账,防止「无预约却下单」。

【原理溯源】

  • 为什么要预约制而不是直接秒杀? 直接秒杀时全部流量涌入,大量无效请求(未预约、黄牛)打挂系统。预约制把“筛选”前置到活动前,抢购瞬间只有有资格的用户进入,流量大幅下降。这是用时间换空间——把峰值压力摊到预约期。
  • 为什么要资格 token 而不是查 DB? 抢购瞬间 QPS 极高,查 DB 会挂。token 在 Redis,O(1) 校验+原子消费,把 DB 挡在外面。
  • 为什么 token 要与用户绑定? 防转让滥用。token 存 userId,校验时比对当前用户,即使 token 泄露也无法被他人使用。
  • 为什么要原子消费(GETDEL,或 Lua 内 EXISTS+GET+DEL)? 防止一个 token 被用两次。原子「读取并删除」才保证一次性;SETNX 不能当消费手段——它只有“首次写入成功”的占位语义,对已存在的 key 既不返回旧值也不删除(见本题【参考答案】第 4 条与第 110 题)。
  • 预约人数超过库存怎么办? 预约只是筛选资格,不保证中签。抢购时库存决定成败,预约多说明热度高,可加大备货或加价。

【选型判断树】

text
预约:Redis Set / DB 唯一索引(user_id+活动)
资格发放:Redis token,绑定 userId
抢购校验:Redis 存在性 + 原子消费
未预约:直接拒绝
过期:定时清理

判断口诀: 预约筛人、token 资格、原子消费、服务端绑定。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“预约资格是秒杀的前置流量筛选”
0:30–1:30预约阶段记录、唯一索引、上限
1:30–2:30资格发放Redis token 绑定用户
2:30–3:30抢购校验原子消费;防绕过
3:30–4:30扩展过期清理、提醒、统计
4:30–5:00收尾“一句话:预约筛流量,token 保资格,原子防重用”

【关键数字】

参数经验值说明
预约上限可限量控热度
token 存储Redis毫秒校验
消费GETDEL 原子一次性
过期活动结束后清理防残留
预约 token 内存纯 value 50MB;每人一个独立 key 含结构开销约 100–150MB不轻,口径见正文第 8 条
抢购校验 QPS约 10 万(有资格用户)Redis 扛

【追问链】(三层)

L1|“资格不绑定用户会怎样?” → token 可转让滥用;必须绑定 userId 校验。

L2|“一个 token 能抢两次吗?” → 不能;原子消费(GETDEL 或 Lua EXISTS+DEL,不是 SETNX)保证一次性。客户端重试会拿到「已使用」而非二次下单;订单侧再加用户+活动唯一索引双保险。

L3|“预约人数远超库存怎么办?” → 抢购限流+库存决定;预约只是资格筛选,可加大备货或营销造势。

【评分标准】

档位答案特征
60 分知道要预约才能抢
80 分token 资格、原子消费、服务端校验
95 分流量筛选价值、绑定防转让、过期清理、与 134/147/150 联动

【关联题】

【自测】

  1. 判断对错:资格 token 可以放在前端,抢购时带上即可。 参考答案: 前端可伪造;必须服务端 Redis 校验+绑定用户。
  2. 为什么要原子消费? 参考答案: 防止一个 token 被用两次。
  3. 预约制的系统价值? 参考答案: 前置筛选,降低抢购瞬间无效流量。

158. 每天百万封邮件/短信,可靠发送与退订管理(邮件发送系统) ​

【考察内容】可靠批量任务系统设计

【题目】运营每天要发百万封营销邮件/短信,通知类也要发。要求:模板管理、失败重试、退订处理(合规)、发送频率控制(防骚扰投诉)、通道容灾。系统怎么设计?

【参考答案】

  1. 架构:业务方 → 发送 API(校验+模板渲染)→ 消息落库(发送任务表)→ MQ 分发 → 发送执行器(调用邮件/短信渠道)→ 回执更新;
  2. 关键设计:
    • 模板管理:模板+变量渲染(防注入);短信签名、字数、敏感词校验;
    • 批量发送:营销邮件批量任务(分片、限速——渠道 QPS 有限,防被渠道封);
    • 重试与死信:发送失败重试(指数退避)→ 死信人工;回执(投递成功/失败/退订)更新状态;
    • 幂等:发送任务唯一 ID(业务+类型),重复提交不重复发送;
    • 退订/黑名单:营销类必须支持退订(合规),发送前过滤黑名单;
  3. 性能:批量合并(邮件批量接口)、并发控制(渠道限速)、消息表分表(按发送时间/状态);
  4. 合规:授权发送(用户同意)、频率限制(每日上限)、敏感内容审核;
  5. 监控:发送量、成功率、失败原因分布、渠道延迟;
  6. 容量估算:日 100 万封,若 8 小时窗口则均值约 35 条/s,营销高峰可到 100+/s;短信渠道配额常见 500~2000 条/s(按条计——批量接口一次请求塞 N 条要扣 N 枚令牌,别把它当 QPS 限),执行器必须按渠道令牌桶限速,宁慢勿被封;
  7. 失败与降级:主渠道挂自动切备用渠道并告警;模板/渲染服务挂时通知类降级固定文案,营销类延后;任务积压按优先级:验证码/交易通知 > 营销。

【原理溯源】

  • 为什么要任务表+MQ+执行器? 百万级发送不能同步调渠道 API(阻塞、超时、失败难追踪)。任务表落库保证不丢,MQ 削峰,执行器按渠道限速调用。这是批量可靠任务的标准架构。
  • 为什么要渠道限速? 短信/邮件渠道有 QPS 配额,打满会被封。执行器用令牌桶/信号量按渠道配置限速,宁慢勿封。
  • 为什么要退订? 合规要求(个保法/反垃圾邮件法)。营销类必须提供退订入口,发送前过滤黑名单。不合规有法律风险。
  • 为什么要回执? 发送成功≠送达。回执更新状态,支撑统计、重试、用户查询。
  • 为什么幂等用业务键? 业务方可能重复提交(重试)。用“业务单号+通知类型”唯一,重复提交不重复发送。

【选型判断树】

text
任务表(落库)→ MQ(削峰)→ 执行器(限速调渠道)
重试:指数退避 + 死信
幂等:业务键唯一
合规:退订 + 黑名单 + 频控
监控:成功率、失败分布

判断口诀: 落库保不丢,MQ 保削峰,限速保不封,退订保合规。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“批量发送是可靠任务+渠道限速+合规”
0:30–1:30架构任务表+MQ+执行器
1:30–2:30限速与重试渠道限速;退避;死信
2:30–3:30合规退订、黑名单、频控
3:30–4:30幂等与监控业务键;成功率
4:30–5:00收尾“一句话:可靠任务架构+渠道限速+合规退订”

【关键数字】

参数经验值说明
日发送百万级批量
渠道限速按配额防封
重试2~3 次退避死信
退订必须合规
频控日上限防骚扰
日发送均值~35/s(100 万/8h)峰可数倍
渠道配额常见 500~2000 TPS必须限速

【追问链】(三层)

L1|“渠道限速怎么实现?” → 令牌桶/信号量控制执行器并发,按渠道配置 QPS 上限。

L2|“用户退订后怎么生效?” → 黑名单实时更新;发送前过滤;已在队列中的存量任务消费时再查一次黑名单,避免“退订后仍收到”。

L3|“渠道挂了怎么办?” → 多渠道备份、故障转移、任务积压告警;死信人工。

【评分标准】

档位答案特征
60 分知道要调发送 API
80 分任务表+MQ+限速+重试
95 分退订合规、幂等、回执、渠道容灾、与 139 题呼应

【关联题】

【自测】

  1. 判断对错:可以直接循环调短信 API 发百万条。 参考答案: 会超时、被封、失败难追踪;必须任务表+MQ+限速。
  2. 为什么要退订? 参考答案: 合规要求;防投诉与法律风险。
  3. 渠道限速怎么做的? 参考答案: 令牌桶/信号量按渠道 QPS 配额控制。

159. 积分获取/消耗/等级成长,会员体系怎么做(会员积分系统) ​

【考察内容】账户流水型业务设计

【题目】电商要做会员体系:消费得积分、积分抵现、成长值升级(普通→黄金→钻石)、不同等级不同权益。积分和成长值的流水怎么记?升级规则怎么设计、要不要降级?积分消耗和账户金额一样不能超扣,怎么保证?

【参考答案】

  1. 数据模型:用户积分账户(user_id、可用积分、累计积分)、积分流水表(流水号、类型:获取/消耗/过期、数量、关联业务单号、时间——追加写);等级表(等级、成长值区间、权益);成长值流水;
  2. 积分获取:下单/签到/活动 → 积分发放(MQ 异步+幂等:业务单号唯一,防重复发放)→ 写流水+更新账户(同一事务);
  3. 积分消耗:积分抵扣/兑换 → 扣减(原子性:条件更新 available >= 数量,或账户行锁)→ 写流水;防并发扣减(同一账户同时下单用积分——行锁/乐观锁);
  4. 积分过期:按获取时间先进先出(FIFO)过期——定期任务按批次过期或实时计算(流水队列);
  5. 等级(题干第二问「要不要降级」要先定口径,两种都要能答):保级制=成长值按自然年或滚动 12 个月统计,周期末重算,低于门槛即降档(多数电商会员用这套,否则等级只升不降、权益成本失控);不降级制=成长值只累计、等级永久(体验好,但权益成本随年限单调上涨,需财务确认)。选定后必须配齐四件:成长值有效期(与第 4 条积分 FIFO 同构)、降级前保级提醒+缓冲期、降档后权益回收(已领取的券不追回、未领取按新档发放)、等级变更事件通知下游刷新权益缓存。工程实现:成长值流水+每日/每周期结算作业重算,不在每次消费时实时判级(避免边界抖动);等级读走缓存;
  6. 对账:积分账户余额=流水和(定期核对);积分是“虚拟资产”,记录审计留痕;
  7. 风控:异常获取(刷单积分)监测、积分套现防护;
  8. 容量估算:日发放假设 100 万笔、消耗 20 万笔,流水日增约 120 万行,按月分表;账户读热点用户可缓存;扣减走条件更新,单账户串行可接受;
  9. 失败与降级:积分服务挂→下单主链路不依赖积分成功,发放 MQ 延迟补发;扣减失败提示积分不可用,不阻塞支付;过期任务失败次日重跑,以流水为准。

【原理溯源】

  • 为什么积分要“账户+流水”而不是直接改余额? 同支付账务(132 题):流水追加写可审计、可对账、可追溯;直接改余额丢历史、并发不安全。积分是虚拟资产,但账务纪律与真金白银一样。
  • 为什么发放要幂等? 下单事件可能重复消费。业务单号唯一索引保证同一订单只发一次积分。
  • 为什么扣减要条件更新? 并发下两个请求同时扣同一账户会超扣。WHERE available >= n 条件更新原子保证。
  • 为什么积分要过期? 控制负债规模、刺激消费。FIFO 按获取时间过期,公平且易实现(按批次)。
  • 成长值和积分为什么要分开? 积分可消耗(抵现),成长值只累计(升级)。混在一起会导致“消耗积分导致降级”的错误语义。分开建模,职责清晰。

【选型判断树】

text
账户:积分账户 + 流水(追加写)
发放:MQ 异步 + 业务单号幂等
扣减:条件更新防超扣
过期:FIFO 按批次
等级:先定降级口径(保级制=周期重算降档/永久制=等级只升不降、需财务确认)→ 成长值累计 → 等级 → 权益缓存
对账:余额=流水和

判断口诀: 流水驱动、幂等发放、原子扣减、FIFO 过期、成长值分离。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“积分是账户流水型业务,与支付同构”
0:30–1:30数据模型账户+流水+等级表
1:30–2:30获取与消耗幂等发放;条件更新扣减
2:30–3:30过期与等级FIFO;成长值分离;降级要选口径:保级制(周期末重算、低于门槛降档)或永久制(权益成本随年限涨)
3:30–4:30对账与风控余额=流水和;防刷
4:30–5:00收尾“一句话:流水保可审计,幂等保不重复,条件保不超扣”

【关键数字】

参数经验值说明
发放幂等业务单号唯一防重复
扣减条件更新 WHERE available>=n防超扣
过期FIFO 批次公平
对账日/周余额=流水和
等级成长值区间权益缓存
日流水~120 万笔(发 100 万+耗 20 万)按月分表

【追问链】(三层)

L1|“积分过期怎么算?” → 按批次(获取时间)先进先出,避免逐笔计算。

L2|“并发扣积分怎么防超扣?” → 条件更新 WHERE available >= n 或行锁/乐观锁;失败返回「积分不足」。同一账户热点时可本地排队串行,减少无效行锁冲突。

L3|“积分能套现吗?怎么防?” → 限制使用场景(不可提现)、风控监测异常获取与兑换、与支付隔离。

【评分标准】

档位答案特征
60 分知道要有积分账户
80 分流水驱动、幂等、条件扣减
95 分FIFO 过期、成长值分离、等级口径(保级/永久+缓冲期与权益回收)、对账、风控套现、与 132 题同构

【关联题】

【自测】

  1. 判断对错:积分可以直接 UPDATE balance。 参考答案: 错。流水驱动,可审计可对账。
  2. 为什么成长值和积分分开? 参考答案: 积分可消耗,成长值只累计;混用会导致降级错误。
  3. 积分过期用什么策略? 参考答案: FIFO 按获取时间批次过期。

160. 企业内部知识库:文档组织/全文检索/权限/版本(知识库系统) ​

【考察内容】知识管理类系统设计

【题目】公司要做内部知识库:文档按目录/空间组织、全文检索、按团队控制权限、文档有历史版本。文档存储(DB+对象存储)、全文检索(ES)、权限模型、版本管理(diff/回溯)怎么设计?

【参考答案】

  1. 核心实体:空间/知识库 → 目录树 → 文档;文档内容(结构化块/富文本/附件);
  2. 文档存储:文档表(doc_id、空间、标题、作者、状态)+ 内容存储(对象存储存大正文/附件,DB 存元数据与索引信息);版本管理(编辑生成版本快照,支持回滚);diff:按段落/块 ID 做块级差异(或存 op 增量),只保留增量+定期全量快照,回溯时按「快照+diff」重建,同一份 diff 直接供前端做版本对比展示;
  3. 全文检索:文档内容同步到 ES(增量:编辑事件触发/定时扫描),支持标题/正文/标签检索,高亮展示;
  4. 权限:空间级(成员/只读/管理员)+文档级(继承空间权限,可覆盖);RBAC+ACL 结合;权限校验在服务端;
  5. 并发编辑:编辑锁(悲观:编辑中锁定他人只读)或协作文档(OT/CRDT,见 145 题);
  6. 其他:目录树(parent_id+冗余 path 物化路径避免递归查询,移动目录时批量重写子树 path 或改闭包表;同级按排序字段)、评论/点赞(复用通用模块)、移动端同步、导入导出(Word/PDF 转换)、审计(谁改了文档);
  7. 扩展:AI 能力(文档摘要、问答 RAG);
  8. 容量估算:假设 10 万文档×均 50KB → 对象存储约 5GB,极小;ES 索引因分词膨胀约 2~3 倍正文;日编辑 5000 次增量同步完全可接受,检索读 QPS 内部系统通常不高,缓存热门文档元数据即可;
  9. 失败与降级:ES 挂→搜索降级为目录浏览+最近打开;对象存储故障→文档打开重试,版本快照仍可从 DB 元数据定位;权限服务超时默认拒绝(fail-closed),避免越权读;只有非敏感的列表类只读才另设短 TTL 缓存兜底(与第 154 题的分级口径配套,两处不是两套答案)。

【原理溯源】

  • 为什么正文要对象存储而不是 DB 大字段? 文档正文可能很大(图文混排),DB 大字段拖慢查询、备份、复制。对象存储存内容,DB 只存元数据与索引信息。这是冷热分离。
  • 为什么权限要“空间级+文档级”? 空间是团队边界,默认继承空间权限;个别文档需特殊控制(如 HR 文档仅管理员可见)则覆盖。继承+覆盖兼顾管理效率与灵活性。
  • 为什么要版本快照? 文档会被编辑,需回溯历史(误删恢复、审计)。每次编辑生成版本,可 diff、可回滚。存储上用“增量+定期快照”平衡空间。
  • 为什么检索要 ES? 全文检索(中文分词、高亮、相关性)MySQL 做不好。ES 倒排索引+分词器是标准方案。
  • 目录树为什么用 parent_id+path? 纯 parent_id 要递归查路径;冗余 path(物化路径)可一次查子树。深层目录频繁遍历时 path 更快。

【选型判断树】

text
存储:DB 元数据 + 对象存储正文
检索:ES 增量同步
权限:空间级继承 + 文档级覆盖;服务端校验
版本:快照+diff+回滚
目录:parent_id + path 冗余
并发:编辑锁或 OT/CRDT

判断口诀: 冷热分离、继承覆盖、快照回滚、path 加速。

【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“知识库是内容存储+检索+权限+版本”
0:30–1:30存储DB+对象存储分离
1:30–2:30检索ES 增量同步
2:30–3:30权限空间+文档两级;服务端
3:30–4:30版本与目录快照回滚;path 冗余
4:30–5:00收尾“一句话:冷热分离保性能,继承覆盖保灵活,快照保可回溯”

【关键数字】

参数经验值说明
正文外置> 1KB冷热分离
版本保留按需(30~90 天)回滚
检索同步秒级增量ES
权限空间+文档继承覆盖
目录path 冗余防递归
文档量级10 万篇×50KB ≈ 5GB对象存储
日编辑数千次ES 增量可行

【追问链】(三层)

L1|“正文存 DB 大字段行不行?” → 查询慢、备份重;应对象存储+ES 索引。

L2|“文档树深层嵌套怎么存?” → parent_id+path 物化路径,避免递归;移动目录时批量重写子树 path(或用闭包表)。

L3|“权限只做前端行不行?” → 不行;必须服务端校验,防越权。

【评分标准】

档位答案特征
60 分知道要存文档和搜索
80 分冷热分离、ES、权限两级
95 分版本快照回滚、path 冗余、并发编辑、与 145/154/136 联动

【关联题】

【自测】

  1. 判断对错:文档正文应存 DB 主表。 参考答案: 大字段拖慢;应对象存储。
  2. 权限为什么要空间+文档两级? 参考答案: 空间默认继承高效,文档可覆盖灵活。
  3. 版本管理的价值? 参考答案: 回溯、审计、误删恢复。

161. 给大模型对话应用做后端:调用管理/限流/成本(LLM 后端) ​

【考察内容】LLM 工程是 2025+ 大热高频题

【题目】公司要做 AI 对话应用(接大模型 API),用户量上来后:调用费暴涨、上游限流、响应慢、Prompt 被刷。后端怎么设计?请求管理(缓存/路由)、限流与配额、成本控制(降级模型)、安全(Prompt 注入防护)怎么做?

【参考答案】

  1. 架构:业务应用 → LLM 网关 → 模型服务(自研部署/vLLM 或外部 API);
  2. 核心能力:
    • 统一接入:多模型(不同厂商/版本)统一 API 封装,路由(按模型能力/成本/负载选模型);
    • 限流与配额:按用户/应用维度限流(令牌桶),防止恶意调用烧钱;配额(免费额度/付费额度);
    • 缓存:相同 Prompt 结果缓存(Redis,TTL 短)——语义缓存(向量相似度命中)降本增效;
    • 成本控制:Token 计量(输入/输出)、按模型定价计费、预算告警(超预算熔断);
    • 重试与容错:模型超时/限流(429)重试(退避)、故障切换(主模型挂切备用模型);
    • 流式输出:SSE(Server-Sent Events)透传首字延迟(TTFT)优化;
  3. 可靠性:请求日志(Prompt/响应/耗时全记录,审计与调试)、敏感内容审核(入站/出站)、数据隔离(租户);Prompt 注入防护(与内容审核是两回事,都要答):系统指令与用户输入、检索片段分层拼接(不可信文本不进指令位)、工具调用走白名单+最小权限、输出前审核与敏感字段过滤、对「忽略指令/交出系统提示词」类越权请求单独检测拦截;
  4. 性能:KV Cache/前缀缓存(同前缀 Prompt 复用)、批处理(vLLM continuous batching)、模型路由(简单请求用快模型);
  5. 监控:Token 消耗、延迟(TTFT/TPOT)、错误率、成本大盘;
  6. 容量与成本:假设日调用 100 万次、均输入 800+输出 500 token;旗舰模型按对外接口价估:示例口径 输入 $3/百万 token、输出 $15/百万 token → 1e6×800 输入=8e8 token≈$2400,1e6×500 输出=5e8 token≈$7500,日成本 $1e4 量级(单价按当期官网价替换,答题要先报单价再报总额),语义缓存命中 30% 可省约三成;
  7. 失败与降级:上游 429/超时→退避后切备用模型;预算熔断后仅白名单可用;全挂时返回缓存结果或排队提示。

【原理溯源】

  • 为什么需要 LLM 网关而不是直连模型 API? 直连无法统一限流、计费、缓存、路由、审计。用户量上来后成本失控、被刷、上游 429 无应对。网关把横切能力集中,业务只调统一 API。这是AI 时代的 API 网关。
  • 为什么要做语义缓存? 相同/相似 Prompt 重复率高(客服 FAQ)。精确匹配缓存命中率低;语义缓存(embedding 相似度>阈值)命中率高,显著降本。代价是要向量检索与相似度计算。
  • 为什么要 Token 计量? LLM 按 token 计费,不计量则成本黑盒。计量后可按用户/应用分摊、设配额、预算告警熔断。
  • 为什么要模型路由? 不同任务对模型能力要求不同。简单任务用小模型(快、便宜),复杂任务用大模型。路由按 prompt 特征/用户等级选模型,平衡成本与效果。
  • 为什么要流式 SSE? LLM 生成慢(秒级),同步等待体验差。SSE 逐 token 推送,首字延迟(TTFT)短,用户感知快。

【选型判断树】

text
网关:统一接入、限流、缓存、计费、路由
缓存:精确匹配 + 语义缓存
限流:用户/应用维度令牌桶
成本:Token 计量 + 预算熔断
路由:按任务复杂度选模型
流式:SSE

判断口诀: 网关集中管控,语义缓存降本,路由平衡成本效果。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“LLM 后端核心是网关化管控”
0:30–1:30网关能力接入、限流、缓存、计费
1:30–2:30成本控制Token 计量;语义缓存;模型路由
2:30–3:30可靠性重试、故障切换、审计
3:30–4:30性能与安全流式 SSE;Prompt 注入防护
4:30–5:00收尾“一句话:网关集中管控,缓存与路由降本,计量防失控”

【关键数字】

参数经验值说明
语义缓存阈值余弦相似度 > 0.9按业务调
Token 计量输入/输出分开计费
限流用户/应用维度防刷
TTFT< 1s首字延迟
预算超限熔断防烧钱
日调用(例)100 万次成本基数
缓存命中收益命中 30% 省约 30% 费用语义缓存

【追问链】(三层)

L1|“语义缓存怎么实现?” → Prompt 转 embedding,向量相似度>阈值命中缓存;动态数据不能缓存。

L2|“上游模型 429 怎么办?” → 退避重试、故障切换备用模型、排队限流;熔断后返回缓存/降级文案,而不是把错误原样打给用户。

L3|“Prompt 注入怎么防?” → 输入过滤、输出审核、沙箱执行、权限最小化;不能完全依赖模型。

【评分标准】

档位答案特征
60 分知道要调大模型 API
80 分网关、限流、缓存、计费
95 分语义缓存、模型路由、流式 SSE、注入防护、成本熔断

【关联题】

【自测】

  1. 判断对错:应用可以直接调 OpenAI API,不需要网关。 参考答案: 无统一限流/计费/缓存/审计;成本易失控。
  2. 语义缓存的价值? 参考答案: 提高缓存命中率,显著降低 LLM 调用成本。
  3. 为什么用 SSE? 参考答案: 流式输出降低首字延迟,改善体验。

162. 视频播放量亿级访问、实时展示、还要防刷(计数系统) ​

【考察内容】计数系统是超高并发设计代表题(抖音/微博高频)

【题目】视频的播放量要实时展示(几亿次播放),刷新一次就 +1 还要防脚本刷量。计数怎么存储(Redis 原子计数→异步落库)?防刷(IP/用户/设备维度去重)怎么做?数据一致性(展示值 vs 最终值)怎么处理?

【参考答案】

  1. 需求:计数写入(播放+1)高频、读取高频(展示)、数值最终一致即可、防刷;
  2. 写入链路:客户端上报(播放事件)→ 接入层校验(防刷:频控、IP/设备指纹、服务端抽检)→ MQ 异步 → 聚合服务(Flink/定时批量合并)→ 定期写 DB;
  3. 实时展示:Redis INCR(key=count:{id})实时累加,读取直接返回;定期落库(如每 5 分钟把增量写 DB,Redis 与 DB 对账)——播放量可容忍秒级延迟,不追求强一致;
  4. 防刷:同用户同视频去重(Redis Set/时间窗口)、频控(单位时间计数上限)、UA/设备校验、异常流量清洗(离线修正);
  5. 热点视频:单 key 热点——计数分片(count:{id}:{0~N},写入随机分片,读取 SUM 聚合)缓解单 key 竞争;
  6. 存储:Redis 承担实时,DB 落最终值(或 ClickHouse 统计),展示用缓存;计数 key 分两类存:长尾视频用 Hash 批量存储(field=视频 ID,value=计数)省内存;头部热视频要从 Hash 里摘出来单独做分片 key——共享 Hash 本身仍是单 key 串行,第 5 条的计数分片对它才有效(按第 124 题「只对超热 key 分片」口径);
  7. 对账:Redis 计数 vs DB 计数差异,定期修正(以事件明细为准);
  8. 容量估算:播放事件假设 1 亿/天 → 全天均值仅 1157/s,但晚间 2 小时集中 40% 时时段均值 ≈ 5600/s,再乘起播瞬时集中系数 10~20 → 峰值按 5.6~11 万 QPS(容量按 10 万规划);Redis INCR 单实例 10 万+,需 Cluster;Hash 批量时若一 Hash 放 1000 个视频,亿级视频约 10 万 key(1e8÷1000;若按千万视频才是 1 万 key);
  9. 失败与降级:Redis 挂→展示降级读 DB 最终值(允许略旧);MQ 积压异步消化不丢事件;对账失败次日重跑,极端可从明细重算。

【原理溯源】

  • 为什么播放量可以容忍最终一致? 播放量是展示型数据,差几次用户无感;强一致代价极高(每次 +1 写 DB)。用 Redis 实时累加+定期落库,把一致性成本降到最低。这是业务语义驱动的一致性取舍。
  • 为什么热点要计数分片? 明星视频播放量单 key 会成热点,所有写打同一 Redis 节点。拆成 count:{id}:0~N,写随机分片,读 SUM,把单点串行变多 key 并行。
  • 为什么要 Hash 批量存储? 亿级视频每个一个 key,key 数量爆炸。用 Hash(field=视频 ID)把多个视频计数放进一个 key,节省内存与 key 管理开销。
  • 为什么防刷要多维度? 单维度(IP)易被绕过(代理池)。IP+设备指纹+用户+时间窗口组合,提高绕过成本。异常流量离线清洗修正计数。
  • 为什么要对账? Redis 可能丢数据(极端),DB 是权威。定期对账以事件明细为准修正,保证最终正确。

【选型判断树】

text
写:防刷校验 → MQ → 聚合 → Redis INCR
读:Redis 直接返回
落库:定期批量写 DB
热点:计数分片 0~N
存储:Hash 批量(field=视频 ID)
对账:Redis vs DB,明细为准

判断口诀: Redis 扛实时、分片扛热点、Hash 省内存、对账保最终。

【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)

时间任务内容
0:00–0:30定性“计数是超高并发+最终一致+防刷”
0:30–1:30写入链路防刷→MQ→Redis INCR
1:30–2:30热点治理计数分片;Hash 批量
2:30–3:30防刷多维度去重;异常清洗
3:30–4:30一致性定期落库;对账
4:30–5:00收尾“一句话:实时靠 Redis,准确靠对账,防刷靠多维”

【关键数字】

参数经验值说明
写入亿级/天超高并发
落库周期1~5 分钟可容忍
热点分片8~32抗单 key
Hash 批量一 key 多视频省内存
防刷多维度IP+设备+用户
对账日级明细为准
播放事件1 亿/天,峰 10 万 QPS需 Cluster
Hash 分桶1 key ≈ 1000 field省 key 数

【追问链】(三层)

L1|“Redis 宕机计数丢了怎么办?” → 播放量可从事件明细重算;Redis 持久化+DB 兜底;业务容忍。

L2|“热点视频单 key 打挂 Redis 怎么办?” → 计数分片 0~N,写随机、读 SUM;极热视频可本地缓存展示值(秒级延迟)再回源聚合。

L3|“怎么防脚本刷量?” → 多维度去重(IP/设备/用户)、频控、异常增速检测、离线清洗修正。

【评分标准】

档位答案特征
60 分知道用 Redis 计数
80 分异步落库、防刷、最终一致
95 分计数分片、Hash 批量、对账、与 124 题点赞同构但更强调海量

【关联题】

【自测】

  1. 判断对错:播放量必须强一致,每次 +1 写 DB。 参考答案: 展示型数据可最终一致;Redis 实时+定期落库。
  2. 热点计数为什么要分片? 参考答案: 避免单 key 串行瓶颈;写分片、读 SUM。
  3. Hash 批量存储解决什么? 参考答案: 亿级视频 key 数量爆炸;一 key 多 field 省内存。

第六章完,共 40 题(123–162)。

持续学习,持续积累。