系统稳定性建设:监控告警、容量规划与大促保障

速查导航

阅读时间: 45 分钟 | 难度: ⭐⭐⭐⭐ | 面试频率: 中高

任务速查:


前言:怎样的系统算是稳定高可用的

首先回答另一个问题,怎样的系统算是稳定的?

Google SRE 用服务可靠性层级模型描述可靠性基础与高层次需求(Dickerson’s Hierarchy of Service Reliability),如下图。[1]


该模型由Google SRE工程师Mikey Dickerson在2013年提出,将系统稳定性需求按照基础程度进行了不同层次的体系化区分,形成稳定性标准金字塔模型:

  • 金字塔的底座是监控(Monitoring),这是一个系统对于稳定性最基础的要求,缺少监控的系统,如同蒙上眼睛狂奔的野马,无从谈及可控性,更遑论稳定性。
  • 更上层是应急响应(Incident Response),从一个问题被监控发现到最终解决,这期间的耗时直接取决于应急响应机制的成熟度。合理的应急策略能保证当故障发生时,所有问题能得到有序且妥善的处理,而不是慌乱成一锅粥。
  • 事后总结以及根因分析(Postmortem&Root Caue Analysis),即我们平时谈到的“复盘”,虽然很多人都不太喜欢这项活动,但是不得不承认这是避免我们下次犯同样错误的最有效手段,只有当摸清故障的根因以及对应的缺陷,我们才能对症下药,合理进行规避。
  • 测试和发布管控(Testing&Release procedures),大大小小的应用都离不开不断的变更与发布,有效的测试与发布策略能保障系统所有新增变量都处于可控稳定区间内,从而达到整体服务终态稳定
  • 容量规划(Capacity Planning)则是针对于这方面变化进行的保障策略。现有系统体量是否足够支撑新的流量需求,整体链路上是否存在不对等的薄弱节点,都是容量规划需要考虑的问题。
  • 位于金字塔模型最顶端的是产品设计(Product)与软件研发(Development),即通过优秀的产品设计与软件设计使系统具备更高的可靠性,构建高可用产品架构体系,从而提升用户体验

系统稳定性建设概述


从金字塔模型我们可以看到构建维护一个高可用服务所需要做到的几方面工作:

  • 产品、技术、架构的设计,高可用的架构体系
  • 系统链路&业务策略梳理和维护(System & Biz Profiling)
  • 容量规划(Capacity Planning)
  • 应急响应(Incident Response)
  • 测试
  • 事后总结(Testing & Postmortem)
  • 监控(Monitoring)
  • 资损体系
  • 风控体系
  • 大促保障
  • 性能优化


监控&告警梳理 – Monitoring

站在监控的角度看,我们的系统从上到下一般可以分为三层:业务(Biz)、应用(Application)、系统(System)。系统层为最下层基础,表示操作系统相关状态;应用层涵盖应用进程、运行时(如 JVM 或 Go runtime)与中间件运行状态;业务层为最上层,为业务视角下服务对外运行状态。因此进行大促稳定性监控梳理时,可以先脱离现有监控,先从核心、资损链路开始,按照业务、应用(中间件、JVM、DB)、系统三个层次梳理需要哪些监控,再从根据这些索引找到对应的监控告警,如果不存在,则相应补上;如果存在则检查阈值、时间、告警人是否合理。

监控

Google SRE 的四项黄金信号是延迟(Latency)、错误(Errors)、流量(Traffic)与饱和度(Saturation),可用于梳理服务监控。[2] 各层监控指标可以据此分类,具体如下:


告警

是不是每项监控都需要告警?答案当然是否定的。建议优先设置Biz层告警,因为Biz层我们对外服务最直观业务表现,最贴切用户感受。Application&System层指标主要用于监控,部分关键&高风险指标可设置告警,用于问题排查定位以及故障提前发现。对于一项告警,我们一般需要关注级别、阈值、通知人等几个点。

  1. 级别
    即当前告警被触发时,问题的严重程度,一般来说有几个衡量点:
  • 是否关联NOC
  • 是否产生严重业务影响
  • 是否产生资损
  1. 阈值
  • 即一项告警的触发条件&时间,需根据具体场景合理制定。一般遵循以下原则:
  • 按影响和可操作性分级,不要求每个异常都通知人。一般诊断信号进入指标或日志;需要处理但不紧急的问题进入工单;正在造成明显用户影响、资损,或即将突破明确服务目标且需要立即干预的问题,才触发即时告警。即时告警应有责任人、处理手册和可以执行的止血措施。[2]
  • 同时控制漏报与噪声。阈值、持续窗口和服务目标应结合实际负载验证;采用去重、聚合和已知维护期间的抑制,避免同一次故障反复通知。频繁告警应复核真实影响、检测规则和处置效果,不能仅靠调高阈值隐藏故障。
  • 若单一指标无法反馈覆盖整体业务场景,可结合多项指标关联构建。
  • 需符合业务波动曲线,不同时段可设置不同条件&通知策略。
  1. 通知人&方式
  • 通知方式由严重程度、响应时限和责任人决定,不能仅按 Biz、Application 或 System 层划分。需要立即干预的问题通知值班人员,并按超时升级规则处理;不紧急的整改进入工单,诊断指标保留在仪表盘与日志中。
  • 业务负责人是否参与通知,取决于用户影响、业务决策和事故协同需要;基础设施异常若已经影响核心链路,也可能需要即时响应。

应产出数据

完成该项梳理工作后,我们应该产出以下数据:

  1. 系统监控模型,格式同表1
  • Biz、Application、System 分别存在哪些待监控点
  • 监控点是否已全部存在指标,仍有哪些待补充
  1. 系统告警模型列表,需包含以下数据
  • 关联监控指标(链接)
  • 告警关键级别
  • 是否推送GOC
  • 是否产生资损
  • 是否关联故障
  • 是否关联预案
  1. 业务指标大盘,包含Biz层重点监控指标数据
  2. 系统&应用指标大盘,包含核心系统关键系统指标,可用于白盒监控定位问题。

高可用的架构设计

高可用架构是系统稳定性的基石。一个好的架构设计能够从根本上减少故障发生的概率,并在故障发生时将影响范围降到最低。

高可用架构核心原则

冗余设计(Redundancy)

冗余是实现高可用的基础手段,通过部署多个相同的组件来消除单点故障。

冗余层级 实现方式 典型场景
应用层冗余 多实例部署 + 负载均衡 Web 服务、API 网关
数据层冗余 主从复制、多副本 MySQL 主从、Redis Cluster
机房冗余 同城双活、异地多活 核心交易系统
网络冗余 多运营商、多链路 CDN、DNS

冗余度计算:
假设单节点可用性为 99%,双节点冗余后可用性 = 1 - (1-0.99)² = 99.99%

解耦设计(Decoupling)

1
2
3
4
5
6
7
8
9
10
紧耦合架构                    解耦架构
┌─────────────────┐ ┌─────────────────┐
│ 服务 A │ │ 服务 A │
│ ↓ │ │ ↓ │
│ 服务 B │ → │ 消息队列 │
│ ↓ │ │ ↓ │
│ 服务 C │ │ 服务 B/C │
└─────────────────┘ └─────────────────┘
任一服务故障 服务故障不传导
导致整体不可用 其他服务正常运行

