一、Checklist 不是许愿单
性能调优最常见的失败,不是“没有检查项”,而是“检查项都打了勾,事故还是发生了”。原因通常有三个:没有基线、只测平均值、只测正常路径。Checklist 的价值在于强迫团队在同一个问题上使用同一套证据,而不是提供一张可以交差的表格。
使用这份清单前,先约定四条规则:
- 先有基线:没有调优前的数据,就无法判断变化是优化还是波动。
- 一次只改一个变量:同时改连接池、GC 参数和序列化格式,结果无法归因。
- 记录环境:机器规格、内核参数、运行时版本、数据集大小、并发模型都要写进报告。
- 定义回滚:每个改动都要能在十分钟内撤回,并知道撤回后看哪个指标。
文中的阈值都标注为“经验值”,它们来自常见项目的量级,不是在本项目环境测得,也不能直接照搬。正确用法是把它当作起点,再用自己的基线校准。
指标口径也要提前统一。吞吐量是每秒处理多少请求还是每秒完成多少业务动作?延迟是从客户端发出算起,还是从服务端收到算起?错误率是否包含客户端主动取消和网络中断?同一个数字在不同口径下可能相差数倍。建议在报告首页写清楚四个定义:请求边界、成功判定、统计窗口、百分位算法。没有这四项,前后对比就没有意义。
还要区分“资源指标”和“体验指标”。CPU、内存、连接数是资源指标,能解释瓶颈;登录耗时、消息延迟、战斗结算时间是体验指标,才直接对玩家负责。调优时先用体验指标定位问题,再用资源指标找原因,顺序反了就容易陷入“CPU 降了但玩家更卡”的怪圈。
二、连接与网关
连接层是游戏服务端最先承压的地方。这里的错误往往不是“慢”,而是“直接不可用”:文件描述符耗尽、accept 队列溢出、心跳把连接全部踢掉。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| 文件描述符使用率 | ulimit -n 与实际打开数的比值 | 峰值低于 70%–80% | 新连接被拒绝,日志出现 too many open files |
| accept 队列 | 内核 somaxconn、应用 backlog 与压测连接速率 | 队列溢出计数接近 0 | 握手成功但服务端无响应 |
| 连接建立成功率 | 压测客户端统计成功/尝试 | 高于 99.9% | 玩家反复重连,形成重连风暴 |
| 单连接内存 | RSS 增量除以连接数 | 数十 KB 量级,随协议与缓冲区增长 | 连接数一高就 OOM |
| 心跳超时 | 客户端与服务端时间戳对齐 | 2–3 个心跳周期 | 弱网玩家被误踢 |
| 空闲连接回收 | 统计长时间无消息的连接数 | 按玩法设定,通常分钟级 | 僵尸连接占满配额 |
| 网关 CPU | 峰值时段的使用率与软中断分布 | 峰值低于 60%–70% | 无扩容余量,抖动即超时 |
| TLS 握手开销 | 握手耗时与 CPU 占用 | 与连接速率匹配,有会话复用 | 登录高峰握手排队 |
| 单帧大小上限 | 编解码层拒绝超限帧 | 按协议设定,通常远小于内存上限 | 恶意大包拖垮节点 |
| 连接迁移/重连 | 断网、切网、服务端重启演练 | 可恢复且不重复扣费 | 状态错乱与重复登录 |
连接数不能拍脑袋估算。一个玩家可能同时有登录连接、战斗连接和推送连接,重连期间还会短暂出现新旧连接并存;如果按“在线人数 = 连接数”做容量规划,峰值会明显偏低。规划时应分别估算稳定连接、峰值连接和故障恢复连接,并给文件描述符、内存与端口留出余量。
网关层要特别关注“重连风暴”。服务端重启或网络抖动后,大量客户端会在同一秒重连;如果登录、拉取数据、进入场景都是同步重操作,网关会在恢复瞬间再次被打满。常见缓解手段是随机退避、分批放行、缓存热点数据,以及把非关键初始化延后。
三、协议与序列化
协议层的性能问题通常被“网络慢”掩盖。实际原因可能是序列化占用过多 CPU、消息体过大、或者每次编解码都产生大量临时对象。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| 单条消息大小 | 统计 p50/p95/p99 字节数 | 按玩法设定,控制长尾 | 带宽与延迟同时上升 |
| 序列化 CPU 占比 | 剖面中编解码函数占比 | 经验上低于 20%–30% | 业务逻辑被挤占 |
| 每消息分配次数 | 基准测试的 allocs/op | 热路径尽量接近 0 | GC 压力与尾延迟上升 |
| 编解码延迟 | 微基准与线上采样 | p99 在毫秒级以内 | 高频消息堆积 |
| 压缩收益 | 压缩前后体积与 CPU 成本 | 收益明显且 CPU 可接受 | 压缩比低却付出高 CPU |
| 协议兼容 | 新旧客户端混跑、字段增删演练 | 旧客户端不崩、不误解 | 版本升级期间大面积异常 |
| 未知字段处理 | 构造多余字段与缺失字段 | 忽略未知、拒绝非法 | 解析错位或静默丢数据 |
| 消息上限 | 超长字符串、嵌套数组、深度攻击 | 有明确上限与错误码 | 内存放大与解析阻塞 |
| 数值精度 | 大整数、浮点、时间戳边界 | 与客户端一致 | 数值错乱与回档争议 |
测序列化时要把编码和解码分开统计。很多格式编码快、解码慢,或者在小消息上表现很好、在大消息上突然退化。还要用真实的消息分布测试,而不是只测一条最典型的请求;战斗广播、排行榜拉取、聊天消息的体积差异很大,平均值会掩盖长尾。
序列化选型不应只看编码速度。解码速度、兼容策略、跨语言支持、调试可读性和工具链同样重要。已有章节 Protobuf 与 FlatBuffers 讨论过不同格式的取舍,这里再强调一点:如果协议没有版本策略,任何格式都救不了升级事故。
四、并发与调度
并发层的核心不是“线程越多越好”,而是“排队时间可控”。游戏服务端常见模型包括每连接协程、固定 worker 池、分片事件循环和 Actor。不同模型的检查项不同,但都要回答同一个问题:请求在队列里等了多久。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| 可运行 goroutine/线程数 | 运行时指标与剖面 | 与 CPU 核数同量级,避免失控 | 调度开销压过业务 |
| 任务队列深度 | 队列长度与入队/出队速率 | 峰值不持续增长 | 延迟累积,最终超时 |
| 队列等待时间 | 从入队到开始执行的时间 | p99 低于业务超时的 1/3 | 请求在排队中已注定失败 |
| 锁竞争 | 锁等待时间与阻塞剖面 | 热路径无明显长等待 | 吞吐随核数增加反而下降 |
| 定时器精度 | 定时任务实际触发与计划偏差 | 按玩法容忍度设定 | 战斗结算、活动开启漂移 |
| 分片倾斜 | 各 worker 的负载方差 | 最热分片不长期打满 | 单核瓶颈拖累整体 |
| 阻塞调用 | 事件循环中是否出现同步 I/O | 热路径为 0 | 单点阻塞扩散为全节点卡顿 |
| 背压 | 下游变慢时是否拒绝或降级 | 有界队列与明确策略 | 内存无限增长后崩溃 |
| 优雅停机 | 停止接收、排空、超时强制 | 在发布窗口内完成 | 重启丢消息、玩家状态异常 |
并发模型没有绝对优劣。每连接协程写起来简单,但连接数极高时调度与内存成本要核算;固定 worker 池容易控制资源,但慢任务会阻塞同池请求;分片事件循环减少锁竞争,却带来分片倾斜和跨片通信问题。选择模型后,必须把“队列等待时间”作为一等指标,而不是只看 CPU。
五、内存与 GC
内存问题的危险在于它通常不是立刻崩溃,而是慢慢变差:分配率上升、GC 更频繁、尾延迟变高,最后在高峰时刻 OOM。检查时要区分“常驻内存”“临时分配”和“泄漏趋势”。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| RSS 增长趋势 | 每小时/每天采样并做线性拟合 | 稳定负载下接近水平 | 缓慢泄漏,最终 OOM |
| 堆目标与上限 | 运行时堆指标与容器限制 | 留有 20%–30% 余量 | 触发 OOM Kill 或频繁 GC |
| 分配速率 | 每秒分配字节数与对象数 | 热路径越低越好 | GC 周期缩短,CPU 被吞噬 |
| GC 暂停 | p99 暂停时间 | 按玩法容忍,通常低于帧预算 | 卡顿与超时 |
| 对象存活时间 | 剖面中的分配点与晋升 | 短生命周期对象不长期存活 | 老年代膨胀 |
| 缓存上限 | 缓存条目数与字节数 | 有明确上限与淘汰 | 缓存变成内存泄漏 |
| 大对象 | 单对象大小分布 | 避免频繁分配巨大缓冲 | 堆碎片与复制成本 |
| 缓冲区复用 | 是否使用池化或切片复用 | 热路径减少重复分配 | 分配风暴 |
| 容器内存限制 | 运行时是否感知 cgroup 限制 | 堆上限与容器一致 | 被内核杀死且无堆转储 |
出现内存异常时,先保留现场再重启。堆转储、分配剖面、goroutine/线程快照和最近一次 GC 日志,往往比“重启后恢复了”更有价值。如果容器会在 OOM 时直接杀进程,要提前配置转储路径和持久化卷,否则事故现场会随进程一起消失。
GC 调优不是“把参数调到最优”,而是先减少不必要的分配。一个每帧创建大量临时对象的战斗循环,靠调 GC 参数只能缓解症状。反过来,如果分配已经很低,GC 参数才有意义。还要注意运行时是否感知容器内存限制;很多“神秘 OOM”其实是堆上限按宿主机内存计算,远超容器配额。
六、数据库与缓存
数据库层的检查项要围绕“请求量、延迟、连接、锁、复制”展开。缓存则要围绕“命中率、热点、失效、一致性”。两者不能混为一谈:缓存命中率高不代表数据库没问题,可能只是把问题延后了。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| 查询 QPS 与 p99 | 按语句类型分别统计 | p99 在业务超时内 | 慢查询拖垮连接池 |
| 慢查询 | 慢日志阈值与执行计划 | 持续出现即需处理 | 锁等待与连接耗尽 |
| 连接池使用率 | 活跃/空闲/等待数 | 峰值低于 80%,等待接近 0 | 请求排队或连接泄漏 |
| 事务时长 | 从开始到提交的分布 | 短事务为主,长事务有上限 | 锁持有过久,阻塞写入 |
| 索引命中 | 执行计划与全表扫描次数 | 热查询走合适索引 | 数据量增长后突然变慢 |
| 缓存命中率 | 命中/总请求 | 按业务设定,热点应较高 | 请求穿透到数据库 |
| 热点 key | 单 key 的 QPS 与带宽 | 不超过单分片承载 | 单节点被打满 |
| 缓存失效策略 | TTL、主动失效、版本号 | 有明确一致性与兜底 | 脏数据与回档 |
| 复制延迟 | 主从延迟秒数 | 在可接受范围内 | 读到旧数据 |
| 备份与恢复 | 定期恢复演练 | 恢复时间满足 RTO | 事故时无法回滚 |
数据库性能的第一原则是“先看执行计划和数据量”。同一条 SQL 在测试库上很快,不代表在生产数据量下也快。第二原则是“连接池不是越大越好”,连接数超过数据库承载后,等待和上下文切换会让整体更慢。缓存则必须回答“失效后怎么办”,否则命中率越高,故障时的穿透越猛。
七、消息与队列
消息层的检查项围绕“积压、重试、顺序、幂等、死信”。游戏服务端常用消息处理跨服、异步任务、日志与补偿,一旦积压,表现可能是玩家操作延迟而不是消息系统报错。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| 消费延迟 | 最新消息时间与当前时间差 | 按业务 SLA 设定 | 玩家看到过期状态 |
| 队列积压 | 未消费消息数与增长斜率 | 不持续增长 | 内存或磁盘被占满 |
| 生产/消费速率 | 两者比值与峰值 | 消费能力有冗余 | 高峰必然积压 |
| 重试次数 | 单消息重试分布 | 有上限与退避 | 重试风暴放大故障 |
| 死信处理 | 死信数量与处理流程 | 有告警与人工/自动补偿 | 消息静默丢失 |
| 幂等 | 重复投递演练 | 重复不产生副作用 | 重复扣费、重复发奖 |
| 顺序 | 同一实体消息是否保序 | 关键实体有序 | 状态覆盖错乱 |
| 分区倾斜 | 各分区吞吐与积压 | 热点分区不长期打满 | 单分区成为瓶颈 |
| 消息大小 | 大消息占比与上限 | 大消息走对象存储或拆分 | 网络与存储放大 |
| 持久化 | 刷盘策略与故障演练 | 满足丢失容忍度 | 宕机丢消息 |
消费能力不足时,第一反应通常是加消费者,但分区数决定了并行度的上限。如果分区数少于消费者数,多出来的实例只能空闲;如果分区倾斜严重,加机器也救不了热分区。扩容前先看每个分区的积压与吞吐,确认瓶颈在消费者、分区还是下游存储。
消息系统最常见的错误,是把“至少一次”当成“恰好一次”。只要存在重试,就必须有幂等键;只要存在多分区,就必须明确哪些操作需要保序。积压告警不能只看队列长度,还要看增长斜率:一个稳定在十万条的队列可能是正常的,一个五分钟内从零涨到一万条的队列才是事故前兆。
八、日志与监控
日志与监控不是“上线后再补”的附属品,而是性能调优的测量工具。检查重点是:指标是否能回答问题、日志是否可承受、告警是否可行动。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| 关键指标覆盖 | 连接、延迟、错误、队列、资源 | 每个核心链路都有 | 出事时只能猜 |
| 指标基数 | 标签组合数量 | 控制在存储可承受范围 | 监控系统被拖垮 |
| 日志量 | 每秒字节数与磁盘增长 | 有采样与轮转 | 磁盘写满,服务停摆 |
| 日志字段 | 连接、命令、耗时、结果码 | 可定位到单次请求 | 只能看到“出错了” |
| 采样策略 | 正常/异常/慢请求分别采样 | 异常与慢请求不丢 | 关键现场被采样掉 |
| 链路追踪 | 跨服务上下文传播 | 核心链路可串起来 | 无法定位慢在哪一跳 |
| 告警质量 | 告警是否对应可执行动作 | 每条告警有负责人 | 告警疲劳,真事故被忽略 |
| 仪表盘 | 是否能回答容量与故障问题 | 核心指标一屏可见 | 排障时到处找数据 |
| 日志脱敏 | token、手机号、聊天内容 | 写入前脱敏 | 隐私与合规风险 |
| 保留周期 | 热/温/冷分层 | 满足审计与排障 | 事故复盘时数据已删 |
日志与指标的保留周期要和排障需求匹配。只保留三天,可能撑不到一次跨周复盘;保留一年又不分层,成本会失控。常见做法是热数据保留数天供实时查询,温数据保留数周供复盘,冷数据归档到对象存储。无论哪一层,都要保证按键和时间可检索,否则“有数据”等于“没有数据”。
监控的第一目标是“缩短定位时间”,不是“收集尽可能多的数据”。一个只有 CPU 和内存的仪表盘,无法回答“是网关慢还是逻辑慢”;一个标签爆炸的指标体系,可能在故障时先把自己拖垮。日志同理:结构化字段比长文本更有用,采样与脱敏必须在写入前完成。
九、发布与容量
发布阶段的性能问题往往来自“变化”:新版本更耗内存、连接没有优雅迁移、缓存冷启动、自动扩缩容反应太慢。检查项要覆盖发布前、发布中和发布后。
| 检查项 | 判定方法 | 经验阈值区间 | 不通过后果 |
|---|---|---|---|
| 容量余量 | 峰值负载与当前容量比值 | 保留 30%–50% 余量 | 一点波动就过载 |
| 压测模型 | 是否模拟真实连接与消息比例 | 与生产画像一致 | 压测通过但上线即崩 |
| 冷启动 | 新实例从启动到可服务的耗时 | 在扩缩容窗口内 | 扩容赶不上流量增长 |
| 预热 | 缓存、连接池、JIT 是否预热 | 核心路径已预热 | 新实例前期延迟高 |
| 灰度 | 按比例/地域/玩家分批 | 有明确放量与回滚条件 | 问题扩散到全量 |
| 回滚 | 版本与配置能否快速回退 | 十分钟内可完成 | 故障时长翻倍 |
| 连接迁移 | 发布时旧连接如何处置 | 优雅排空或重连可控 | 重连风暴 |
| 自动扩缩容 | 触发指标与冷却时间 | 能跟上流量斜率 | 扩容滞后于高峰 |
| 依赖容量 | 数据库、缓存、队列是否同步扩容 | 无单点瓶颈 | 应用扩容后下游先崩 |
| 发布后观察 | 关键指标观察窗口 | 至少覆盖一个高峰 | 问题在低峰被掩盖 |
压测的加压方式也要写清楚:是瞬间打到目标并发,还是分阶段爬坡。瞬间加压适合验证极限,分阶段爬坡更接近真实开服;两者得到的结果不能混用。至少要记录每个阶段的连接数、消息速率、延迟分位和错误率,才能判断系统是在哪个拐点开始退化。
容量评估不能只看应用层。应用扩容三倍,数据库连接数和缓存带宽也会随之上升;如果下游没有同步准备,扩容只会把瓶颈前移。发布时还要考虑“连接是有状态的”:滚动重启如果直接杀进程,玩家会同时重连,恢复瞬间的负载可能高于平时。
十、压测前后对照清单
压测报告至少要有“调优前”和“调优后”两列,并且使用同一套场景、同一份数据、同一批机器。报告开头要写清楚“本次要回答的问题”,例如“连接数从一万提升到五万时,p99 是否仍低于 200ms”。没有问题陈述,数据再多也无法形成结论。
下面是一个可以直接复用的对照表模板。
| 指标 | 调优前 | 调优后 | 目标 | 结论 | 备注 |
|---|---|---|---|---|---|
| 连接建立成功率 | 待填 | 待填 | ≥ 99.9% | 通过/不通过 | 记录客户端与服务端统计口径 |
| 网关 p99 延迟 | 待填 | 待填 | 按玩法设定 | 通过/不通过 | 区分网络与处理耗时 |
| 业务错误率 | 待填 | 待填 | ≤ 0.5% | 通过/不通过 | 排除客户端主动取消 |
| 单节点连接数 | 待填 | 待填 | 达到目标容量 | 通过/不通过 | 记录内存与 CPU |
| CPU 峰值 | 待填 | 待填 | 低于 70% | 通过/不通过 | 记录软中断分布 |
| 堆内存峰值 | 待填 | 待填 | 低于上限 70% | 通过/不通过 | 记录 GC 次数 |
| GC p99 暂停 | 待填 | 待填 | 低于帧预算 | 通过/不通过 | 区分新生代/老年代 |
| 队列最大深度 | 待填 | 待填 | 不持续增长 | 通过/不通过 | 记录积压持续时间 |
| 数据库 p99 | 待填 | 待填 | 在超时内 | 通过/不通过 | 记录慢查询数量 |
| 缓存命中率 | 待填 | 待填 | 按业务设定 | 通过/不通过 | 记录热点 key |
| 消息消费延迟 | 待填 | 待填 | 满足 SLA | 通过/不通过 | 记录积压斜率 |
| 故障恢复时间 | 待填 | 待填 | 满足 RTO | 通过/不通过 | 演练杀进程/断网 |
百分位的计算方式要在两张表里保持一致。常见错误是调优前用固定时间窗口统计,调优后改成按请求统计,结果 p99 看起来大幅改善,实际只是口径变了。压测工具如果存在 coordinated omission,即客户端在等待响应时没有继续按计划发请求,测出的延迟会偏乐观;长连接游戏尤其容易踩这个坑。报告里应说明是否使用开环加压、是否记录未完成请求。
对照表要保留原始数据与采集命令,不能只留下结论。否则下一次调优时,团队无法判断“当时的 p99 是在什么连接数、什么消息比例下测出来的”。
十一、上线前 30 分钟检查清单
上线前的检查目标不是“再优化一点”,而是“确认可观测、可回滚、可恢复”。下面这张表按时间倒排,负责人和证据必须填写。
| 时间 | 动作 | 负责人 | 证据 |
|---|---|---|---|
| T-30 | 确认发布版本与配置版本一致 | 发布负责人 | 版本号与构建记录 |
| T-28 | 检查数据库迁移是否已执行且可回滚 | 后端负责人 | 迁移日志与回滚脚本 |
| T-26 | 确认容量余量与扩缩容策略 | 运维负责人 | 当前负载与阈值截图 |
| T-24 | 核对告警规则与值班名单 | 值班负责人 | 告警测试记录 |
| T-22 | 检查日志、指标、追踪是否正常上报 | 可观测负责人 | 最近十分钟数据 |
| T-20 | 确认灰度比例与回滚命令 | 发布负责人 | 发布单 |
| T-18 | 验证关键依赖(数据库、缓存、队列)健康 | 运维负责人 | 健康检查输出 |
| T-15 | 通知客服与运营,准备玩家公告 | 运营负责人 | 公告链接 |
| T-12 | 冻结非必要变更 | 技术负责人 | 变更系统记录 |
| T-10 | 最后一次冒烟:登录、进场景、战斗、支付回调 | 测试负责人 | 冒烟记录 |
| T-5 | 确认监控大屏与排障入口可访问 | 值班负责人 | 访问截图 |
| T-0 | 开始灰度,观察错误率与延迟 | 发布负责人 | 灰度看板 |
如果任何一项没有证据,就不应该开始发布。上线前 30 分钟不是用来“再改一版”的,而是用来确认“出问题时知道怎么办”。
可以用一份 YAML 把性能门禁固定下来,接入发布流水线或人工评审。阈值只是模板,必须按项目基线调整。
performance_gate:
baseline_required: true
environment: staging
metrics:
connect_success_rate:
target: ">= 0.999"
gateway_p99_latency_ms:
target: "<= 200"
business_error_rate:
target: "<= 0.005"
cpu_peak_ratio:
target: "<= 0.70"
heap_peak_ratio:
target: "<= 0.70"
queue_growth:
target: "no sustained growth"
failure_injection:
required:
- kill_instance
- disconnect_network
- downstream_timeout
rollback:
max_minutes: 10
verified: true
门禁的意义是让“是否达标”可复核,而不是让流水线替团队承担判断。指标口径、采样窗口和失败注入场景都要写在报告里,否则同一份 YAML 可以被不同的人解释成不同结论。
十二、失败场景:全绿的 Checklist 为什么没救回事故
某项目上线前完成了全部检查:连接成功率、CPU、内存、数据库延迟都在阈值内,压测报告全绿。上线当晚,一次机房网络抖动导致大量玩家掉线,随后客户端同时重连,网关在几分钟内不可用。事后复盘发现,Checklist 里的每一项都没错,但测的都是“稳态”。
| 遗漏点 | 当时的数据 | 实际问题 | 改进动作 |
|---|---|---|---|
| 只测平均延迟 | 平均 30ms,p99 未单独看 | 重连时 p99 超过 2s | 所有核心指标增加 p99/p999 |
| 压测用短连接 | 连接建立快、复用少 | 生产是长连接,重连成本高 | 压测模型改为长连接与断线重连 |
| 没有故障注入 | 正常路径全通过 | 网络抖动后重连风暴 | 增加断网、杀进程、下游超时演练 |
| 没有重连退避 | 客户端立即重试 | 同一秒请求打满网关 | 客户端随机退避、服务端分批放行 |
| 告警只看 CPU | CPU 未超阈值 | 连接队列已溢出 | 增加 accept 队列与重连速率告警 |
事故的根因不是某个指标超标,而是“测试场景与真实故障不匹配”。稳态数据只能证明系统在正常输入下可用,不能证明它在抖动、重连、依赖变慢时仍然可用。性能 Checklist 必须包含失败路径,否则它只是一份上线安慰剂。
小结
性能调优的八层检查可以概括为一句话:先建立基线,再逐层找瓶颈,最后用故障场景验证。连接层看容量与重连,协议层看体积与分配,并发层看排队,内存层看趋势,数据库层看执行计划与连接池,消息层看积压与幂等,监控层看可行动性,发布层看余量与回滚。所有阈值都是经验起点,必须用项目自己的数据校准。Checklist 的终点不是打勾,而是让团队在事故发生时能快速回答“哪里慢、为什么慢、怎么恢复”。
常见错误与边界
- 把 Checklist 当形式:只打勾不留证据,评审时无法判断结论是否成立。
- 只检查不修复:发现慢查询、队列增长却不排期处理,清单反而掩盖风险。
- 阈值照搬别的项目:别人的 p99 目标、连接数和机器规格与你不一定相同。
- 只看平均值:平均值正常不代表尾延迟正常,游戏体验由 p99 甚至 p999 决定。
- 只测稳态:没有断网、重启、依赖超时和重连风暴的压测,不能证明故障可恢复。
- 一次改多个变量:无法归因,也无法判断哪个改动真正有效。
- 监控指标越多越好:高基数标签会拖垮存储,告警过多会导致疲劳。
- 上线前临时调参:没有基线、没有回滚的改动,往往把小问题变成大事故。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。