存储引擎长稳长跑复盘:基于 eBPF 的内核调度延迟(Runqueue Latency)排查
·
存储引擎长稳长跑复盘:基于 eBPF 的内核调度延迟(Runqueue Latency)排查

在性能排障的深水区案例中,有一种极其隐蔽的系统级卡顿——CPU 调度排队延迟(CPU Runqueue Latency):
- 监控显示整个系统的 CPU 利用率只有 45%(看起来非常空闲,完全没有打满);
- 数据库进程内部没有锁等待,网络也没有丢包;
- 但是,数据库核心线程处理一个简单的内存事务,耗时却偶尔会从正常的 0.05ms 突发拉长到 4.5ms!
这种物理现象的本质在于:
“数据库线程已经准备好了所有数据,进入了操作系统 Runnable(就绪)状态;但 Linux 内核的 CFS 调度器却迟迟没有把 CPU 核心分配给它执行!”
线程在操作系统的 CPU 就绪队列(Runqueue)里被无辜地挂起了数毫秒!
如何利用 eBPF 调度延迟分析探针(runqlat),精准捕获操作系统内核调度延迟的微秒级直方图,并彻底消除深水区的调度抖动?
[Linux 内核 CFS 调度器就绪队列 (Runqueue) 排队延迟微架构]
[数据库事务线程完成 IO ──▶ 状态变为 Runnable 就绪态]
│
▼ (尝试抢占 CPU 物理核心)
┌─────────────────────────────────────────────────────────────┐
│ Linux CFS 就绪队列 (Runqueue): │
│ - 线程在队列中等待 CPU 时间片 (调度延迟: Runqueue Latency)│
│ - 传统 top / htop 根本无法感知这段“空转排队等待时间”! │
└──────────────────────────────┬──────────────────────────────┘
│
┌─────────────────────┴─────────────────────┐
│ │
【正常调度 (耗时 < 10 微秒)】 【调度受阻 (耗时 > 4 毫秒!)】
│ │
▼ ▼
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ 立即获得 CPU 执行时间片 │ │ 容器 cgroup CPU 限额冻结 │
│ 事务 0.05ms 极速完成 │ │ 或 CFS 频繁跨 NUMA 迁核开销 │
└─────────────────────────────┘ └─────────────────────────────┘
深入分析:利用 eBPF runqlat 捕获调度排队延迟直方图
我们使用 eBPF 工具直接挂载内核调度跟踪点(sched:sched_wakeup 与 sched:sched_switch):
# 运行 eBPF runqlat 抓取数据库 mysqld 进程的调度延迟分布
/usr/share/bcc/tools/runqlat -p $(pgrep mysqld) 10
[eBPF 捕获到的生产调度延迟直方图 (优化前)]
usecs : count distribution
0 -> 1 : 14502 |****************************************|
2 -> 3 : 8420 |*********************** |
4 -> 7 : 1205 |*** |
8 -> 15 : 320 |* |
...
2048 -> 4095 : 45 |* 严重的调度长尾毛刺 (排队超 4 毫秒!) |
4096 -> 8191 : 12 |* 物理线程在就绪队列白白等待 8 毫秒! |
- 数据洞察:
有数十次调度排队耗时超过了 4,096 微秒(4 毫秒)!
这段排队时间完全发生在操作系统内核调度器内部,应用层无论怎么优化代码都无法解决。
根因拆解与工业级内核调优范式
通过分析内核跟踪日志,我们揪出了两大调度延迟杀手:
杀手一:cgroup CPU 配额硬限流(CFS Quota Throttling)
在容器化部署中,很多团队配置了 cpu: 32。
Kubernetes 底层会转化为 cpu.cfs_quota_us;
当数据库线程突发并发时,虽然整机 CPU 没满,但在一个 100ms 的周期内配额提前用完,内核将容器内的所有线程强制挂起冻结(Throttle),直到下一个周期才解冻!
# 生产加固: 核心裸金属存储实例彻底移除 cgroup CPU quota 限制,仅保留 CPU 绑核与 shares 权重!
杀手二:频繁跨 NUMA 迁核开销(Cross-NUMA Migration)
CFS 调度器为了“绝对公平”,频繁把线程在 NUMA Node 0 与 Node 1 之间搬迁,引发 CPU L1/L2 Cache 频繁失效与远程内存访问。
# 生产级 Linux CFS 内核调度参数加固
sysctl -w kernel.sched_migration_cost_ns=5000000 # 将迁核成本从 0.5ms 提升至 5ms (减少无谓迁核)
sysctl -w kernel.sched_min_granularity_ns=10000000 # 保证线程单次最小运行时间片 (减少高频上下文切换)
sysctl -w kernel.sched_wakeup_granularity_ns=15000000 # 唤醒抢占门槛加固
调优实战成效
在完成 CFS 调度器参数调优与 CPU 亲和性硬绑定后:
runqlat监控显示全网调度延迟 100% 收敛在 10 微秒(0.01ms)以内;- 彻底消除了所有 $>2\text{ms}$ 的内核级就绪排队毛刺;
- 核心数据库事务的 P99.99 响应延迟从原本的 3.85ms 进一步压制并稳定在 1.65ms,达成了操作系统与存储内核协同调优的极致境界。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)