解耦手段:

  • 异步化:同步调用改为消息队列异步处理
  • 服务化:单体拆分为微服务,故障隔离
  • 接口抽象:依赖抽象而非实现,便于替换

隔离设计(Isolation)

隔离维度 实现方式 目的
线程池隔离 核心业务独立线程池 防止非核心业务拖垮核心链路
进程隔离 独立部署、容器化 资源隔离,故障不传导
集群隔离 按业务/用户分集群 大客户/VIP 用户独立资源
机房隔离 单元化部署 机房级故障隔离

无状态设计(Stateless)

无状态服务是水平扩展和快速故障恢复的前提。

1
2
3
4
5
6
7
8
9
// 有状态设计(不推荐)
type OrderService struct {
userSession map[string]*Session // 状态存在内存
}

// 无状态设计(推荐)
type OrderService struct {
sessionStore SessionStore // 状态外置到 Redis
}

状态外置方案:

  • Session → Redis/Memcached
  • 文件上传 → 对象存储(S3/OSS)
  • 定时任务状态 → 数据库/ZooKeeper

高可用架构模式

主从模式(Primary-Secondary)

  • 适用场景:数据库、缓存、消息队列
  • 故障切换:检测主节点失效、隔离旧主并选择满足恢复条件的副本后,再提升新主;还需处理客户端重连与结果未知,不能只描述“从节点提升”。
  • 复制与确认:同步复制需要说明确认到达的是接收、持久化还是应用阶段,以及等待哪些副本;异步复制允许主节点先确认,因此故障切换可能丢失尚未复制的已确认写入。
  • 读写一致性:复制方式不能直接等同于强一致或最终一致。还需明确读哪个节点、写入顺序、陈旧容忍度、旧主隔离与恢复协议;同步复制也不自动保证任意副本读到最新值,异步传播也需有可验证的追平与修复机制。数据系统的保证边界见 DDIA 读书分享:数据模型、事务与分布式系统的设计取舍。
flowchart LR
    Primary[主节点:接收写入] -->|复制| ReplicaA[复制节点 A]
    Primary -->|复制| ReplicaB[复制节点 B]
    Reader[允许陈旧数据的读请求] --> ReplicaA
    Reader --> ReplicaB

图中箭头表示复制或读取,不代表同步确认或故障切换已经完成。复制节点是否可承担读取,以及允许的陈旧窗口,需要在具体系统中验证。

集群模式(Cluster)

  • 分片与复制是不同维度:分片决定数据分布在哪里,复制决定同一分片保留哪些副本及如何恢复;“集群”本身不意味着所有节点承担相同职责。
  • Redis Cluster:键按 16,384 个哈希槽分布到主节点,每个主节点负责一组槽,复制节点复制对应主节点的数据,并在满足故障转移条件时参与提升。主节点与复制节点角色不同;异步复制存在已确认写入在故障中丢失的窗口。[3]
  • Elasticsearch:按分片组织数据,每个分片有主分片和可配置的副本分片;具体写入确认、搜索可见性与故障恢复需分别讨论,不能用“分片集群”替代这些保证。相关机制见 DDIA 读书分享:数据模型、事务与分布式系统的设计取舍。
1
2
3
4
5
6
7
8
9
10
11
12
13
Redis Cluster 架构(16384 槽位):
┌─────────────────────────────────────────────┐
│ 槽位 0-5460 槽位 5461-10922 槽位 10923-16383 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ Master │ │ Master │ │ Master │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │ Node 1' │ │ Node 2' │ │ Node 3' │ │
│ │ Slave │ │ Slave │ │ Slave │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────┘

同城双活架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
             ┌─────────────┐
│ 用户请求 │
└──────┬──────┘
│
┌──────▼──────┐
│ DNS/GSLB │
└──────┬──────┘
┌────────────┴────────────┐
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ 机房 A │ │ 机房 B │
│ ┌────────┐ │ │ ┌────────┐ │
│ │ App │ │ │ │ App │ │
│ └───┬────┘ │ │ └───┬────┘ │
│ ┌───▼────┐ │ 同步 │ ┌───▼────┐ │
│ │ MySQL │◄├──────────┤►│ MySQL │ │
│ │ Master │ │ │ │ Slave │ │
│ └────────┘ │ │ └────────┘ │
└─────────────┘ └─────────────┘

关键设计点:

  • 流量调度:DNS/GSLB 按比例分配流量
  • 数据同步:MySQL 半同步复制,延迟 < 100ms
  • 故障切换:机房 A 故障时,流量全部切到机房 B

异地多活架构

异地多活是最复杂也是可用性最高的架构模式,核心挑战是数据一致性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
      北京                      上海                     广州
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 单元 Cell1 │ │ 单元 Cell2 │ │ 单元 Cell3 │
│ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │
│ │ App │ │ │ │ App │ │ │ │ App │ │
│ └────┬────┘ │ │ └────┬────┘ │ │ └────┬────┘ │
│ ┌────▼────┐ │ │ ┌────▼────┐ │ │ ┌────▼────┐ │
│ │ MySQL │ │◄────────►│ │ MySQL │ │◄───────►│ │ MySQL │ │
│ └─────────┘ │ DTS同步 │ └─────────┘ │ DTS同步 │ └─────────┘ │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
└────────────────────────┼───────────────────────┘
│
┌────────▼────────┐
│ 全局路由层 │
│ (按用户ID路由) │
└─────────────────┘

单元化设计要点:

  • 分片键选择:通常按用户 ID 分片,保证同一用户请求落到同一单元
  • 全局服务:库存、支付等需要全局一致的服务单独部署
  • 数据同步:DTS/Canal 异步同步,解决跨单元数据查询

高可用保护机制

限流(Rate Limiting)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 令牌桶限流器实现
type TokenBucket struct {
rate float64 // 令牌生成速率
capacity float64 // 桶容量
tokens float64 // 当前令牌数
lastUpdate time.Time
mu sync.Mutex
}

func (tb *TokenBucket) Allow() bool {
tb.mu.Lock()
defer tb.mu.Unlock()

now := time.Now()
elapsed := now.Sub(tb.lastUpdate).Seconds()
tb.tokens = math.Min(tb.capacity, tb.tokens+elapsed*tb.rate)
tb.lastUpdate = now

if tb.tokens >= 1 {
tb.tokens--
return true
}
return false
}

熔断(Circuit Breaker)

熔断器三状态:关闭(正常)→ 打开(熔断)→ 半开(探测)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
                错误率 > 阈值
┌─────────┐ ──────────────────→ ┌─────────┐
│ 关闭 │ │ 打开 │
│ CLOSED │ ←────────────────── │ OPEN │
└─────────┘ 探测成功 └────┬────┘
↑ │
│ 超时后 │
│ ┌─────────────────────────────┘
│ │
│ ▼
┌────┴────────┐
│ 半开 │
│ HALF-OPEN │
└─────────────┘

降级(Degradation)

