第一章:Golang性能瓶颈定位的核心理念与方法论
性能优化不是盲目调参或重构,而是以可观测性为基石、以数据为唯一依据的系统性诊断过程。Golang 的核心优势在于其运行时自带丰富的性能探针(runtime/trace、net/http/pprof),但真正决定效率的是开发者能否建立“从现象到本质”的归因链条——即区分是 CPU 密集、内存泄漏、 Goroutine 阻塞,还是系统调用等待。
性能问题的三层归因模型
- 应用层:算法复杂度、低效的数据结构(如频繁 slice 扩容)、未复用对象(sync.Pool 缺失)
- 运行时层:GC 压力过高(
GODEBUG=gctrace=1观察停顿)、Goroutine 泄漏(pprof/goroutine?debug=2查看活跃数)、调度器竞争(runtime.SchedStats或 trace 中的Proc切换热区) - 系统层:文件描述符耗尽、网络连接阻塞(
lsof -p <pid>)、CPU 亲和性缺失导致缓存失效
关键诊断工具链与实操步骤
启用标准 pprof 接口需在服务中嵌入:
import _ "net/http/pprof" // 自动注册 /debug/pprof 路由
// 启动 HTTP 服务(即使无其他路由)
go func() { http.ListenAndServe("localhost:6060", nil) }()
随后执行:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30—— 采集 30 秒 CPU 火焰图go tool pprof -http=:8080 cpu.pprof—— 启动交互式分析界面go tool pprof http://localhost:6060/debug/pprof/heap—— 获取实时堆快照,用top -cum定位分配热点
观测优先级建议
| 信号类型 | 首选工具 | 典型触发场景 |
|---|---|---|
| CPU 高占用 | pprof/profile |
紧密循环、正则匹配、JSON 序列化 |
| 内存持续增长 | pprof/heap |
channel 未消费、全局 map 未清理 |
| 响应延迟抖动 | runtime/trace |
GC STW、系统调用阻塞、锁争用 |
| Goroutine 爆炸 | pprof/goroutine |
context 漏传、time.After 未 stop |
真正的瓶颈往往藏在“最不显眼”的调用路径中——例如一个 log.Printf 在 hot path 中触发反射,或 time.Now() 被高频调用引发 VDSO 争用。因此,必须坚持“测量先行”,拒绝经验主义猜测。
第二章:CPU密集型瓶颈的精准识别与验证
2.1 基于pprof CPU profile的火焰图深度解读与采样偏差规避
火焰图本质是栈帧采样频次的可视化映射,但默认 runtime/pprof 的 100Hz 采样频率易遗漏短生命周期函数(如 <10ms 的 goroutine 执行)。
采样偏差的典型表现
- 热点函数未出现在顶部(被高频中断打断)
- 火焰图呈现“锯齿状”而非平滑堆叠
net/http.(*conn).serve占比异常偏低,而runtime.mcall频繁出现
关键调优参数
# 启用高精度采样(需 Go 1.21+)
go tool pprof -sample_index=cpu -seconds=30 -http=localhost:8080 ./myapp http://localhost:6060/debug/pprof/profile
-sample_index=cpu强制使用 CPU 时间而非默认调度器事件;-seconds=30延长采集窗口以降低统计波动;Go 运行时会动态调整采样间隔至 ~1ms 精度(非固定 Hz),显著缓解短函数漏采。
| 偏差类型 | 诱因 | 缓解方案 |
|---|---|---|
| 时间分辨率不足 | 默认 ~10ms 采样周期 | 启用 GODEBUG=asyncpreemptoff=1 + 高频 profile |
| 调度器干扰 | runtime.mcall 挤占栈 |
使用 -symbolize=none 减少符号解析开销 |
graph TD
A[CPU Profile 采集] --> B{采样触发源}
B -->|OS timer interrupt| C[可能错过微秒级执行]
B -->|Go async preempt| D[更精准捕获 goroutine 切换]
D --> E[火焰图顶部真实反映 hot path]
2.2 Goroutine调度延迟与系统调用阻塞的协同分析实践
Goroutine 调度器(M-P-G 模型)在遇到阻塞式系统调用时,会触发 M 与 P 的解绑,避免 P 空转。这一机制直接影响用户态协程的响应延迟。
阻塞系统调用的调度行为
当 goroutine 执行 read()、accept() 等阻塞 syscall 时:
- 运行该 goroutine 的 M 会被挂起,P 转移至其他空闲 M 继续调度;
- 若无空闲 M,运行时会创建新 M(受
GOMAXPROCS和 OS 线程限制); - syscall 返回后,M 尝试重新绑定原 P,或加入全局队列等待调度。
实验对比:阻塞 vs 非阻塞 I/O 延迟
| 场景 | 平均调度延迟(μs) | P 利用率 | 是否触发 M 创建 |
|---|---|---|---|
syscall.Read(阻塞) |
128 | 62% | 是(+3 M) |
net.Conn.Read(非阻塞+epoll) |
9.3 | 97% | 否 |
// 模拟阻塞系统调用对调度器的影响
func blockingSyscall() {
fd, _ := unix.Open("/dev/random", unix.O_RDONLY, 0)
defer unix.Close(fd)
buf := make([]byte, 1)
// ⚠️ 此处触发 M 阻塞,P 被移交,goroutine 暂停调度
_, err := unix.Read(fd, buf) // 阻塞直到内核提供随机字节
if err != nil {
panic(err)
}
}
该调用使当前 M 进入内核休眠态,调度器立即解耦 M-P 关系,并唤醒或新建 M 接管其余 P 上的 goroutine,体现“协作式抢占”本质——延迟源于 OS 调度粒度与 Go 调度器协同开销。
协同延迟根因图谱
graph TD
A[goroutine 执行 syscall] --> B{是否为阻塞型?}
B -->|是| C[内核态休眠,M 脱离 P]
B -->|否| D[轮询/事件驱动,P 持续工作]
C --> E[新 M 创建或唤醒]
C --> F[P 重绑定延迟 + 上下文切换]
E --> F
2.3 高频小对象分配引发的GC压力误判排查(结合GODEBUG=gctrace与memstats)
高频小对象分配常被误判为内存泄漏,实则源于GC频次激增而非堆增长。关键在于区分“GC次数多”与“堆压力大”。
GODEBUG=gctrace 调试输出解析
启用 GODEBUG=gctrace=1 后,每轮GC打印形如:
gc 3 @0.021s 0%: 0.010+0.84+0.005 ms clock, 0.040+0.84/0.36/0.42+0.020 ms cpu, 2->2->1 MB, 4 MB goal, 4 P
gc 3:第3次GC;@0.021s:启动时间;0.010+0.84+0.005:STW标记、并发扫描、STW清理耗时;2->2->1 MB:标记前/标记后/存活对象大小;4 MB goal:下轮目标堆大小。
memstats 关键指标对照
| 字段 | 含义 | 健康阈值 |
|---|---|---|
GCCount |
累计GC次数 | 突增需警惕 |
HeapAlloc |
当前已分配对象大小 | 稳定则非泄漏 |
NextGC |
下次GC触发阈值 | 接近HeapAlloc说明压力真实 |
典型误判场景还原
func hotLoop() {
for i := 0; i < 1e6; i++ {
_ = make([]byte, 32) // 每次分配32B小对象 → 快速填满span,触发频繁GC
}
}
该代码不增加HeapAlloc峰值(对象快速释放),但GCCount飙升——gctrace显示0->0->0 MB循环,memstats.HeapAlloc波动
2.4 内联失效与逃逸分析异常导致的非预期性能损耗实测复现
JVM 在 JIT 编译阶段依赖内联决策与逃逸分析优化热点方法。当对象引用逃逸出方法作用域(如被存入静态集合或作为返回值),JIT 将禁用标量替换与同步消除,并阻止调用者内联——引发显著性能回退。
关键诱因示例
- 方法返回
new ArrayList<>()并被外部持有 - 使用
ThreadLocal存储未封闭对象 - 日志框架中将局部对象传入异步队列
复现实验代码
public static List<String> buildList() {
List<String> list = new ArrayList<>(); // 逃逸:被返回后脱离栈帧
list.add("a"); list.add("b");
return list; // JIT 判定为 GlobalEscape,禁用内联 & 栈上分配
}
逻辑分析:buildList() 被高频调用时,JIT 不再内联其调用点;同时 ArrayList 实例强制堆分配,GC 压力上升。-XX:+PrintInlining -XX:+PrintEscapeAnalysis 可验证逃逸标记为 escaped。
性能对比(10M 次调用,单位:ms)
| 场景 | 耗时 | GC 次数 |
|---|---|---|
| 逃逸版本(返回新 List) | 382 | 12 |
| 非逃逸(入参复用 List) | 156 | 0 |
graph TD
A[热点方法调用] --> B{逃逸分析}
B -->|GlobalEscape| C[禁用内联+堆分配]
B -->|NoEscape| D[标量替换+栈分配+内联]
C --> E[吞吐下降 / GC 上升]
2.5 多核缓存一致性开销(False Sharing)的Go内存布局诊断与修复验证
False Sharing 的根源
当多个goroutine并发修改同一CPU缓存行(通常64字节)中不同字段时,即使逻辑无共享,缓存协议(如MESI)仍强制使该行在核心间频繁无效与同步,引发性能陡降。
诊断:pprof + go tool trace 定位热点
type Counter struct {
hits, misses uint64 // ❌ 同一缓存行,易发生False Sharing
}
分析:
uint64占8字节,hits与misses紧邻存储,在64字节缓存行内共存。多核写入触发缓存行争用,perf stat -e cache-misses可观测异常高缓存失效率。
修复:填充隔离字段
type Counter struct {
hits uint64
_pad1 [56]byte // 确保misses独占新缓存行
misses uint64
}
参数说明:
[56]byte使hits末尾到misses起始跨64字节边界,强制两字段分属不同缓存行。
验证对比(16核压测)
| 方案 | QPS | L3缓存未命中率 |
|---|---|---|
| 原始结构 | 24K | 18.7% |
| 填充后结构 | 89K | 2.3% |
graph TD
A[goroutine A 写 hits] -->|触发整行失效| C[Cache Line]
B[goroutine B 写 misses] -->|被迫重载整行| C
C --> D[性能下降]
第三章:内存与GC相关瓶颈的分层定位策略
3.1 堆内存增长拐点与对象生命周期异常的pprof heap profile交叉验证
当 go tool pprof 显示 heap profile 中 inuse_objects 在某时间点陡增,而 inuse_space 增幅滞后,往往暗示短生命周期对象突发堆积——典型如未及时关闭的 http.Response.Body 或泄漏的 sync.Pool 持有引用。
关键诊断命令
# 捕获带采样精度的堆快照(每分配 512KB 记录一次)
go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap?gc=1&debug=1
-alloc_space跟踪总分配量(含已释放),gc=1强制 GC 后采集,避免浮动垃圾干扰;结合debug=1输出 ASCII 图形便于快速定位热点分配路径。
常见异常模式对照表
| 分配路径 | inuse_objects ↑↑ | inuse_space ↗ | 典型根因 |
|---|---|---|---|
net/http.(*Request).clone |
✅ | ❌ | 请求体未 io.Copy 清空 |
encoding/json.(*Decoder).Decode |
✅ | ✅ | 大结构体反复解码未复用 |
对象存活链分析流程
graph TD
A[pprof heap --inuse_objects] --> B{对象数量突增?}
B -->|Yes| C[按 alloc_space 排序 top3 调用栈]
C --> D[检查栈顶是否含 finalizer/weak ref]
D --> E[验证 runtime.SetFinalizer 是否遗漏调用]
- 突增对象若集中在
runtime.malg或sync.pool.get,需核查Put()调用缺失; - 若
runtime.gcBgMarkWorker占比异常高,表明 GC 扫描压力剧增,间接印证对象生命周期失控。
3.2 sync.Pool误用与泄漏场景的运行时指标追踪(如gc_cycles、mallocs_total)
数据同步机制
sync.Pool 的 Put/Get 操作不保证对象复用,若 Put 频率远高于 Get,对象将滞留于本地池中直至下次 GC——此时 mallocs_total 持续上升,但 gc_cycles 增长缓慢,暴露“假空闲”泄漏。
关键指标对照表
| 指标 | 正常波动特征 | 误用泄漏信号 |
|---|---|---|
go_gc_cycles_total |
与 GC 触发强同步 | 增速显著低于 mallocs_total |
go_memstats_mallocs_total |
线性增长(受业务驱动) | 持续陡升且 Get/put 比 |
运行时诊断代码
// 启用指标采集(需在 init 或 main 开头)
import _ "net/http/pprof"
// 访问 /debug/pprof/metrics 获取 go_gc_cycles_total, go_memstats_mallocs_total
该代码启用标准 metrics 接口;/debug/pprof/metrics 返回 Prometheus 格式指标,go_gc_cycles_total 反映 GC 次数,go_memstats_mallocs_total 统计所有堆分配次数——二者比值偏离 1:1 即提示 Pool 对象未被及时回收。
泄漏路径示意
graph TD
A[Put unused *bytes.Buffer] --> B[滞留 localPool.head]
B --> C{GC 触发}
C -->|仅清理 shared 池| D[localPool 仍持有引用]
D --> E[下次 Get 未命中 → 新 malloc]
3.3 持久化引用链(如全局map未清理、闭包捕获大对象)的pprof trace回溯法
识别泄漏源头:pprof trace 的关键视角
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/trace?seconds=30 启动交互式 trace 分析,重点关注 runtime.gopark → runtime.mcall → goroutine creation 路径中持续存活的 goroutine 及其栈帧引用关系。
闭包捕获导致的隐式持有
var cache = make(map[string]*HeavyObject)
func makeHandler(key string, data *HeavyObject) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cache[key] = data // ❌ 闭包捕获 data,且写入全局 map
fmt.Fprint(w, "ok")
}
}
逻辑分析:
data被闭包捕获后,即使 handler 执行完毕,cache[key]仍强引用*HeavyObject;cache本身永不释放,形成持久化引用链。key作为 map key 亦延长data生命周期。
回溯路径模式表
| trace 节点 | 风险信号 | 推荐操作 |
|---|---|---|
runtime.newobject |
频繁分配大对象(>1MB) | 检查分配者是否被长期持有 |
runtime.mapassign_faststr |
在全局 map 中高频写入 | 审计 key 生命周期与清理逻辑 |
runtime.gcWriteBarrier |
大量 write barrier 日志 | 标识活跃引用链末端 |
引用链可视化(关键路径)
graph TD
A[HTTP Handler Goroutine] --> B[闭包变量 data]
B --> C[全局 cache map]
C --> D[HeavyObject 实例]
D --> E[byte slice / struct fields]
第四章:IO与并发模型瓶颈的动态观测体系
4.1 net/http服务器goroutine堆积的goroutine profile+trace联动分析(含超时/取消传播断点)
goroutine泄漏典型场景
当http.Handler未正确响应ctx.Done(),或阻塞在无缓冲channel、未设超时的time.Sleep、未包装io.Copy时,goroutine持续累积。
关键诊断组合
go tool pprof -goroutines:定位存活goroutine数量与堆栈go tool trace:筛选Goroutine creation+Network blocking事件,定位阻塞源头
超时传播断点示例
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
select {
case <-time.After(5 * time.Second): // ❌ 静态延迟,不响应cancel
case <-ctx.Done(): // ✅ 响应取消信号
http.Error(w, "timeout", http.StatusRequestTimeout)
return
}
}
此写法使ctx.Done()成为goroutine生命周期唯一退出信号,避免goroutine滞留。
trace中关键断点标记
| 事件类型 | 触发条件 | 诊断价值 |
|---|---|---|
context canceled |
ctx.Done()被close |
确认取消已发出 |
blocking on chan send/receive |
goroutine卡在channel操作 | 定位未消费/未关闭channel |
graph TD
A[HTTP请求抵达] --> B[生成request.Context]
B --> C{Handler是否监听ctx.Done?}
C -->|否| D[goroutine永不退出]
C -->|是| E[收到cancel后主动return]
E --> F[runtime自动回收goroutine]
4.2 Go runtime网络轮询器(netpoll)阻塞与epoll就绪事件丢失的strace+gdb联合取证
复现关键路径
通过 strace -p <pid> -e trace=epoll_wait,epoll_ctl 可捕获 netpoll 阻塞点,常观察到 epoll_wait 持续返回 0(超时),但内核 socket 实际已就绪。
gdb 断点定位
(gdb) b runtime.netpoll
(gdb) cond 1 (uintptr)(unsafe.Pointer(&gp.m.park)) == 0x0 # 定位未唤醒的 goroutine
该断点触发时,gp.m.park 为空表明 goroutine 被挂起但未被 netpoll 唤醒,暴露事件注册/唤醒链断裂。
epoll 事件丢失根因表
| 环节 | 表现 | 触发条件 |
|---|---|---|
epoll_ctl(EPOLL_CTL_DEL) |
fd 已关闭但未同步清除事件 | close() 与 netpoll 竞态 |
runtime.pollDesc.reuse() |
pd.rg = 0 未重置 |
多次复用 pollDesc 导致 rg/stale |
事件同步流程
graph TD
A[syscall.Write] --> B[内核 TCP 发送队列满]
B --> C[netpoll 注册 EPOLLOUT]
C --> D[goroutine park]
D --> E[内核缓冲区腾出 → EPOLLOUT 就绪]
E --> F[netpoll 扫描 rg/stale]
F --> G{rg == 0?} -->|是| H[事件静默丢失]
核心在于 pollDesc.rg 的原子更新缺失与 epoll_ctl 调用时机错位。
4.3 context.Context传递断裂导致的goroutine泄漏可视化检测(基于runtime.GoroutineProfile定制分析)
当 context.Context 在调用链中意外丢失(如未透传、被 nil 替换或新建无取消能力的 context.Background()),下游 goroutine 将失去取消信号,形成隐性泄漏。
检测核心原理
利用 runtime.GoroutineProfile 抓取活跃 goroutine 的栈快照,结合正则匹配识别含 context.WithCancel/WithTimeout 但无对应 ctx.Done() 监听的协程。
var profile = make([]runtime.StackRecord, 10000)
n, ok := runtime.GoroutineProfile(profile)
if !ok { panic("profile overflow") }
for i := 0; i < n; i++ {
// 提取栈帧中 context 相关符号与阻塞点
if strings.Contains(string(profile[i].Stack0[:]), "select { case <-ctx.Done()") == false &&
strings.Contains(string(profile[i].Stack0[:]), "http.(*Server).Serve") {
leakCandidates = append(leakCandidates, profile[i])
}
}
逻辑说明:
profile[i].Stack0存储原始栈迹;ctx.Done()缺失且位于 HTTP server 等长生命周期协程中,是典型泄漏线索。10000容量需根据实际并发规模动态估算。
可视化路径
graph TD
A[采集 GoroutineProfile] --> B[解析栈帧符号]
B --> C{含 ctx.Done()?}
C -->|否| D[标记为可疑泄漏]
C -->|是| E[跳过]
D --> F[聚合按调用链分组]
F --> G[生成火焰图+泄漏溯源树]
| 指标 | 正常值 | 泄漏征兆 |
|---|---|---|
| 平均 goroutine 寿命 | > 60s 持续增长 | |
ctx.Done() 出现率 |
≥ 98% | |
| 栈深度中位数 | 8–12 层 | ≥ 20 层(递归/嵌套) |
4.4 sync.Mutex争用热点与RWMutex读写失衡的mutex profile量化建模与压测对比验证
数据同步机制
在高并发读多写少场景中,sync.Mutex易因写操作阻塞全部goroutine,而sync.RWMutex虽分离读写锁,但写饥饿或读goroutine激增仍引发失衡。
压测建模关键指标
mutex contention duration(pprof采集)rwlock read/write ratio(运行时采样)goroutines blocked per second(/debug/pprof/mutex)
对比验证代码片段
// 启用mutex profile(需在main入口前)
import _ "net/http/pprof"
func init() {
runtime.SetMutexProfileFraction(1) // 100%采样率,生产慎用
}
SetMutexProfileFraction(1)强制全量采集锁争用事件,生成/debug/pprof/mutex可导出的profile数据,用于定位争用热点goroutine栈。
| 锁类型 | 平均争用延迟 | P99读延迟 | 写吞吐(QPS) |
|---|---|---|---|
| sync.Mutex | 12.8ms | 15.3ms | 840 |
| sync.RWMutex | 0.9ms | 1.2ms | 210 |
争用路径可视化
graph TD
A[Read-heavy Goroutines] -->|竞争RWMutex.RLock| B[RWMutex reader count++]
C[Write Goroutine] -->|调用RLock后阻塞| D{reader count > 0?}
D -->|Yes| E[等待所有读释放]
D -->|No| F[获取写锁]
第五章:从百万QPS压测到生产环境的定位范式迁移
压测场景与真实故障的鸿沟
某电商大促前,核心下单服务在全链路压测中稳定承载 120 万 QPS,CPU 利用率峰值仅 68%,P99 延迟
从资源维度转向依赖拓扑诊断
我们重构了故障定位流程,弃用“先看 CPU 再查磁盘”的线性排查路径,转而构建实时依赖拓扑图。以下为某次支付超时事件的根因定位过程:
| 时间点 | 观测现象 | 拓扑定位动作 | 发现 |
|---|---|---|---|
| 14:22:07 | 支付回调延迟 >5s | 查询支付网关 → 银行渠道 API 节点 | 该节点出向连接数达 9820(阈值 10000) |
| 14:22:11 | 银行渠道响应码 503 | 下钻至 TLS 层 | SSL handshake timeout 占比 89% |
| 14:22:15 | 对比同集群其他节点 | 检查证书轮换日志 | 该节点未同步更新 CA Bundle,导致 TLS 1.3 握手失败 |
动态黄金信号驱动的告警降噪
传统基于静态阈值的告警在高波动场景下失效率达 63%。我们采用动态黄金信号(Latency、Error、Saturation、Traffic)融合算法,每 30 秒滚动计算各服务的「健康熵值」:
def calculate_health_entropy(latency_p99, error_rate, saturation, traffic_ratio):
# 基于历史基线自适应归一化
norm_latency = min(max((latency_p99 - baseline_lat) / baseline_lat, 0), 1)
norm_error = min(error_rate / 0.005, 1) # 0.5% 为容忍上限
return 0.4 * norm_latency + 0.3 * norm_error + 0.2 * saturation + 0.1 * abs(traffic_ratio - 1)
当熵值 >0.72 且持续 3 个周期,才触发一级告警;同时关联拓扑上游服务熵值变化斜率,自动排除“被拖累型”误报。
全链路上下文快照机制
每次告警触发时,自动采集 5 层上下文快照:① 应用层 traceID 分布热力图;② JVM 线程栈采样(每秒 100ms 抽样);③ eBPF 抓取的 socket read/write 延迟分布;④ 容器网络 namespace 内 netstat 连接状态;⑤ etcd 中该实例配置版本与生效时间戳。某次 DNS 解析超时事件中,第④层快照显示 TIME_WAIT 连接达 28432,结合第⑤层发现配置中 dnsConfig.options.timeout 被错误覆盖为 1s(应为 5s),而压测环境因使用 mock DNS 未暴露此问题。
flowchart LR
A[告警触发] --> B[启动上下文快照]
B --> C[应用层Trace分析]
B --> D[JVM线程栈采样]
B --> E[eBPF Socket延迟]
B --> F[网络连接状态]
B --> G[配置版本审计]
C & D & E & F & G --> H[根因聚类引擎]
H --> I[生成可执行修复建议]
生产环境特有的观测盲区治理
我们发现三类高频盲区:① 内核 TCP fastopen 在 SYN 重传时丢失请求上下文;② gRPC 的 HTTP/2 流控窗口在突发流量下出现虚假流控(window_size=0 但实际无背压);③ Kubernetes kube-proxy IPVS 模式下 conntrack 表溢出导致新连接被 DROP,但 conntrack -S 显示计数正常——因内核未将 DROP 计入 stats。为此,在所有节点部署 eBPF 探针实时捕获 nf_conntrack_full 事件,并与 Prometheus 指标联动告警。
多维数据时空对齐技术
将压测报告中的“TPS 曲线”与生产故障时刻的“错误率热力图”进行时空对齐时,发现两者时间戳偏差达 8.3 秒(NTP 同步误差)。我们引入 Chrony + PTP 双校时机制,并在每个 span 中注入硬件级 TSC 时间戳。对齐后发现:压测中 99.99% 的慢请求集中在单个 Redis 分片,而生产环境中该分片负载仅占集群 12%,但其所在物理机 NVMe SSD 的 queue depth 持续饱和(iostat -x 输出 avgqu-sz > 128),该指标在压测机上被忽略。
