本附录把卷二出现的阈值集中在一处。使用前请先看两句话:
- 标「代码常量」的值,来自实验工程,可以直接在对应包中找到;它们是演示用的起点,不是生产建议。
- 标「案例假设」的值,是为让成本计算可以算一遍而设定的数量级,换产品要重新标定。
任何参数都不该在没有基线的情况下上线。先有指标,再调参数;否则调完之后没人能证明变好还是变坏。
内容分层参数
第 1 章的 content.TierOf 用访问行为而不是创建时间决定存储层。
| 参数 | 值 | 类型 | 含义 |
|---|---|---|---|
| 热层天数 | 7 天 | 代码常量 | 最近 7 天读过即 hot |
| 热层次数 | 30 天内 10 次 | 代码常量 | 高频访问也进 hot |
| 温层天数 | 90 天 | 代码常量 | 90 天内读过进 warm |
| 冷层 | 其余 | 代码常量 | 长期未访问进 cold |
这两个条件取「或」:只要满足其中一个就进热层。原因是内容的热度有两种形态,一种是最近被读,一种是被反复读。只看其中一个,会漏掉另一种。
分层的目的是控制存储成本与读取延迟,不是给内容贴标签。如果冷层存储和热层成本差不多,就不值得做分层;先算一遍「迁移节省的存储费」是否大于「迁移任务的开发与运维成本」。
审核时限参数
第 2 章的审核链路有几个时间上限,它们直接影响用户体验。
| 参数 | 建议起点 | 类型 | 说明 |
|---|---|---|---|
| 机审超时 | 2 秒 | 案例假设 | 超时按「转人审」处理,不直接拒绝 |
| 人审 SLA | 30 分钟 | 案例假设 | 高峰期可放宽到 2 小时,但要告知用户 |
| 申诉时限 | 7 天 | 案例假设 | 从拒绝时间起算 |
| 复审抽样率 | 1% | 案例假设 | 按审核员与内容类型分层抽样 |
| 审核结论缓存 | 24 小时 | 案例假设 | 同一作者同一类型短期重复内容可复用 |
机审超时不能设得太短。设成 200 毫秒,会把大量正常内容推给人审,人审队列立刻积压;设成 10 秒,用户发布等待时间不可接受。2 秒是一个常见折中,但要用自己的机审 p99 来定。
申诉时限的意义不是「让用户申诉」,而是「让误判有出口」。时限太短,用户来不及反应;太长,审核团队要维护大量历史记录。7 天是多数内容产品的常见值。
Feed 推拉选择参数
第 3 章的 feed.Choose 按粉丝数选择默认策略。
| 参数 | 值 | 类型 | 含义 |
|---|---|---|---|
| 推模式上限 | 粉丝 < 1000 | 代码常量 | 小账号写扩散 |
| 拉模式下限 | 粉丝 > 100000 | 代码常量 | 大账号读聚合 |
| 混合区间 | 1000–100000 | 代码常量 | 一半推、一半拉 |
| 写成本 | 0.5 毫秒/份 | 案例假设 | 单次收件箱写入 |
| 读成本 | 0.2 毫秒/源 | 案例假设 | 单次来源合并 |
这三个门槛是案例假设,不是行业标准。真实选择要看三个量:粉丝数分布、刷新频率、存储成本。如果产品里 90% 的用户粉丝数小于 100,推模式的总写入量可能完全可以接受;如果头部作者粉丝数达到百万,推模式的写入放大就会失控。
FanoutCost 给出的成本是线性估算,忽略了批量写入、管道、缓存命中等优化。它的用途是比较数量级,不是预测真实延迟。
Feed 排序参数
第 4 章的 rank.Score 用四类信号加权,时间按 1/(1+ageHours) 衰减。
| 信号 | 权重起点 | 类型 | 说明 |
|---|---|---|---|
| 时间 | 1.0 | 案例假设 | 衰减项,1 小时内容得 0.5 |
| 点赞 | 0.02 | 案例假设 | 50 赞约等于 1 个时间单位 |
| 评论 | 0.05 | 案例假设 | 评论比点赞重,但权重不能过高 |
| 亲和 | 0.5 | 案例假设 | 作者与读者的历史互动强度,取值 0–1 |
权重之和必须为正,单项必须非负。代码会拒绝全零权重,因为那会让所有内容同分,排序退化成随机。
调权重时一次只动一个,并且先定评价指标。常见的指标是「完读率」「互动率」「次日留存」,三个指标往往互相冲突:提高时间权重会让新内容更多,但可能降低互动率;提高亲和权重会让熟人内容更多,但可能降低新作者的曝光。
没有实验分桶能力时,不要凭感觉调权重。第 4 章讲过,分桶要稳定、指标要预先定义、观察期要覆盖至少一个完整周期。
关系与互动参数
第 5、6 章涉及的阈值主要影响体验与成本。
| 参数 | 建议起点 | 类型 | 说明 |
|---|---|---|---|
| 关注上限 | 5000 | 案例假设 | 超过后提示「先取关」 |
| 好友上限 | 1000 | 案例假设 | 双向关系,成本更高 |
| 拉黑上限 | 500 | 案例假设 | 防止滥用,也控制判定链长度 |
| 点赞频控 | 60 次/分钟 | 案例假设 | 按用户维度 |
| 评论频控 | 20 次/分钟 | 案例假设 | 按用户 + 内容维度 |
| 计数漂移容忍 | 5% | 案例假设 | counter.Evaluate 的 tolerance 上限为 1 |
频控用滑动窗口,不用固定分桶。固定分桶在窗口边界会放过两倍流量:用户在 59 秒和 61 秒各发 60 次,固定分钟桶会全部放行。
计数漂移容忍度取决于指标。点赞数漂移 5% 用户几乎感知不到;收藏数漂移 5% 可能影响用户决策;金额类计数不能有漂移,必须强一致。
评论折叠参数
第 7 章的 comment.FoldRule 控制折叠,不删除。
| 参数 | 建议起点 | 类型 | 说明 |
|---|---|---|---|
| 折叠起始深度 | 3 | 代码常量(可配) | 根评论深度 0,回复从 1 起 |
| 深层保留点赞数 | 5 | 代码常量(可配) | 达到深度且点赞不足则折叠 |
| 最大展示深度 | 5 | 案例假设 | 超过后显示「展开更多」 |
| 单页评论数 | 20 | 案例假设 | 按内容与排序方式调整 |
折叠规则要满足两个条件:根评论永不折叠(否则用户看不到讨论的起点);折叠不删除节点(否则被折叠者的申诉无从查起)。
BlockedAuthors 用于把被拉黑作者的非根评论连同子树一起折叠。这个名单应该在查询时实时计算,而不是缓存太久;用户解除拉黑后,折叠状态要能恢复。
IM 消息参数
第 8、9 章的参数决定消息能不能被正确排序、去重与补偿。
| 参数 | 建议起点 | 类型 | 说明 |
|---|---|---|---|
| 群成员上限 | 500 | 案例假设 | 超过后改用频道模型 |
| 消息去重 TTL | 300 秒 | 案例假设 | delivery.Window.TTL,单位秒 |
| 在线心跳 TTL | 90 秒 | 案例假设 | 超过视为离线 |
| 已读上报节流 | 2 秒 | 案例假设 | 避免高频写已读表 |
| 消息保留期 | 180 天 | 案例假设 | 到期归档,不物理删除 |
| 单次拉取条数 | 50 | 案例假设 | 漫游分页上限 |
去重 TTL 要大于「客户端重发 + 网关重试 + 消费重试」的最大时间跨度。设成 30 秒,一次网络抖动后的重发就可能绕过窗口;设成 24 小时,窗口内存或存储成本会很高。5 分钟是常见折中。
在线心跳 TTL 要与客户端心跳间隔配合。客户端 30 秒一次心跳,服务端 TTL 设 90 秒,可以容忍两次丢包。TTL 设得比心跳间隔还短,会把正常连接判成离线。
已读序号按设备存,读时取最大值。不要按设备求和,也不要在客户端做合并;多端合并的逻辑必须在一个地方,否则每个端都会写出自己的版本。
推送参数
第 10 章的推送链路有几个硬约束来自渠道,不是产品能随便定的。
| 参数 | 建议起点 | 类型 | 说明 |
|---|---|---|---|
| APNs 包体上限 | 4 KB | 渠道约束 | 超过会被拒收 |
| FCM 包体上限 | 4 KB | 渠道约束 | 数据消息另有上限 |
| 小程序订阅消息 | 按模板字段 | 渠道约束 | 不能自由拼接长文本 |
| 营销推送频控 | 3 条/天 | 案例假设 | 按用户维度 |
| 事务通知频控 | 不限制条数 | 案例假设 | 但要聚合同一会话 |
| 重试次数 | 3 次 | 案例假设 | 指数退避,间隔 1/5/30 秒 |
| 降级链 | 推送 → 站内信 → 短信 | 案例假设 | 短信只用于高优先级 |
渠道包体上限是硬约束,不是建议。模板渲染后要按渠道裁剪,而不是把一套长文案发给所有渠道。小程序订阅消息尤其严格:模板字段、字数、跳转路径都要提前申请。
事务通知不限制条数,但必须聚合。用户收到「A 评论了你」「B 评论了你」「C 评论了你」三条推送,体验远差于一条「3 人评论了你」。聚合窗口建议 5–15 分钟,太短起不到聚合作用,太长会让用户觉得通知延迟。
搜索与推荐参数
第 11、12 章的参数主要影响一致性与兜底。
| 参数 | 建议起点 | 类型 | 说明 |
|---|---|---|---|
| 索引滞后预算 | 60 秒 | 案例假设 | 超过后告警 |
| 删除生效目标 | 5 分钟 | 案例假设 | 写进产品文案 |
| 召回条数 | 500 | 案例假设 | 排序前候选集 |
| 排序返回条数 | 20 | 案例假设 | 首屏 |
| 推荐兜底链 | 个性化 → 热榜 → 最新 | 案例假设 | 每级都有明确触发条件 |
| 特征缓存 TTL | 10 分钟 | 案例假设 | 在线特征 |
| 冷启动观察期 | 7 天 | 案例假设 | 新用户/新内容 |
索引滞后不是 bug,是架构属性。产品预期要写清楚「删除后最长 5 分钟从搜索消失」,而不是承诺「立即消失」。如果业务要求立即消失,读路径要先查主库状态,再展示索引结果。
推荐兜底链的每一级都要有触发条件:个性化返回空、超时、特征缺失、新用户。没有触发条件的兜底等于没有兜底,因为没人知道什么时候该用。
容量换算
把参数代进容量公式时,注意单位。
| 量 | 换算 | 常见错误 |
|---|---|---|
| 1 天 | 86400 秒 | 用 100000 秒近似 |
| 1 TB | 1024 GB | 与十进制 TB 混用 |
| 峰值系数 | 3–5 倍均值 | 用均值做容量规划 |
| 写放大 | 粉丝数 × 每份字节 | 忽略副本与索引 |
| 读放大 | 来源数 × 每次查询 | 忽略缓存未命中 |
峰值系数是最容易被低估的参数。内容社区的流量高峰通常出现在晚间和突发事件,峰值可能是均值的 3–5 倍。按均值规划容量,高峰一定撑不住。
写放大要算三部分:收件箱条目、索引文档、缓存副本。只算收件箱,会低估一半以上的存储。
参数标定流程
参数不是拍脑袋定的,也不是一次定死的。建议的标定流程是:
- 定指标。 这个参数影响哪个指标?延迟、成本、准确率、还是用户体验?
- 取基线。 上线前先测一遍当前值,没有基线就没有「变好」。
- 小流量验证。 用 1%–5% 流量验证方向,不要一次全量。
- 观察完整周期。 至少覆盖一个工作日 + 一个周末,内容产品的周期性强。
- 记录取舍。 这个参数调大,什么变好、什么变差,写进决策记录。
第 5 步最容易被跳过。参数调整往往有多目标冲突,只记录「调了之后指标 A 涨了」,不记录「指标 B 跌了」,下次遇到同样问题还会再试一遍。
不同规模下的参数倾向
同一组参数在百万级和千万级产品里往往要往相反方向调。下面这张表给出趋势,不是具体数值。
| 参数 | 百万级倾向 | 千万级倾向 | 原因 |
|---|---|---|---|
| 推拉门槛 | 门槛可以低一些 | 门槛必须提高 | 头部粉丝数变大,写扩散成本非线性上升 |
| 收件箱写入 | 同步可接受 | 必须异步 + 合并 | 单次发布扩散量超出同步预算 |
| 计数更新 | 直接写缓存 | 批量聚合 + 对账 | 高频写会打满热点分片 |
| 已读上报 | 每次上报 | 节流 + 合并 | 多端高频写会放大存储压力 |
| 审核 | 机审为主 | 分层机审 + 人审抽检 | 内容量超出人工处理能力 |
| 搜索同步 | 双写可接受 | 必须走变更日志 | 双写失败会留下长期不一致 |
| 推送 | 按事件即时发 | 聚合 + 频控 | 单用户事件量变大,必须合并 |
| 通知降级 | 站内信即可 | 分优先级 + 短信 | 不同通知的商业价值差异变大 |
读这张表时要问自己一个问题:我的瓶颈现在在哪? 如果瓶颈是开发人力,参数应该偏保守,先用简单方案;如果瓶颈是成本,参数应该偏激进,尽早引入合并与分层;如果瓶颈是体验,参数应该偏实时,接受更高的成本。
参数与监控指标的对应
每个参数都应该有至少一个监控指标,否则调了之后无法判断效果。
| 参数 | 监控指标 | 异常信号 |
|---|---|---|
| 机审超时 | 机审 p99、转人审率 | p99 接近超时值、转人审率突然上升 |
| 人审 SLA | 审核队列长度、超时率 | 队列持续增长、超时率超过 5% |
| 推拉门槛 | 收件箱写入 QPS、刷新 p99 | 写入 QPS 随发布量线性上涨 |
| 排序权重 | 点击率、完读率、多样性 | 单一作者占比过高、内容同质化 |
| 频控窗口 | 触发次数、误伤率 | 正常用户被拦截、刷子未被拦截 |
| 去重 TTL | 重复消息率、窗口命中率 | 重复率上升、窗口内存超限 |
| 在线 TTL | 误判离线率、投递失败率 | 正常用户被频繁判离线 |
| 推送重试 | 到达率、降级率 | 到达率下降、降级到短信的比例上升 |
| 索引滞后 | 同步延迟 p99、删除生效时间 | 延迟持续超过预算、删除后仍可搜到 |
| 推荐兜底 | 兜底触发率、兜底点击率 | 兜底触发率上升说明主路径不稳定 |
指标的选择要能回答问题,而不是「看起来专业」。比如「收件箱写入 QPS」比「系统吞吐」有用,因为它直接对应推模式的成本;「删除生效时间」比「索引延迟」有用,因为它是用户能感知的承诺。
如果某个参数没有对应指标,要么补指标,要么承认它是拍脑袋定的。后者不丢人,但要写进决策记录,方便以后推翻。
参数变更记录模板
每次调整参数,建议在决策记录里留下下面几行。模板很短,但它能避免「半年后没人记得为什么是这个值」。
参数:feed.Choose 推模式下限
旧值:1000 粉丝
新值:3000 粉丝
原因:头部作者占比上升,1000–3000 区间的写扩散成本超过读聚合
影响:小作者内容进入收件箱的延迟从秒级变为刷新时合并
观察指标:收件箱写入 QPS、首页刷新 p99、小作者内容曝光量
回滚条件:刷新 p99 上升超过 20%,或小作者曝光量下降超过 10%
观察期:14 天
记录里最重要的是「影响」和「回滚条件」。只写「因为性能问题调整」,等于没写;没有回滚条件,出问题时只能凭感觉决定是否改回去。
常见参数错误
最后列几个在评审中常见的参数错误。
- 把超时设成「比下游 p99 大一点」。 下游 p99 会随负载变化,超时应该按「业务能接受的最长等待」定,而不是按下游表现定。
- 把重试次数设成「越多越可靠」。 重试会放大流量,下游已经过载时,重试会让它彻底崩掉。重试必须有上限、退避和熔断。
- 把缓存 TTL 设成「越长越省」。 TTL 越长,数据越旧;对可见性、删除、审核状态这类数据,旧数据就是错误数据。
- 把频控设成「一个全局值」。 营销、事务、互动三类通知的频控逻辑不同,用同一个阈值会误伤。
- 把分页大小设成「越大越少请求」。 分页越大,单次查询越慢,超时风险越高;深度分页还会拖垮数据库。
- 把阈值当成永久值。 产品阶段变了,阈值要重标;把一年的旧阈值沿用到新阶段,是最常见的隐性故障源。
这些错误的共同点是:它们都在某个局部看起来合理,但忽略了系统其他部分的变化。参数表的用途不是让你照抄,而是让你知道「这里有一个需要持续标定的决策点」。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。