降级类型 触发条件 降级策略
自动降级 错误率/RT 超阈值 返回缓存数据或默认值
手动降级 大促前/故障时 关闭非核心功能
读降级 主库压力大 读走从库或缓存
写降级 下游不可用 写入 MQ 异步重试

超时控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 超时控制最佳实践
func CallDownstream(ctx context.Context) error {
// 1. 设置合理超时
ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()

// 2. 调用下游
result, err := downstream.Call(ctx, req)

// 3. 超时处理
if ctx.Err() == context.DeadlineExceeded {
// 降级逻辑
return getFromCache()
}
return result, err
}

超时设置原则:

  • 上游超时 > 下游超时(避免上游已超时,下游还在执行)
  • 预留 Buffer:下游 RT P99 + 20%
  • 级联超时:A→B→C,A 超时 = B 超时 + C 超时 + Buffer

系统链路梳理和维护 System & Biz Profiling

系统链路梳理是所有保障工作的基础,如同对整体应用系统进行一次全面体检,从流量入口开始,按照链路轨迹,逐级分层节点,得到系统全局画像与核心保障点。

入口梳理盘点

一个系统往往存在十几个甚至更多流量入口,包含HTTP、RPC、消息等都多种来源。如果无法覆盖所有所有链路,可以从以下三类入口开始进行梳理:

  • 核心重保流量入口
    • 用户承诺服务SLI较高,对数据准确性、服务响应时间、可靠度具有明确要求。
    • 业务核心链路,浏览、下单、支付、履约
    • 面向企业级用户
  • 资损事件对应入口
    • 关联到公司资金收入或者客户资金收入收费服务
  • 大流量入口
    • 系统TPS&QPS TOP5~10
    • 该类入口虽然不涉及较高SLI与资损要求,但是流量较高,对整体系统负载有较大影响

节点分层判断

对于复杂场景可以做节点分层判断

流量入口就如同线团中的线头,挑出线头后就可按照流量轨迹对链路上的节点(HSF\DB\Tair\HBase等一切外部依赖)按照依赖程度、可用性、可靠性进行初级分层区分。

  1. 强弱依赖节点判断
  • 若节点不可用,链路业务逻辑被中断 or 高级别有损(存在一定耐受阈值),则为业务强依赖;反之为弱依赖。
  • 若节点不可用,链路执行逻辑被中断(return error),则为系统强依赖;反之为弱依赖。
  • 若节点不可用,系统性能受影响,则为系统强依赖;反之为弱依赖。
  • 按照快速失败设计逻辑,该类节点不应存在,但是在不变更应用代码前提下,如果出现该类节点,应作为强依赖看待。
  • 若节点无感可降级 or 存在业务轻微损伤替换方案,则为弱依赖。
  1. 低可用依赖节点判断
  • 节点服务日常超时严重
  • 节点对应系统资源不足
  1. 高风险节点判断
  • 上次大促后,节点存在大版本系统改造
  • 新上线未经历过大促的节点
  • 节点对应系统是否曾经出现高级别故障
  • 节点故障后存在资损风险

应产出数据

  • 识别核心接口(流程)调用拓扑图或者时序图(借用分布式链路追踪系统获得调用拓扑图)
  • 调用比
  • 识别资损风险
  • 识别内外部依赖

完成该项梳理工作后,我们应该产出以下数据:对应业务域所有核心链路分析,技术&业务强依赖、核心上游、下游系统、资损风险应明确标注。

业务策略&容量规划 Capacity Planning - 容量规划

业务策略

不同于高可用系统建设体系,大促稳定性保障体系与面向特定业务活动的针对性保障建设,因此,业务策略与数据是我们进行保障前不可或缺的数据。
一般大促业务数据可分为两类,全局业务形态评估以及应急策略&玩法。

全局评估

该类数据从可以帮助我们进行精准流量评估、峰值预测、大促人力排班等等,一般包含下面几类:

  • 业务量预估体量(日常X倍)
  • 预估峰值日期
  • 大促业务时长(XX日-XX日)
  • 业务场景预估流量分配

应急策略

  • 该类数据指相较于往年大促活动,本次大促业务变量,可用于应急响应预案与高风险节点评估等,一般包含下面两类:
  • 特殊业务玩法

容量规划的本质是追求计算风险最小化和计算成本最小化之间的平衡,只追求任意其一都不是合理的。为了达到这两者的最佳平衡点,需尽量精准计算系统峰值负载流量,再将流量根据单点资源负载上限换算成相应容量,得到最终容量规划模型。

流量模型评估

  1. 入口流量

对于一次大促,系统峰值入口流量一般由常规业务流量与非常规增量(比如容灾预案&业务营销策略变化带来的流量模型配比变化)叠加拟合而成。

  • 常规业务流量一般有两类计算方式:
    • 历史流量算法:该类算法假设当年大促增幅完全符合历史流量模型,根据当前&历年日常流量,计算整体业务体量同比增量模型;然后根据历年大促-日常对比,计算预估流量环比增量模型;最后二者拟合得到最终评估数据。
    • 由于计算时无需依赖任何业务信息输入,该类算法可用于保障工作初期业务尚未给出业务总量评估时使用,得到初估业务流量。
    • 业务量-流量转化算法(GMV\DAU\订单量):该类算法一般以业务预估总量(GMV\DAU\订单量)为输入,根据历史大促&日常业务量-流量转化模型(比如经典漏洞模型)换算得到对应子域业务体量评估。- 该种方式强依赖业务总量预估,可在保障工作中后期使用,在初估业务流量基础上纳入业务评估因素考虑。
  • 非常规增量包括营销策略变更、重试放大与容灾切流。例如,假设两个机房平分流量,一个故障后另一个承接全部流量,需要验证剩余机房能处理该故障场景下的实际峰值。故障概率可用于比较期望成本与风险,不能用概率加权后的平均流量证明故障发生时容量充足。若成本不允许全量承接,应提前明确限流或降级范围,并用演练验证。[4]
  1. 节点流量
    节点流量由入口流量根据流量分支模型,按比例转化而来。分支流量模型以系统链路为计算基础,遵循以下原则:
  • 同一入口,不同链路占比流量独立计算。
  • 针对同一链路上同一节点,若存在多次调用,需计算按倍数同比放大(比如DB\Tair等)。
  • DB写流量重点关注,可能出现热点造成DB HANG死。

容量转化

容量评估需要区分吞吐能力、在途请求数与资源上限。应用节点的可用吞吐量,是在指定请求组合、数据分布、并发和依赖条件下,仍满足延迟与错误率目标的处理速率;不能只看“最大并发数”。

1)Little’s Law:估算平均在途请求数

在稳定系统中,使用同一观察边界和足够长的窗口,Little’s Law 给出 L = λ × W:L 是平均在途请求数,λ 是平均到达率,W 是包含排队时间的平均逗留时间。[5] 不能把平均时间换成 P99,也不能把 L 解释为最大并发上限或单机容量。持续过载、队列不断增长时,不应以瞬时数据套用稳态关系。

教学假设:λ = 1,000 请求/秒,W = 0.1 秒,则平均在途请求数约为 100。这个结果不证明节点能安全处理 100 个并发请求;仍需压测确定延迟、错误率与资源饱和边界。

2)N + X 冗余原则

