第一章:Go后端性能压测白皮书导论
性能压测不是上线前的临时补救,而是贯穿Go服务生命周期的质量基础设施。在高并发、低延迟场景下,Go凭借其轻量协程、高效GC和原生并发模型成为主流后端选择,但默认行为不等于生产就绪——goroutine泄漏、内存分配激增、锁竞争、HTTP超时配置失当等问题往往在压测中集中暴露。
压测的核心目标
- 验证服务在预期QPS下的稳定性(如P99响应时间≤200ms)
- 发现资源瓶颈点(CPU饱和、内存持续增长、Goroutine数异常飙升)
- 评估弹性能力(自动扩缩容触发阈值、熔断降级生效时机)
- 建立基线指标(冷启动耗时、连接池复用率、GC pause百分位)
关键工具链选型原则
| 工具类型 | 推荐方案 | 说明 |
|---|---|---|
| 流量生成 | hey / k6 |
hey -n 10000 -c 200 http://localhost:8080/api/v1/users 可快速验证基础吞吐;k6支持JS脚本编排复杂场景(登录→查询→提交) |
| 指标采集 | pprof + expvar |
启动时注册:http.HandleFunc("/debug/pprof/", pprof.Index),通过 go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30 获取CPU火焰图 |
| 日志分析 | 结构化日志 + jq |
在HTTP handler中注入请求ID并记录耗时:log.Printf("req_id=%s method=GET path=/users status=%d latency_ms=%.2f", reqID, statusCode, elapsed.Seconds()*1000) |
Go运行时关键调优入口
启用GODEBUG=gctrace=1观察GC频率与停顿;通过GOMAXPROCS限制OS线程数避免调度抖动;对高频分配路径使用sync.Pool复用对象(如JSON解码器实例)。示例代码:
var decoderPool = sync.Pool{
New: func() interface{} {
return json.NewDecoder(nil) // 初始化无绑定reader的decoder
},
}
// 使用时:
dec := decoderPool.Get().(*json.Decoder)
dec.Reset(req.Body) // 复用底层缓冲
err := dec.Decode(&data)
decoderPool.Put(dec) // 归还至池
压测前务必关闭debug模式、禁用pprof未授权访问,并确保环境隔离——生产配置、真实数据分布、网络拓扑均需尽可能贴近线上。
第二章:基础设施与运行时层优化
2.1 Go Runtime GC调优与GOMAXPROCS动态配置实践
Go 程序性能常受限于 GC 压力与 OS 线程调度失衡。合理配置 GOMAXPROCS 与 GC 触发阈值,可显著降低延迟抖动。
动态调整 GOMAXPROCS 的典型场景
import "runtime"
// 根据容器 CPU 配额自动适配(如 Kubernetes Downward API 获取 limits.cpu)
if cpuLimit := os.Getenv("CONTAINER_CPU_LIMIT"); cpuLimit != "" {
if n, err := strconv.ParseInt(cpuLimit, 10, 64); err == nil {
runtime.GOMAXPROCS(int(n)) // ⚠️ 避免设为 0 或超物理核心数
}
}
runtime.GOMAXPROCS(n) 控制 P(Processor)数量,即最大并行运行的 OS 线程数;设为过大会增加调度开销,过小则无法利用多核。
GC 调优关键参数对照表
| 参数 | 默认值 | 推荐范围 | 影响 |
|---|---|---|---|
GOGC |
100 | 50–200 | 百分比:上一次 GC 后堆增长多少触发下一次 GC |
GOMEMLIMIT |
off | 如 4GiB |
绝对内存上限,强制 GC 防止 OOM |
GC 行为决策流程
graph TD
A[堆内存增长] --> B{是否 ≥ 上次GC后 * GOGC%?}
B -->|是| C[启动标记-清除]
B -->|否| D[继续分配]
C --> E{GOMEMLIMIT 是否超限?}
E -->|是| F[强制触发 GC 并阻塞分配]
2.2 HTTP Server底层参数调优:KeepAlive、Read/Write Timeout与ConnState监控
KeepAlive 连接复用策略
启用长连接可显著降低 TLS 握手与 TCP 建立开销。Gin(基于 net/http)默认开启,但需显式配置生命周期:
srv := &http.Server{
Addr: ":8080",
Handler: router,
KeepAlive: 30 * time.Second, // TCP keepalive 探测间隔(OS 级)
ReadTimeout: 10 * time.Second, // 从连接建立到读取首字节的上限
WriteTimeout: 15 * time.Second, // 从响应开始写入到完成的总时限
}
KeepAlive 控制内核层心跳探测频率;ReadTimeout 防止慢速客户端阻塞读缓冲;WriteTimeout 避免大响应体传输卡顿导致 goroutine 泄漏。
ConnState 实时连接状态观测
通过 ConnState 回调可捕获连接生命周期事件:
var connStates = make(map[net.Conn]http.ConnState)
srv.ConnState = func(conn net.Conn, state http.ConnState) {
switch state {
case http.StateNew:
connStates[conn] = state
case http.StateClosed, http.StateHijacked:
delete(connStates, conn)
}
}
该机制支持构建连接数水位告警与异常连接诊断能力。
超时参数协同关系
| 参数 | 作用域 | 典型值 | 风险提示 |
|---|---|---|---|
ReadTimeout |
连接级读操作 | 5–30s | 过短误杀流式接口 |
WriteTimeout |
连接级写操作 | ≥ReadTimeout | 过短中断大文件下载 |
IdleTimeout |
空闲连接存活 | 60s(需手动设) | 缺失将导致 TIME_WAIT 爆增 |
graph TD
A[Client发起请求] --> B{ReadTimeout触发?}
B -- 是 --> C[关闭连接]
B -- 否 --> D[Handler处理]
D --> E{WriteTimeout触发?}
E -- 是 --> C
E -- 否 --> F[返回响应]
2.3 内存分配优化:sync.Pool复用对象与避免逃逸的实测对比
Go 中高频短生命周期对象易引发 GC 压力。sync.Pool 提供对象复用能力,而编译器逃逸分析决定堆/栈分配——二者协同可显著降低分配开销。
对象复用实践
var bufPool = sync.Pool{
New: func() interface{} { return new(bytes.Buffer) },
}
func processWithPool() {
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset() // 必须重置状态
buf.WriteString("hello")
// ... use buf
bufPool.Put(buf) // 归还前确保无外部引用
}
Get() 返回任意复用实例(可能为 nil,故 New 函数必须非空);Put() 仅在对象未被其他 goroutine 引用时才安全归还。
性能对比(100万次分配)
| 场景 | 分配次数 | GC 次数 | 耗时(ms) |
|---|---|---|---|
直接 new(bytes.Buffer) |
1,000,000 | 12 | 48.2 |
sync.Pool 复用 |
12 | 0 | 8.7 |
关键约束
sync.Pool不保证对象存活周期,不可用于跨 goroutine 长期持有- 避免逃逸需让编译器判定变量生命周期完全在栈内(如局部 slice 不返回其底层数组指针)
graph TD
A[创建对象] --> B{逃逸分析}
B -->|栈分配| C[无GC压力]
B -->|堆分配| D[触发GC]
D --> E[sync.Pool介入复用]
E --> F[降低实际堆分配频次]
2.4 Goroutine泄漏检测与pprof+trace协同定位方法论
Goroutine泄漏常表现为持续增长的runtime.NumGoroutine()值,却无对应业务逻辑终止信号。
核心诊断流程
- 启动时记录基准goroutine快照(
debug.ReadGCStats辅助排除GC干扰) - 持续采样
/debug/pprof/goroutine?debug=2获取完整栈信息 - 结合
go tool trace捕获调度事件,定位阻塞点
pprof与trace协同分析表
| 工具 | 输出焦点 | 关键参数 | 典型线索 |
|---|---|---|---|
go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=2 |
阻塞栈深度与调用链 | --alloc_space、top |
重复出现的select{}或chan recv栈帧 |
go tool trace |
Goroutine生命周期(start/end/block/unblock) | -http=localhost:8080 |
长时间处于Gwaiting状态且无Grunnable→Grunning跃迁 |
// 示例:主动注入泄漏检测钩子
func monitorGoroutines() {
prev := runtime.NumGoroutine()
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for range ticker.C {
curr := runtime.NumGoroutine()
if curr > prev+10 { // 阈值需结合业务基线调整
log.Printf("leak alert: %d → %d goroutines", prev, curr)
pprof.Lookup("goroutine").WriteTo(os.Stdout, 2) // 输出完整栈
}
prev = curr
}
}
该函数每30秒比对goroutine数量变化,超阈值时导出全栈。WriteTo(os.Stdout, 2)启用详细栈模式,暴露runtime.gopark调用位置,是定位chan/time.Sleep/sync.WaitGroup泄漏的关键依据。
graph TD
A[HTTP请求触发泄漏] --> B[启动goroutine执行异步任务]
B --> C{任务是否完成?}
C -- 否 --> D[阻塞在未关闭channel接收]
C -- 是 --> E[defer wg.Done()]
D --> F[pprof发现goroutine堆积]
F --> G[trace显示Gwaiting超10s]
G --> H[定位到未close的channel]
2.5 Linux内核参数调优:net.core.somaxconn、fs.file-max与TCP栈参数联动验证
高并发场景下,net.core.somaxconn(默认128)与fs.file-max(系统级文件句柄上限)必须协同调优,否则连接队列溢出或accept()失败将频发。
关键参数联动关系
somaxconn≤fs.file-max / 2(预留足够句柄给已建立连接与进程)- 需同步增大
net.ipv4.tcp_max_syn_backlog(SYN队列)以匹配半连接负载
验证命令与配置
# 查看当前值并临时调整
sysctl -w net.core.somaxconn=65535
sysctl -w fs.file-max=2097152
sysctl -w net.ipv4.tcp_max_syn_backlog=65535
逻辑说明:
somaxconn限制全连接队列长度;若其值超过fs.file-max的合理比例,新连接将因无法分配socket文件描述符而被内核丢弃。tcp_max_syn_backlog需同步提升,避免SYN洪峰时队列截断。
参数依赖关系(mermaid)
graph TD
A[客户端SYN] --> B[tcp_max_syn_backlog]
B --> C[三次握手完成]
C --> D[somaxconn全连接队列]
D --> E[accept系统调用]
E --> F[消耗fs.file-max中的fd]
第三章:应用逻辑与中间件层优化
3.1 JSON序列化性能陷阱:encoding/json vs json-iterator vs simdjson压测对比
现代微服务中,JSON序列化常成性能瓶颈。默认 encoding/json 基于反射+接口断言,泛型支持弱且内存分配频繁。
基准测试环境
- Go 1.22、Intel Xeon Platinum 8360Y、16GB RAM
- 测试负载:10KB 结构化用户数据(嵌套4层,含 slice/map/string/int)
性能对比(单位:ns/op,越低越好)
| 库 | Marshal | Unmarshal | 内存分配次数 |
|---|---|---|---|
encoding/json |
12,480 | 18,920 | 42 |
json-iterator |
5,310 | 7,650 | 18 |
simdjson |
1,890 | 2,340 | 3 |
// simdjson 需预编译 schema(零拷贝解析)
var parser simdjson.StreamingParser
parser.Init() // 复用 parser 实例避免初始化开销
buf := make([]byte, 0, 10240)
jsonBytes, _ := json.Marshal(data)
_, err := parser.Parse(jsonBytes, &targetStruct) // 直接写入栈/堆变量
simdjson利用 AVX2 指令并行解析 JSON token,跳过字符串拷贝;但要求目标结构体字段名与 JSON key 完全匹配(无 tag 映射能力),适用于高吞吐、schema 稳定场景。
3.2 数据库访问优化:连接池参数调优、预处理语句复用与上下文超时注入实践
连接池核心参数调优策略
HikariCP 是当前高性能首选,关键参数需协同调整:
| 参数名 | 推荐值 | 说明 |
|---|---|---|
maximumPoolSize |
CPU核数 × (2–4) | 避免线程争抢与内存溢出 |
connection-timeout |
3000ms | 防止建连阻塞业务线程 |
idle-timeout |
600000ms(10min) | 平衡空闲连接回收与复用率 |
预处理语句复用实践
// ✅ 正确:PreparedStatement 被 Connection 缓存复用
String sql = "SELECT * FROM users WHERE status = ? AND created_at > ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "ACTIVE");
ps.setTimestamp(2, Timestamp.from(Instant.now().minusDays(7)));
ps.executeQuery(); // 多次执行自动复用编译计划
}
逻辑分析:JDBC 驱动在 prepareStatement() 时将 SQL 模板注册至连接级缓存;? 占位符确保执行计划可复用,避免硬解析开销。若拼接字符串则触发全量重编译。
上下文超时注入
// Go + pgx 示例:将 HTTP 请求上下文透传至 DB 层
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
defer cancel()
rows, err := pool.Query(ctx, "SELECT id, name FROM products WHERE category = $1", category)
逻辑分析:ctx 携带截止时间,一旦超时,pgx 主动中止查询并释放连接,防止连接池被长事务耗尽。这是服务间超时传递的关键闭环。
3.3 中间件链路精简:自定义Router替代Gin默认中间件栈的QPS提升实证
Gin 默认路由层隐式注入 Recovery 和 Logger 中间件,即便未显式调用,也会参与每次请求生命周期。
核心优化路径
- 移除冗余中间件注册逻辑
- 使用
gin.New()配合自定义http.ServeMux路由分发 - 仅在需调试/错误捕获时按需注入中间件
性能对比(压测结果,16核/32GB,wrk -t8 -c100 -d30s)
| 场景 | 平均 QPS | P99 延迟 |
|---|---|---|
| Gin 默认栈 | 12,480 | 42ms |
| 自定义 Router(无中间件) | 18,960 | 19ms |
// 构建零中间件 Router 实例
r := gin.New()
r.Use(gin.Recovery()) // 仅保留必要兜底
r.NoRoute(func(c *gin.Context) {
c.String(404, "not found")
})
该写法跳过 gin.Default() 的 Logger() 初始化,避免日志 I/O 与上下文拷贝开销;NoRoute 直接接管未匹配路径,绕过 Gin 内部 handlers 数组遍历逻辑。
请求链路简化示意
graph TD
A[HTTP Request] --> B[Gin Engine.ServeHTTP]
B --> C{Default: Logger+Recovery}
C --> D[Handler Chain]
A --> E[Custom Router.ServeHTTP]
E --> F[Direct Handler Dispatch]
第四章:可观测性与压测工程化体系构建
4.1 基于go-wrk与vegeta的分布式压测脚本编写与结果聚合分析
为实现跨节点协同压测,采用 go-wrk(轻量低开销)与 vegeta(高精度流式压测)双引擎并行驱动:
# 启动 vegeta 服务端监听(Node A)
vegeta attack -targets=targets.txt -rate=100 -duration=30s -output=results.bin | vegeta report
该命令以 100 RPS 持续压测 30 秒,将二进制结果流式写入
results.bin,支持后续聚合解析;-targets.txt支持动态域名与路径组合,适配灰度环境。
分布式执行协调
- 使用
ssh并行触发各节点压测脚本 - 各节点结果统一推送至中心 NFS 存储
- 通过
vegeta encode将多节点.bin文件合并为单一流式报告
结果聚合关键指标对比
| 工具 | 并发模型 | 时延统计粒度 | 是否支持分布式结果合并 |
|---|---|---|---|
| go-wrk | goroutine池 | 毫秒级直方图 | ❌(需手动解析 JSON) |
| vegeta | channel流控 | 微秒级分位数 | ✅(vegeta encode) |
graph TD
A[压测脚本启动] --> B{选择引擎}
B -->|高吞吐场景| C[go-wrk + 自定义聚合器]
B -->|精准SLA验证| D[vegeta + encode合并]
C & D --> E[统一CSV/InfluxDB入库]
4.2 火焰图生成全链路:pprof CPU/Mutex/Block Profile采集与火焰图交互式解读
三类核心 profile 采集方式
CPU profile:需持续运行程序(-cpuprofile=cpu.pprof),采样间隔默认100Hz,反映热点函数执行时长;Mutex profile:启用GODEBUG=mutexprofile=1后调用runtime.SetMutexProfileFraction(1),捕获锁竞争路径;Block profile:通过runtime.SetBlockProfileRate(1)开启,记录 goroutine 阻塞事件(如 channel wait、sync.Mutex 持有)。
生成火焰图的标准化流程
# 采集并转换为火焰图可读格式
go tool pprof -http=:8080 cpu.pprof # 启动交互式 Web UI
go tool pprof -raw -seconds=30 http://localhost:6060/debug/pprof/profile # 远程实时采集
--seconds=30控制采样时长;-raw输出原始 profile 数据供flamegraph.pl处理;-http启动内置可视化服务,支持按函数、标签、时间范围动态过滤。
profile 类型对比
| 类型 | 触发条件 | 典型瓶颈场景 | 采样开销 |
|---|---|---|---|
| CPU | 定时中断(默认10ms) | 计算密集型逻辑 | 低 |
| Mutex | 锁释放时记录持有栈 | 高并发争抢共享资源 | 中 |
| Block | goroutine 进入阻塞状态 | I/O 等待、channel 阻塞 | 可控(依赖 rate) |
graph TD
A[启动 Go 程序] --> B{启用 profiling}
B --> C[CPU: runtime.SetCPUProfileRate]
B --> D[Mutex: SetMutexProfileFraction]
B --> E[Block: SetBlockProfileRate]
C & D & E --> F[pprof HTTP handler]
F --> G[go tool pprof 解析]
G --> H[Flame Graph 渲染]
4.3 Prometheus+Grafana指标看板搭建:关键延迟分位数(P90/P99)、goroutines数与内存增长率监控
核心指标采集配置
在 prometheus.yml 中启用 Go 运行时指标暴露:
scrape_configs:
- job_name: 'go-app'
static_configs:
- targets: ['localhost:8080'] # 应用需启用 /metrics 端点
metrics_path: '/metrics'
该配置使 Prometheus 每 15s 抓取一次 /metrics,自动收录 go_goroutines、go_memstats_heap_alloc_bytes 及 http_request_duration_seconds 等原生指标。
关键查询表达式
| 指标类型 | PromQL 查询式 | 说明 |
|---|---|---|
| P90 延迟(秒) | histogram_quantile(0.9, rate(http_request_duration_seconds_bucket[5m])) |
基于直方图桶计算滑动窗口 P90 |
| Goroutines 实时数 | go_goroutines |
直接反映协程并发压力 |
| 内存增长率(MB/s) | rate(go_memstats_heap_alloc_bytes[5m]) / 1024 / 1024 |
每秒堆分配速率,定位泄漏苗头 |
Grafana 面板逻辑
graph TD
A[Prometheus] -->|pull| B[Go App /metrics]
B --> C[go_goroutines]
B --> D[http_request_duration_seconds_bucket]
B --> E[go_memstats_heap_alloc_bytes]
C & D & E --> F[Grafana Dashboard]
F --> G[P90/P99 趋势图]
F --> H[Goroutines 数量热力图]
F --> I[内存增速折线图]
4.4 自动化回归压测流水线:GitHub Actions触发+基准线比对+性能衰减告警机制
触发与执行设计
当 main 分支合并或 perf/ 前缀标签推送时,GitHub Actions 自动拉起压测任务:
on:
push:
branches: [main]
tags: ['perf/v*']
该配置确保仅在功能稳定(主干)或明确性能发布(带 perf 标签)时触发,避免噪声干扰。
基准线比对流程
压测结果自动与最近三次成功运行的中位数基准比对,关键指标包括 P95 延迟、吞吐量(RPS)和错误率。
| 指标 | 当前值 | 基准中位数 | 允许偏差 | 状态 |
|---|---|---|---|---|
| P95延迟(ms) | 218 | 192 | +10% | ⚠️ 衰减 |
| RPS | 1420 | 1560 | -8% | ⚠️ 衰减 |
告警机制
if (( $(echo "$current_p95 > $baseline_p95 * 1.1" | bc -l) )); then
curl -X POST "$ALERT_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{\"text\":\"⚠️ 性能衰减告警:P95延迟超阈值 $current_p95 ms\"}"
fi
脚本使用 bc 进行浮点比较,通过 Webhook 推送至企业微信/Slack;阈值支持环境变量注入,便于多环境差异化配置。
graph TD
A[Git Push/Tag] –> B[GitHub Actions Job]
B –> C[执行k6压测]
C –> D[提取JSON指标]
D –> E[查Redis基准缓存]
E –> F[偏差计算+阈值判定]
F –> G{是否超限?}
G –>|是| H[触发告警]
G –>|否| I[更新基准缓存]
第五章:结语与高并发演进路线图
真实业务场景下的演进起点
某电商中台在2021年Q3日订单峰值突破8万单,MySQL主库CPU持续超90%,库存扣减接口P99延迟达3.2s。团队未直接上分布式事务,而是先落地「本地消息表+定时补偿」模式,在3周内将超时订单率从7.3%压降至0.14%,为后续架构升级赢得关键缓冲期。
阶梯式能力演进路径
以下为经生产验证的四阶段演进路线(单位:日均请求量):
| 阶段 | 核心技术选型 | 容量阈值 | 关键指标改善 |
|---|---|---|---|
| 单体优化 | 连接池调优+读写分离 | ≤50万 | DB负载下降42%,缓存命中率81%→93% |
| 微服务化 | Spring Cloud Alibaba + Seata AT | 50万–300万 | 接口平均RT从480ms→210ms,故障隔离率提升至99.6% |
| 异步解耦 | Kafka+Saga事务+状态机 | 300万–1500万 | 库存服务吞吐量从1200 QPS→8600 QPS,消息积压归零率99.99% |
| 混合云弹性 | Service Mesh + 多活单元化 | ≥1500万 | 故障恢复时间从12min→47s,跨AZ流量调度精度达毫秒级 |
典型反模式警示
- ❌ 过早引入分库分表:某金融系统在单表仅280万行时强行分片,导致JOIN查询失效、分页逻辑崩溃,返工耗时11人日;
- ❌ 忽视时钟漂移:某IoT平台使用NTP同步但未配置
adjtimex校准,在容器重启后出现327ms时钟回拨,引发Kafka消费者重复消费; - ✅ 正确实践:某直播平台采用
@Async + CompletableFuture.allOf()替代线程池硬编码,在弹幕发送链路中将并发控制粒度精确到用户会话ID,避免热点账户打爆线程池。
生产环境压测黄金法则
# 基于真实流量录制的渐进式压测脚本(Locust)
from locust import HttpUser, task, between
class ConcurrentUser(HttpUser):
wait_time = between(0.5, 2) # 模拟真实用户思考时间
@task(3) # 3倍权重访问商品详情页
def get_item(self):
self.client.get("/api/v1/items/1024",
headers={"X-Trace-ID": str(uuid4())})
@task(1) # 1倍权重触发下单
def create_order(self):
self.client.post("/api/v1/orders",
json={"item_id": 1024, "qty": 1},
timeout=3.0) # 强制超时防止雪崩
架构决策树
flowchart TD
A[QPS > 5000?] -->|是| B[是否存在强一致性要求?]
A -->|否| C[继续单体优化]
B -->|是| D[选择Seata XA/Saga]
B -->|否| E[采用最终一致性+幂等设计]
D --> F[检查DB是否支持XA协议]
F -->|否| G[降级为TCC模式]
E --> H[引入Redis Stream做事件分发]
监控体系必须覆盖的5个维度
- 实时性:Prometheus采集间隔≤15s,Grafana告警响应延迟
- 一致性:OpenTelemetry trace_id在Nginx→Spring Boot→MyBatis→Redis全链路透传
- 可追溯:ELK中error日志自动关联trace_id与SQL执行计划
- 容量水位:JVM Metaspace使用率>85%触发自动扩容,而非等待OOM
- 业务语义:订单创建成功率监控需排除风控拦截等预期失败场景
技术债偿还优先级矩阵
高影响/低实施成本 → Redis大Key扫描工具上线(2人日)
高影响/高实施成本 → MySQL 8.0并行复制改造(17人日)
低影响/低实施成本 → Nginx access_log增加$upstream_response_time(0.5人日)
低影响/高实施成本 → 全链路TLS1.3升级(暂缓,待CDN厂商支持)
演进过程中的组织适配
某支付网关团队在第二阶段微服务化时,将SRE工程师嵌入每个业务小组,共同制定SLI(如“支付回调300ms内成功率达99.95%”),并通过GitOps流水线将SLI阈值写入Helm Chart的values.yaml,使每次发布自动校验容量基线。
