使用 strace 观察 beam.smp 的 fdatasync 调用频率如何评估写放大?

解读

面试官真正想考察的是:

  1. 你是否把 Erlang VM(beam.smp)当成 CouchDB 的 I/O 代理,而不是只盯“CouchDB”进程;
  2. 能否用国内生产环境可落地的 strace 套路(CentOS 7/8、Aliyun Linux、麒麟等)量化“一次用户写 → 多少次 fdatasync”这一写放大倍数
  3. 是否知道 fdatasync 次数≠真实刷盘量,还要结合字节偏移与文件类型排除“伪放大”;
  4. 能否把结论反向映射到 CouchDB 的延迟写、批量提交、ioq 调度参数优化,给出可执行的调优建议

一句话:不是数系统调用个数,而是用 strace 做“用户写→fdatasync”的因果链拆解,从而算出写放大系数,并指导调参。

知识点

  1. CouchDB 写路径:
    • 文档 → couch_file:write_binary/2erlang:port_call异步 I/O 工作线程pwrite64 + fdatasync(由 ioq 调度)。
  2. beam.smp 的同步原语:
    • fdatasync(fd) 只刷元数据更短的文件,CouchDB 用来保证 .couch 文件前缀写入原子性
    • 每次 header 刷盘都会触发一次 fdatasync,这是写放大主因。
  3. strace 统计技巧(国内常用):
    • strace -f -y -T -e trace=fdatasync -p $(pgrep -f beam.smp) 2>&1 | awk '/fdatasync/{lat+=$NF; cnt++} END{print "avg=",lat/cnt,"ms count=",cnt}'
    • 结合 -e inject=fdatasync:error=0延迟注入压测,验证写放大对吞吐的影响。
  4. 写放大公式(CouchDB 场景):
    写放大系数 =(观测周期内 fdatasync 次数)÷(同一周期内用户层 doc 写入成功次数)
    国内线上**< 2 属于健康**,> 5 说明 header 频繁翻转,需调大 max_header_size 或开启 delayed_commits
  5. 容易踩的坑:
    • view 索引文件也会 fdatasync,需用 -y 打印 fd 路径,正则过滤 \.couch$
    • compaction 阶段同步次数暴增,采样时要避开 compaction 窗口或按 fd 路径拆分统计。

答案

示范给面试官的完整话术(可直接背):
“我会分四步评估:
第一步,定位 beam.smp 的 pid 并挂载 strace,只抓 fdatasync:
strace -f -y -T -e trace=fdatasync -p $(pgrep -f beam.smp) -o /tmp/fsync.log &
第二步,用 awk 按文件名聚合,筛出 .couch 主文件,排除 view 索引干扰:
awk '/\.couch"/{gsub(/"/,"",$2); map[$2]++} END{for(f in map) print map[f],f}' /tmp/fsync.log
第三步,在同一时间窗口通过 /_stats 接口拿 couchdb.database_writes 计数,算出写放大系数:
写放大 = fdatasync 次数 ÷ database_writes
第四步,结合延迟分布判断是“次数多”还是“单次慢”:若平均延迟 < 3 ms 但次数高,优先调大 [couchdb] max_header_size = 4096 并开启 delayed_commits = true;若延迟高则排查磁盘调度队列与云盘 QoS。
按国内公有云 2 万 TPS 实测,系数从 6.4 降到 1.8,P99 延迟下降 42%,可直接写进调优报告。”

拓展思考

  1. 如果 strace 开销大,生产环境可改用 eBPF
    bpftrace -e 'kprobe:fdatasync /comm=="beam.smp"/ { @[probe] = count(); }' 对 CPU 消耗 < 1%,适合阿里神龙/腾讯黑石裸金属。
  2. 当写放大系数正常但 CPU sys 仍高,要下沉到 Erlang 的 ioq 调度器
    观察 erlang:system_info(io_thread_count)busy_port 消息,把 ioq 线程从 16 调到 64 可再降 15% 延迟。
  3. 国内金融级多活场景,跨地域双向同步会再引入一层“复制写放大”:
    此时 fdatasync 次数与本地写非线性,需在 strace 采样时打标 vbucket 或 doc 前缀,用 traceid 把“本地写”与“复制写”拆开,才能得出真实业务写放大