先压测得到满足 SLO 的单实例安全吞吐量 q,再初估 N = ceil(目标峰值 QPS / q)。这要求负载可均衡、实例近似同质且瓶颈未转移到数据库等共享依赖;分片与热点需要单独评估。

冗余 X 要根据故障域损失、扩容延迟、流量突发和成本决定。若需要容忍 k 个实例同时不可用,应验证 (N_total - k) × q ≥ 目标峰值 QPS;若要容忍整个可用区失效,就按该区实例数和剩余依赖容量验算。固定增加 20% 只是可选教学假设,不能替代故障场景验证。

全链路压测与容量验证

  • 按业务请求比例、热点键、缓存命中率、数据规模和重试策略构造负载;记录流量注入速率与实际完成吞吐量,避免把压测工具能力当成系统能力。
  • 逐步加压并进行稳态观察,寻找满足目标延迟、错误率、队列长度和依赖水位的安全吞吐区间。
  • 单独验证冷缓存、扩容延迟、实例 / 可用区故障与依赖降级,检查流量转移和重试是否使剩余节点过载。
  • 保存压测环境、版本、负载模型、监控证据与通过条件;测试流量必须隔离真实扣款、发券等副作用。容量结论仅对已验证的条件成立。[4]

应产出数据

  • 基于模型评估的入口流量模型 & 集群自身容量转化结果(若为非入口应用,则为限流点梳理)。
  • 基于链路梳理的分支流量模型 & 外部依赖容量转化结果。

大促保障

Incident Response - 紧急&前置预案梳理

要想在大促高并发流量场景下快速对线上紧急事故进行响应处理,仅仅依赖值班同学临场发挥是远远不够的。争分夺秒的情况下,无法给处理人员留有充足的策略思考空间,而错误的处理决策,往往会导致更为失控严重的业务&系统影响。因此,要想在大促现场快速而正确的响应问题,值班同学需要做的是选择题(Which),而不是陈述题(What)。而选项的构成,便是我们的业务&系统预案。从执行时机与解决问题属性来划分,预案可分为技术应急预案、技术前置预案、业务应急预案、业务前置预案等四大类。结合之前的链路梳理和业务评估结果,我们可以快速分析出链路中需要的预案,遵循以下原则:

  • 技术应急预案:该类预案用于处理系统链路中,某层次节点不可用的情况,例如技术/业务强依赖、弱稳定性、高风险等节点不可用等异常场景。
  • 技术前置预案:该类预案用于平衡整体系统风险与单节点服务可用性,通过熔断等策略保障全局服务可靠。例如弱稳定性&弱依赖服务提前降级、与峰值流量时间冲突的离线任务提前暂定等。
  • 业务应急预案:该类预案用于应对业务变更等非系统性异常带来的需应急处理问题,例如业务数据错误(数据正确性敏感节点)、务策略调整(配合业务应急策略)等
  • 业务前置预案:该类预案用于配和业务全局策略进行的前置服务调整(非系统性需求)

应产出数据

完成该项梳理工作后,我们应该产出以下数据:

  • 执行&关闭时间(前置预案)
  • 触发阈值(紧急预案,须关联相关告警)
  • 关联影响(系统&业务)
  • 决策&执行&验证人员
  • 开启验证方式
  • 关闭阈值(紧急预案)
  • 关闭验证方式

阶段性产出-全链路作战地图

进行完上述几项保障工作,我们基本可得到全局链路作战地图,包含链路分支流量模型、强弱依赖节点、资损评估、对应预案&处理策略等信息。大促期间可凭借该地图快速从全局视角查看应急事件相关影响,同时也可根据地图反向评估预案、容量等梳理是否完善合理。

Incident Response - 作战手册梳理

作战手册是整个大促保障的行动依据,贯穿于整个大促生命周期,可从事前、事中、事后三个阶段展开考虑。整体梳理应本着精准化、精细化的原则,理想状态下,即便是对业务、系统不熟悉的轮班同学,凭借手册也能快速响应处理线上问题。
事前
1)前置检查事项清单

  • 大促前必须执行事项checklist,通常包含以下事项:
  • 按已验证的预案处理异常节点;重启或手动 Full GC 需要诊断依据、影响评估和恢复验证,不作为大促前必做动作
  • 影子表数据清理
  • 检查上下游机器权限
  • 检查限流值
  • 检查机器开关一致性
  • 检查数据库配置
  • 检查中间件容量、配置(DB\缓存\NoSQL等)
  • 检查监控有效性(业务大盘、技术大盘、核心告警)
  • 每个事项都需包含具体执行人、检查方案、检查结果三列数据
    2)前置预案
  • 域内所有业务&技术前置预案。

事中

  1. 紧急技术&业务预案
    需要包含的内容基本同前置预案,差异点如下:
  • 执行条件&恢复条件:具体触发阈值,对应监控告警项。
  • 通知决策人。
  1. 应急工具&脚本
    常见故障排查方式、核心告警止血方式(强弱依赖不可用等),业务相关日志捞取脚本等。
  2. 告警&大盘
  • 应包含业务、系统集群及中间件告警监控梳理结果,核心业务以及系统大盘,对应日志数据源明细等数据:
  • 日志数据源明细:数据源名称、文件位置、样例、切分格式。
  • 业务、系统集群及中间件告警监控梳理结果:关联监控指标(链接)、告警关键级别、是否推送GOC、是否产生资损、是否关联故障、是否关联预案。
  • 核心业务&系统大盘:大盘地址、包含指标明细(含义、是否关联告警、对应日志)。
  1. 上下游机器分组
  • 应包含核心系统、上下游系统,在不同机房、单元集群分组、应用名,可用于事前-机器权限检查、事中-应急问题排查黑屏处理。
  1. 值班注意事项
  • 包含每班轮班同学值班必做事项、应急变更流程、核心大盘链接等。
  1. 核心播报指标
  • 包含核心系统&服务指标(CPU\LOAD\RT)、业务关注指标等,每项指标应明确具体监控地址、采集方式。
  1. 域内&关联域人员通讯录、值班
  • 包含域内技术、TL、业务方对应排班情况、联系方式(电话),相关上下游、基础组件(DB、中间件等)对应值班情况。
  1. 值班问题记录
  • 作战记录,记录工单、业务问题、预案(前置\紧急)(至少包含:时间、问题描述(截图)、影响分析、决策&解决过程等)。值班同学在值班结束前,进行记录。
    事后
  1. 系统恢复设置事项清单(限流、缩容)
    一般与事前检查事项清单对应,包含限流阈值调整、集群缩容等大促后恢复操作。
  2. 大促问题复盘记录
  • 应包含大促遇到的核心事件总结梳理。

沙盘推演和演练 Incident Response

实战沙盘演练是应急响应方面的最后一项保障工作,以历史真实故障CASE作为应急场景输入,模拟大促期间紧急状况,旨在考验值班同学们对应急问题处理的响应情况。
一般来说,一个线上问题从发现到解决,中间需要经历定位&排查&诊断&修复等过程,总体遵循以下几点原则:

  • 尽最大可能让系统先恢复服务,同时为根源调查保护现场(机器、日志、水位记录)。
  • 避免盲目搜索,依据白盒监控针对性诊断定位。
  • 有序分工,各司其职,避免一窝蜂失控乱象。
  • 依据现场情况实时评估影响范围,实在无法通过技术手段挽救的情况(例如强依赖不可用),转化为业务问题思考(影响范围、程度、是否有资损、如何协同业务方)。
  • 沙盘演练旨在检验值班同学故障处理能力,着重关注止血策略、分工安排、问题定位等三个方面:
    国际化中台双11买家域演练
    根据故障类型,常见止血策略有以下解决思路:
  • 入口限流:调低对应Provider服务来源限流值
  • 应对突发流量过高导致自身系统、下游强依赖负载被打满。
  • 下游降级:降级对应下游服务
  • 下游弱依赖不可用。
  • 下游业务强依赖经业务同意后降级(业务部分有损)。
  • 单点失败移除:摘除不可用节点
  • 单机水位飙高时,先下线不可用单机服务(无需下线机器,保留现场)。
  • 应对集群单点不可用、性能差。
  • 切换:单元切流或者切换备份

应对单库或某单元依赖因为自身原因(宿主机或网络),造成局部流量成功率下跌下跌。
Google SRE 的事故管理强调职责分工与交接,下面据此整理应急协作要素。[6]

  • 嵌套式职责分离,即分确的职能分工安排
  • 控制中心\作战室
  • 实时事故状态文档
  • 明确公开的职责交接
  • 其中嵌套式职责分离,即分确的职能分工安排,达到各司其职,有序处理的效果,一般可分为下列几个角色:
    事故总控:负责协调分工以及未分配事务兜底工作,掌握全局概要信息,一般为PM/TL担任。
    事务处理团队:事故真正处理人员,可根据具体业务场景&系统特性分为多个小团队。团队内部存在域内负责人,与事故总控人员进行沟通。
    发言人:事故对外联络人员,负责对事故处理内部成员以及外部关注人员信息做周期性信息同步,同时需要实时维护更新事故文档。
    规划负责人:负责外部持续性支持工作,比如当大型故障出现,多轮排班轮转时,负责组织职责交接记录

资损体系

资损(资金损失)是互联网业务中最严重的故障类型之一,尤其在金融、电商、支付等领域。资损防控体系需要覆盖事前预防、事中发现、事后止血的全生命周期。

资损场景分类

资损类型 场景举例 影响
多扣 重复扣款、金额计算错误 用户资金损失,客诉赔付
少扣 优惠券超发、折扣计算错误 公司资金损失
错付 付款到错误账户 资金追回困难
漏扣 订单状态异常导致未收款 应收账款损失
数据不一致 账务与实际资金不符 财务风险

事前:资损风险识别与预防

资损风险点识别

通过链路梳理识别所有资损风险点:

1
2
3
4
5
6
7
8
订单链路资损风险点:
┌─────────────────────────────────────────────────────────┐
│ 下单 → 计价 → 优惠 → 支付 → 扣库存 → 发货 → 结算 │
│ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │
│ 重复 价格 优惠券 重复 超卖 错发 结算 │
│ 下单 篡改 超发 扣款 漏发 金额错 │
└─────────────────────────────────────────────────────────┘

资损防控 Checklist

检查项 防控措施 责任人
接口幂等 唯一索引 + 状态机 开发
金额计算 Decimal 类型,禁用浮点 开发
参数校验 金额上下限、业务规则校验 开发
权限控制 敏感操作二次确认 产品
配置变更 双人复核、灰度发布 运维
代码审查 资损相关代码强制 Review Tech Lead

防重设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 幂等性设计 - 数据库唯一索引
CREATE UNIQUE INDEX uk_order_pay ON payment_records (order_id, pay_type);

// 幂等性设计 - 状态机控制
func ProcessPayment(orderID string) error {
// 1. 加分布式锁
lock := redis.Lock(fmt.Sprintf("pay:%s", orderID), 30*time.Second)
if !lock.Acquire() {
return ErrProcessing
}
defer lock.Release()

// 2. 检查状态
order := db.GetOrder(orderID)
if order.Status != OrderStatusUnpaid {
return ErrAlreadyPaid // 幂等返回
}

// 3. 执行支付
if err := doPayment(order); err != nil {
return err
}

// 4. 更新状态(乐观锁)
affected := db.Exec(`
UPDATE orders SET status = 'PAID'
WHERE id = ? AND status = 'UNPAID'
`, orderID)

if affected == 0 {
return ErrConcurrentModify
}
return nil
}

事中:实时资损发现

实时对账系统


【得物技术】浅谈资损防控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实时对账架构:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 交易系统 │ │ 支付系统 │ │ 账务系统 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────┐
│ Kafka │
└───────────────────────────┬──────────────────────────┘
│
┌──────▼──────┐
│ Flink 实时 │
│ 对账引擎 │
└──────┬──────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 告警 │ │ 拦截 │ │ 报表 │
└─────────┘ └─────────┘ └─────────┘

对账规则引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 对账规则定义
type ReconcileRule struct {
Name string
LeftSource string // 左数据源(如:订单表)
RightSource string // 右数据源(如:支付表)
JoinKey string // 关联键
CheckItems []CheckItem
}

type CheckItem struct {
LeftField string
RightField string
Operator string // =, !=, >, <, range
Threshold float64 // 允许误差
}

// 示例:订单金额与支付金额一致性校验
var orderPaymentRule = ReconcileRule{
Name: "订单支付金额一致性",
LeftSource: "orders",
RightSource: "payments",
JoinKey: "order_id",
CheckItems: []CheckItem{
{LeftField: "total_amount", RightField: "pay_amount", Operator: "=", Threshold: 0},
{LeftField: "status", RightField: "status", Operator: "mapping"},
},
}

异常告警分级

告警级别 触发条件 处理方式
P0 紧急 金额差异 > 10万 或 差异笔数 > 100 立即电话 + 自动熔断
P1 严重 金额差异 > 1万 或 差异笔数 > 10 钉钉 + 短信
P2 一般 金额差异 > 1000 或 差异笔数 > 1 钉钉通知
P3 提醒 存在差异记录 日报汇总

事后:止血与复盘

资损止血 SOP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
资损事件处理流程:
┌────────────────────────────────────────────────────────┐
│ 1. 发现告警(< 1 min) │
│ ↓ │
│ 2. 确认资损(< 5 min) │
│ - 查看监控大盘 │
│ - 确认影响范围 │
│ ↓ │
│ 3. 止血操作(< 10 min) │
│ - 关闭入口(下单/支付) │
│ - 下线问题节点 │
│ - 回滚变更 │
│ ↓ │
│ 4. 影响评估(< 30 min) │
│ - 统计资损金额 │
│ - 统计影响用户数 │
│ ↓ │
│ 5. 数据修复(视情况) │
│ - 自动修复脚本 │
│ - 人工核对修复 │
│ ↓ │
│ 6. 复盘总结(24h 内) │
│ - 根因分析 │
│ - 改进措施 │
└────────────────────────────────────────────────────────┘

资损修复策略

场景 修复策略 注意事项
多扣用户 原路退款 + 补偿 需财务审批
少扣用户 评估是否追缴 金额较小可放弃
优惠券超发 标记失效 + 用户通知 已使用的认亏
数据不一致 以权威数据源为准修复 保留修复日志

资损防控度量指标

指标 计算方式 目标值
资损发现率 发现资损笔数 / 实际资损笔数 > 99%
发现时效 从发生到发现的平均时间 < 5 分钟
止血时效 从发现到止血的平均时间 < 10 分钟
修复率 已修复金额 / 资损总金额 > 95%
资损率 资损金额 / 交易总金额 < 0.001%

参考学习

资损案例可参考 [7]、[8] 与 [9]。

风控体系

风控体系是保障业务安全的重要防线,主要应对欺诈、薅羊毛、恶意攻击等风险场景。一个完善的风控体系需要覆盖事前、事中、事后全链路。

风控场景分类

风险类型 典型场景 影响
账户风险 盗号、撞库、虚假注册 用户资产损失、平台信誉受损
交易风险 刷单、套现、恶意退款 资金损失、数据失真
营销风险 薅羊毛、优惠券滥用、黄牛 营销成本浪费
内容风险 垃圾信息、违规内容 合规风险、用户体验差
信用风险 恶意欠款、信用违约 坏账损失

风控系统架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
                      ┌─────────────────┐
│ 业务系统 │
│ (订单/支付/营销) │
└────────┬────────┘
│ 风控请求
┌────────▼────────┐
│ 风控网关 │
│ (统一接入) │
└────────┬────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ 实时风控引擎 │ │ 准实时引擎 │ │ 离线分析 │
│ (毫秒级) │ │ (秒级) │ │ (小时/天级) │
│ │ │ │ │ │
│ - 规则引擎 │ │ - 特征计算 │ │ - 模型训练 │
│ - 模型推理 │ │ - 风险画像 │ │ - 策略挖掘 │
│ - 黑白名单 │ │ - 关联分析 │ │ - 报表统计 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└─────────────────────┼─────────────────────┘
│
┌────────▼────────┐
│ 数据平台 │
│ (特征/画像/日志) │
└─────────────────┘

实时风控引擎设计

规则引擎

规则引擎是风控系统的核心组件,支持灵活配置和动态更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 规则定义示例
type Rule struct {
ID string
Name string
Priority int
Conditions []Condition
Action Action
Enabled bool
}

type Condition struct {
Field string // 字段名
Operator string // 操作符: =, !=, >, <, in, regex
Value interface{} // 比较值
}

type Action struct {
Type string // PASS, REJECT, REVIEW, CHALLENGE
Score float64 // 风险分数
Reason string // 拒绝原因
}

// 示例规则:同一设备24小时内注册超过5个账号
var deviceRegisterRule = Rule{
Name: "设备注册频率限制",
Priority: 100,
Conditions: []Condition{
{Field: "event_type", Operator: "=", Value: "register"},
{Field: "device_register_count_24h", Operator: ">", Value: 5},
},
Action: Action{Type: "REJECT", Reason: "设备注册频率异常"},
}

特征工程

特征类型 示例 计算方式
实时特征 用户当前IP、设备指纹 请求参数直接获取
滑动窗口特征 5分钟内登录次数 Redis ZSET 滑动窗口
累计特征 历史消费总额、注册天数 用户画像服务
关联特征 同设备关联账号数 图数据库查询
模型特征 欺诈概率分数 ML 模型实时推理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 滑动窗口特征计算
func GetLoginCount5Min(userID string) int {
key := fmt.Sprintf("login:count:%s", userID)
now := time.Now().Unix()
windowStart := now - 300 // 5分钟窗口

// 清理过期数据
redis.ZRemRangeByScore(key, "-inf", windowStart)

// 获取窗口内计数
count := redis.ZCount(key, windowStart, now)
return count
}

// 记录登录事件
func RecordLogin(userID string) {
key := fmt.Sprintf("login:count:%s", userID)
now := time.Now().Unix()
redis.ZAdd(key, now, fmt.Sprintf("%d", now))
redis.Expire(key, 10*time.Minute) // 双倍窗口 TTL
}

机器学习模型

模型类型 适用场景 特点
规则模型 明确特征的欺诈行为 可解释性强,快速上线
有监督模型 有标签数据的场景 XGBoost、LightGBM
无监督模型 异常检测、新型欺诈 Isolation Forest
图模型 团伙欺诈、关联分析 GNN、社区发现

风控决策流程

1
2
3
4
5
用户请求 → 数据采集 → 特征计算 → 规则引擎 → 模型推理 → 决策聚合 → 返回结果
│ │ │
▼ ▼ ▼
规则命中 模型分数 最终决策
(布尔值) (0-100) PASS/REJECT/REVIEW

决策聚合策略:

  • 串行策略:规则先行,命中则直接返回
  • 并行策略:规则 + 模型并行执行,综合评分
  • 熔断策略:风控服务超时/异常时,根据业务重要性决定放行或拒绝

风控运营

处置动作

处置动作 适用场景 用户体验
直接通过 低风险用户 无感知
人工审核 中风险,需人工判断 延迟处理
验证码挑战 疑似机器行为 轻度打扰
短信验证 账户安全验证 中度打扰
直接拒绝 高风险/黑名单 阻断
静默观察 采集数据不阻断 无感知

效果度量

指标 定义 目标
拦截率 拦截的风险事件 / 实际风险事件 > 95%
误杀率 误拦正常用户 / 正常用户总数 < 0.1%
响应时间 风控请求 RT P99 < 50ms
覆盖率 接入风控的业务场景比例 100%

策略迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
数据驱动的风控策略迭代闭环:
┌─────────────────────────────────────────────────────┐
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 日志采集 │ → │ 案例标注 │ → │ 特征分析 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ↑ │ │
│ │ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 效果评估 │ ← │ 灰度发布 │ ← │ 策略优化 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
└─────────────────────────────────────────────────────┘

风控体系建设参考


性能优化

性能优化是稳定性建设的重要组成部分。高性能不仅带来更好的用户体验,也能在流量高峰时提供更大的容量 Buffer,降低系统压力。

性能优化方法论

性能优化原则

  • 先测量,后优化:没有数据支撑的优化是盲目的
  • 抓大放小:优先优化瓶颈点,遵循 80/20 法则
  • 空间换时间:缓存、预计算、冗余存储
  • 串行改并行:利用多核、异步化
  • 减少 IO:批量处理、连接复用、数据压缩

性能分析套路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
性能问题定位流程:
┌─────────────────────────────────────────────────────┐
│ 1. 现象收集 │
│ - 接口 RT 多少?P99/P95/AVG? │
│ - CPU/内存/IO 占用? │
│ - 哪些时段出现? │
│ │
│ 2. 瓶颈定位 │
│ - 链路追踪:哪一跳耗时最长? │
│ - 火焰图:CPU 消耗在哪些函数? │
│ - 慢查询日志:哪些 SQL 耗时长? │
│ │
│ 3. 根因分析 │
│ - 是计算密集还是 IO 密集? │
│ - 是代码问题还是资源问题? │
│ - 是配置问题还是架构问题? │
│ │
│ 4. 方案制定与验证 │
│ - 制定优化方案 │
│ - 压测验证效果 │
│ - 灰度上线 │
└─────────────────────────────────────────────────────┘

各层性能优化策略

网络层优化

优化点 方案 效果
减少 RTT CDN 加速、边缘计算 降低用户感知延迟
连接复用 HTTP/2、连接池 减少建连开销
数据压缩 Gzip、Brotli 减少传输数据量
协议优化 HTTP/3(QUIC) 减少队头阻塞

应用层优化

a) 缓存优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
缓存层级:
┌─────────────────────────────────────────────────────┐
│ L1: 本地缓存(进程内) │
│ - 容量小、速度快(纳秒级) │
│ - 适合热点数据、配置数据 │
│ - 工具:Caffeine、Guava Cache │
│ │
│ L2: 分布式缓存 │
│ - 容量大、速度快(毫秒级) │
│ - 适合共享数据 │
│ - 工具:Redis、Memcached │
│ │
│ L3: 数据库 │
│ - 容量最大、速度较慢(10ms+) │
│ - 持久化存储 │
└─────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 多级缓存读取示例
func GetUserProfile(userID string) (*UserProfile, error) {
// L1: 本地缓存
if profile := localCache.Get(userID); profile != nil {
return profile.(*UserProfile), nil
}

// L2: Redis
profile, err := redis.Get(fmt.Sprintf("user:%s", userID))
if err == nil && profile != nil {
localCache.Set(userID, profile, 5*time.Minute)
return profile, nil
}

// L3: 数据库
profile, err = db.GetUserProfile(userID)
if err != nil {
return nil, err
}

// 回填缓存
redis.Set(fmt.Sprintf("user:%s", userID), profile, 30*time.Minute)
localCache.Set(userID, profile, 5*time.Minute)
return profile, nil
}

b) 并发优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 串行调用 → 并行调用
// Before: RT = T1 + T2 + T3 = 300ms
func GetOrderDetail_Before(orderID string) (*OrderDetail, error) {
order, _ := getOrder(orderID) // 100ms
user, _ := getUser(order.UserID) // 100ms
products, _ := getProducts(order.Items) // 100ms
return &OrderDetail{order, user, products}, nil
}

// After: RT = max(T1, T2, T3) = 100ms
func GetOrderDetail_After(ctx context.Context, orderID string) (*OrderDetail, error) {
var (
order *Order
user *User
products []*Product
)

g, ctx := errgroup.WithContext(ctx)

g.Go(func() error {
var err error
order, err = getOrder(orderID)
return err
})

g.Go(func() error {
var err error
user, err = getUser(order.UserID)
return err
})

g.Go(func() error {
var err error
products, err = getProducts(order.Items)
return err
})

if err := g.Wait(); err != nil {
return nil, err
}
return &OrderDetail{order, user, products}, nil
}

c) 批量处理

1
2
3
4
5
6
7
8
9
10
11
12
// 单条查询 → 批量查询
// Before: 100 次网络往返
for _, id := range userIDs {
user, _ := redis.Get(fmt.Sprintf("user:%s", id))
}

// After: 1 次网络往返
keys := make([]string, len(userIDs))
for i, id := range userIDs {
keys[i] = fmt.Sprintf("user:%s", id)
}
users, _ := redis.MGet(keys...)

数据库优化

a) SQL 优化

问题 优化方案
无索引/索引失效 添加合适索引,避免索引失效场景
**SELECT *** 只查需要的字段
大事务 拆分小事务,减少锁持有时间
深分页 游标分页(WHERE id > last_id LIMIT 10)
N+1 查询 JOIN 或批量查询

b) 索引优化原则

1
2
3
4
5
6
7
8
9
10
11
-- 联合索引最左前缀
CREATE INDEX idx_user_status_time ON orders(user_id, status, create_time);

-- 以下查询可以使用索引
SELECT * FROM orders WHERE user_id = 123;
SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID';
SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID' AND create_time > '2025-01-01';

-- 以下查询无法使用索引
SELECT * FROM orders WHERE status = 'PAID'; -- 缺少最左字段
SELECT * FROM orders WHERE user_id = 123 OR status = 'PAID'; -- OR 导致失效

c) 读写分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
             ┌─────────────┐
│ 应用服务 │
└──────┬──────┘
│
┌──────▼──────┐
│ 数据源路由 │
└──────┬──────┘
┌────────────┴────────────┐
│ 写操作 │ 读操作
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Master │ ──────→ │ Slave │
│ (写库) │ 同步 │ (读库) │
└─────────────┘ └─────────────┘

JVM/GC 优化(Java)

优化点 方案
堆内存 根据业务特点设置合理大小
GC 算法 G1/ZGC 替代 CMS
对象池 复用大对象,减少 GC 压力
逃逸分析 栈上分配,减少堆分配

Go 性能优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 1. sync.Pool 复用对象
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}

func ProcessRequest() {
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf)
// 使用 buf
}

// 2. 减少内存分配
// Before: 每次拼接都分配新内存
func buildString_Before(items []string) string {
result := ""
for _, item := range items {
result += item
}
return result
}

// After: 预分配
func buildString_After(items []string) string {
var builder strings.Builder
builder.Grow(1024) // 预分配
for _, item := range items {
builder.WriteString(item)
}
return builder.String()
}

// 3. 先通过 benchmark / profile 确认热点;不要默认移除 defer。
// 手动释放资源需覆盖所有返回路径,性能收益取决于 Go 版本与代码形态。

性能测试与监控

压测类型

类型 目的 场景
基准测试 获取单接口性能基线 新接口上线前
负载测试 验证目标 QPS 下表现 常规压测
压力测试 找到系统极限 容量评估
稳定性测试 长时间运行是否稳定 内存泄漏排查
全链路压测 真实场景模拟 大促前

性能指标

指标 含义 目标参考
QPS/TPS 每秒请求/事务数 根据业务目标
RT(P50/P95/P99) 响应时间分位值 P99 < 500ms
错误率 失败请求占比 < 0.1%
CPU 使用率 计算资源占用 < 70%(留 Buffer)
内存使用率 内存资源占用 < 80%
GC 频率/耗时 垃圾回收影响 Full GC < 1次/天

性能优化案例

案例:接口 RT 从 500ms 优化到 50ms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
问题:商品详情接口 P99 RT = 500ms

分析:
1. 链路追踪发现:
- 数据库查询:200ms
- 价格计算服务:150ms
- 库存服务:100ms
- 其他:50ms

2. 深入分析:
- 数据库:缺少索引,全表扫描
- 价格计算:串行调用 5 个下游
- 库存服务:无缓存,每次查 DB

优化方案:
1. 数据库添加索引:200ms → 10ms
2. 价格计算并行化:150ms → 50ms(max(5个下游))
3. 库存增加缓存:100ms → 5ms

优化后:P99 RT = 50ms(10倍提升)

SLI/SLO/SLA 体系

概念定义

概念 全称 定义
SLI Service Level Indicator 服务质量指标,可量化的度量值
SLO Service Level Objective 服务质量目标,SLI 的目标值
SLA Service Level Agreement 服务等级协议,对外承诺
1
2
3
SLI:观测用户体验的指标
SLO:在明确窗口与统计口径下设定的目标
SLA:有责任与后果约定的服务协议,并非所有 SLO 都是 SLA

常用 SLI 指标

指标类型 SLI 定义 SLO 示例
可用性 成功有效请求数 / 全部有效请求数 30 天内 ≥ 99.9%
延迟 有效请求中耗时不超过阈值的比例 30 天内 ≥ 99% 的请求耗时 ≤ 200ms
处理及时性 记录在约定时限内处理完成的比例 30 天内 ≥ 99.9% 在 5 分钟内完成
正确性 经核验结果正确的有效请求比例 30 天内 ≥ 99.99%
新鲜度 查询时数据年龄不超过阈值的比例 30 天内 ≥ 99.9% 不超过 5 分钟

表中数字均为教学假设。有效请求、失败、超时、重试与缺失测量的口径必须明确。P99 可作诊断指标,延迟 SLO 用阈值达标比例更便于计算错误预算;吞吐量通常用于描述负载与容量,不能在用户需求不足时把低 QPS 当成服务失败。[10]

Error Budget(错误预算)

Error Budget 用于在明确可靠性目标下管理变更风险。[10]

1
2
3
4
5
6
7
8
9
允许失败比例 = 1 - SLO
请求型错误预算 = 窗口内有效请求总数 × 允许失败比例

教学假设:30 天内 1,000 万次有效请求,SLO = 99.9%
允许失败请求数 = 10,000 次

只有按时间统计的可用性 SLI,才可换算不可用时长:
30 天 × 24 小时 × 60 分钟 × 0.1% = 43.2 分钟
请求型 SLI 的 0.1% 不能直接换算为 43.2 分钟停机。

Error Budget 策略:先约定统计窗口、预算消耗速率与发布政策。预算充足仍需正常变更验证;快速消耗时及时排查并降低风险;预算耗尽可按团队政策暂停非紧急发布,保留修复与安全变更的例外。不能只凭“还有预算”宣布一次发布安全。


混沌工程

混沌工程是一种通过主动注入故障来验证系统韧性的实践方法。

混沌工程原则

  1. 建立稳态假设:定义系统正常运行的指标
  2. 多样化真实事件:模拟生产环境的各种故障
  3. 在生产环境运行:测试环境无法完全模拟生产
  4. 自动化持续运行:纳入 CI/CD 流水线
  5. 最小化爆炸半径:从小范围开始,逐步扩大

故障注入类型

故障类型 注入方式 验证目标
进程故障 Kill 进程、OOM 服务自愈、故障切换
网络故障 延迟、丢包、断网 超时处理、重试机制
依赖故障 Mock 下游返回错误 降级策略、熔断机制
资源耗尽 CPU 打满、磁盘填满 限流、告警
时钟偏移 NTP 故障模拟 时间敏感逻辑

混沌实验流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌─────────────────────────────────────────────────────┐
│ 1. 定义稳态 │
│ - 确定关键 SLI(可用性、RT) │
│ - 记录当前基线值 │
│ │
│ 2. 假设 │
│ - 注入 XX 故障后,系统应能在 XX 秒内恢复 │
│ │
│ 3. 设计实验 │
│ - 故障类型、注入时长 │
│ - 影响范围(单机/单机房/全局) │
│ - 回滚方案 │
│ │
│ 4. 执行实验 │
│ - 从最小范围开始 │
│ - 实时监控 SLI │
│ - 随时准备回滚 │
│ │
│ 5. 分析结果 │
│ - 系统表现是否符合预期? │
│ - 发现了哪些薄弱点? │
│ │
│ 6. 改进 │
│ - 修复发现的问题 │
│ - 扩大实验范围 │
└─────────────────────────────────────────────────────┘

混沌工程工具

工具 适用场景 特点
ChaosBlade [11] 多平台通用 开源故障注入工具,覆盖多类实验场景
Chaos Monkey AWS/云环境 Netflix 开源,随机终止实例
LitmusChaos Kubernetes CNCF 项目,云原生
Gremlin 企业级 商业产品,功能全面

变更管理

变更是系统故障的主要来源。Google SRE 统计显示,70% 的故障与变更相关。

变更类型

变更类型 风险等级 审批要求
代码发布 高 CR + 灰度
配置变更 中-高 双人审批
DB Schema 变更 高 DBA 审批
基础设施变更 高 架构组审批
依赖升级 中 兼容性测试

安全变更原则

  1. 可回滚:任何变更必须有回滚方案
  2. 可观测:变更前后有明确的监控指标
  3. 渐进式:灰度发布,逐步放量
  4. 自动化:减少人工操作失误
  5. 时间窗口:避开业务高峰期

灰度发布策略

1
2
3
4
5
6
7
8
9
灰度发布流程:
┌─────────────────────────────────────────────────────┐
│ 1% → 5% → 10% → 30% → 50% → 100% │
│ │ │ │ │ │ │ │
│ └──────┴──────┴───────┴───────┴───────┘ │
│ 每个阶段观察 10-30 分钟 │
│ 确认无异常后进入下一阶段 │
│ 发现问题立即回滚 │
└─────────────────────────────────────────────────────┘

On-Call 实践

On-Call 是 SRE/运维团队的核心职责,负责生产环境的故障响应。

On-Call 轮值设计

维度 最佳实践
轮值周期 一周为单位,避免过长导致疲劳
人员配置 主备双人制,避免单点
交接 详细的 Handoff 文档
补偿 调休或补贴

On-Call 响应流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
告警触发
│
▼
┌─────────────┐ 5 分钟内
│ 确认告警 │───────────────→ 未响应?升级到 Backup
└──────┬──────┘
│
▼
┌─────────────┐ 15 分钟内
│ 初步诊断 │───────────────→ 无法定位?拉群协作
└──────┬──────┘
│
▼
┌─────────────┐
│ 止血操作 │───────────────→ 优先恢复服务
└──────┬──────┘
│
▼
┌─────────────┐
│ 根因分析 │───────────────→ 可后续进行
└──────┬──────┘
│
▼
┌─────────────┐
│ 事后复盘 │───────────────→ 24-48 小时内完成
└─────────────┘

On-Call 减负措施

  • 消除噪音告警:合并重复告警,优化阈值
  • 自动化修复:常见问题自动处理(如磁盘清理)
  • 完善 Runbook:标准化处理流程
  • 定期演练:保持团队能力

参考资料

  1. Site Reliability Engineering

  2. Monitoring Distributed Systems;Google SRE,2016。

  3. Redis cluster specification;Redis,官方文档;核验于 2026-10-12。

  4. Handling Overload;Google SRE,2016。

  5. A Proof for the Queuing Formula: L = λW;John D. C. Little,Operations Research,1961;核验于 2026-10-12。

  6. Managing Incidents;Google SRE,2016。

  7. 资损防控技术体系简介及实践

  8. 浅谈资损防控

  9. 美团配送资金安全治理实践

  10. Implementing SLOs;Google SRE,2018;核验于 2026-10-12。

  11. ChaosBlade;ChaosBlade 项目,官方站点;故障注入能力与文档入口;核验于 2026-10-12。