资讯详情

blktrace与fio实战:录制真实业务I/O并回放验证存储性能

📅 2026/10/20 6:12:51 | 华诺云谱 👁 阅读
blktrace与fio实战:录制真实业务I/O并回放验证存储性能
做存储性能分析久了你会发现一个很尴尬的问题我们在测试环境里用fio造的随机读写负载跟生产环境里真实业务产生的I/O根本是两个世界。测试测出来的漂亮数字拿到生产环境一跑业务方完全不认账。要解决这个问题思路其实很朴素——既然合成负载不真实那就把真实负载录下来再拿到目标硬件上重放。这就是Linux下blktrace配合fio回放要做的事情。这篇文章把我实际用过的一套流程完整写出来包含抓取、解析、转换、回放的每一个环节以及我在操作中踩过的坑和验证过的参数给想用这套方法做存储选型和性能验证的工程师做个参考。这套方案适合谁如果你是做存储系统测试、数据库性能调优、SRE容量规划或者正在纠结换一台新的存储阵列表现到底能不能扛住现有业务那这篇文章可以直接帮你省掉大量摸索时间。不需要理解内核源码只要照着流程走就能把一段真实业务I/O完整搬到另一台设备上重放并量化对比新旧环境的性能差异。1. blktrace的核心原理为什么它能还原真实I/O1.1 从iostat看不到的东西说起日常排查I/O问题大家最常用的是iostat。它能告诉我们设备当前的IOPS、吞吐量、平均队列长度、平均服务时间这些聚合指标确实有用但有个天然缺陷它丢掉了每一次I/O的细节。就像体检报告告诉你平均心率80但你看不到这80次心跳里有多少次是剧烈运动后的160又有多少次是安静睡眠中的55。blktrace完全不是这个思路。它在内核块设备层插桩把每一个到达块设备的I/O请求都记录下来包括设备号、时间戳、操作类型读/写/丢弃、起始扇区、I/O大小、下发进程名和进程ID。也就是说它能给你一份完整的24小时心电图。拿到这份记录之后你可以精确回答这些问题这台设备上一小时发起了多少次4KB随机写队列深度峰值是多少写放大来自哪里哪个进程在半夜产生大量顺序读blktrace的记录点覆盖了整个block层的I/O路径从IO请求进入块层Q到驱动处理D再到完成C每一段耗时都能被拆开分析。这也是为什么它在I/O路径问题排查中比iostat这类统计工具强大得多。1.2 为什么抓完trace还要回放录制trace本身只能用于离线分析真正有价值的是把这份真实I/O负载在新设备上重新跑一遍。举几个典型场景第一硬件替换验证。你想把现有的机械盘阵列换成全闪阵列光靠两组fio基准测试根本没法回答业务会不会变快这个问题。因为真实业务是随机写和顺序读混合、I/O大小不均、并发度波动的复杂模式基准测试的固定随机比例完全模拟不了。把生产环境磁盘上的I/O跟踪下来原样回放到新存储上测试结果才有说服力。第二内核参数和文件系统调优。修改I/O调度器、切换文件系统、调整RAID条带策略这类变更的影响往往只体现在特定负载模式下。有了真实trace你可以在同一台机器上只改变一个参数其余完全不变两次回放的结果直接对比说服力远强于跑两遍benchmark。第三疑难问题复现。业务反馈某段时间磁盘特别慢但等你到场时问题已经过去了。用blktrace在类似业务时段抓一段trace回到测试环境反复回放或者用btt工具分析各阶段耗时大概率能找到根因——到底是设备服务时间长还是队列堆积严重还是一堆进程在打架。1.3 工具族的分工这套流程里主要涉及四个工具给还没用过的读者先理清关系工具职责典型用法blktrace内核块层I/O跟踪器直接抓取traceblktrace -d /dev/sdb -w 60blkparse解析blktrace产生的二进制日志生成可读文本或转换为fio可用格式blkparse -i trace -d merge.binbtt对blkparse输出做各阶段时间分析生成延迟统计btt -i parsed.txtfio负载生成和回放工具支持blktrace格式的I/O日志回放fio --ioengineblktrace --read_iolog...blktrace抓取原始数据blkparse是翻译官btt做延迟归因分析fio做最终回放。这套组合拳打下来一套完整的工作流就闭环了。2. 抓取前的准备工作内核、工具与规划2.1 内核与环境是否就绪blktrace依赖内核的块层I/O跟踪支持绝大多数发行版默认开启但动手之前还是花一分钟确认一下避免抓了半天发现trace是空的。# 检查内核是否开启BLK_DEV_IO_TRACE grep CONFIG_BLK_DEV_IO_TRACE /boot/config-$(uname -r) # 挂载debugfs mount -t debugfs debugfs /sys/kernel/debug # 确认三个核心工具都装了 which blktrace blkparse btt fio如果grep结果显示CONFIG_BLK_DEV_IO_TRACEydebugfs挂载成功工具都存在环境就没问题。需要注意有些容器环境或精简系统没有把debugfs挂上这一步必须手动处理。2.2 抓取窗口怎么选才有代表性这是整条流程里最容易翻车的一步。很多新手随便抓个30秒就开始回放结果得到的trace完全不能代表真实负载。我的建议是三条原则覆盖一个完整的业务周期。如果业务是白天高峰期明显、夜间低峰那就抓白天的峰值时段如果是每天凌晨有定时任务就把定时任务时段包含进去。抓取时间不低于5分钟。太短的数据受偶然性影响很大太长又会引入过多数据量。我一般取10到15分钟既能稳定反映负载特征文件体积又在可控范围内。抓取期间记录业务侧元信息。实时记录TPS、延迟指标、在线用户数、并发任务数回放时方便对照。哪怕只是随手在终端记录几个关键值后面分析都会轻松很多。2.3 脚本化抓取与元信息记录生产环境抓trace效率很重要。我习惯写一个简单的抓取脚本把初始信息和结束信息都留下#!/bin/bash DEV/dev/sdb OUTtrace_sdb_$(date %Y%m%d_%H%M%S) DURATION600 echo start: $(date %F %T) ${OUT}_meta.txt iostat -x 1 $DURATION ${OUT}_meta.txt IOSTAT_PID$! blktrace -d $DEV -w $DURATION -o $OUT kill $IOSTAT_PID 2/dev/null echo end: $(date %F %T) ${OUT}_meta.txt脚本会在抓trace的同时用iostat每秒记录一次设备状态作为背景资料。回放结束后的对比分析里这份背景资料经常能解释一些奇怪现象。3. 实战抓取blktrace命令参数与性能开销3.1 一条抓取命令的完整拆解blktrace命令看起来简单但参数并不少。我用一条比较完整的命令来说明blktrace -d /dev/sdb -a read -a write -a discard -o disk_trace -w 600 -b 4096 -n 8各参数含义-d /dev/sdb指定跟踪的块设备可以同时跟多个设备。-a read -a write -a discard设置要跟踪的I/O类型。默认全部跟踪如果只需要读写用-a过滤能显著降低开销和文件体积。-o disk_trace输出文件前缀。实际会产生以CPU编号结尾的多个文件比如disk_trace.blktrace.0、disk_trace.blktrace.1。-w 600跟踪时长单位秒。600秒就是10分钟。-b 4096每CPU缓冲区大小单位KB。这个参数影响大I/O下是否丢事件。-n 8使用8个线程通常建议等于CPU核数。如果不想限制I/O类型直接去掉-a参数即可下面大多数场景我都用全量跟踪。3.2 输出文件为什么要合并一个比较困惑新手的点是明明只指定了一个设备抓完却多了一堆文件。这是因为blktrace按CPU编号分别输出日志每个CPU核心一个文件。默认情况下文件后缀就是CPU编号如果不合并处理后续解析和回放会非常麻烦。合并用blkparse的-i参数指定前缀即可它会自动把所有CPU的trace合并成一个可读文件。而如果想生成fio能够直接回放的二进制文件则用-d参数# 合并输出可读文本 blkparse -i disk_trace -o disk_trace.txt # 合并输出fio可用的二进制文件 blkparse -i disk_trace -d disk_trace_merge.bindisk_trace_merge.bin就是我们后面要交给fio的关键文件。这个例子虽然简单但很多人卡在这一步——直接拿.blktrace.0给fiofio只读到了其中一个CPU的trace回放效果会严重失真。3.3 抓取时的性能损耗与trace完整性判断blktrace不是零开销的工具。每一条I/O都要经过内核插桩点记录、写入缓冲区、再刷到磁盘这在高速设备上会带来不可忽视的消耗。抓取期间业务本身可能就有几个百分点的性能下降这在生产环境是可以接受的但要提前和相关团队打招呼。更要紧的是判断trace有没有丢事件。blktrace内部有丢事件计数机制抓取完成后blkparse解析时会给出警告信息。我遇到过几次因为缓冲区太小高I/O下大量事件被丢弃的情况回放出来的负载明显偏弱。排查方法是抓取时用-b调大缓冲区并用-n增加线程数。如果丢事件依然严重就缩短抓取窗口或者过滤掉不需要的I/O类型。4. 用blkparse解析与btt定位瓶颈4.1 blkparse可读输出的字段含义合并后的文本文件每行对应一条I/O事件字段顺序如下设备号 CPU编号 序列号 时间戳 动作类型 起始扇区 大小 进程名 进程ID 8,16 0 1 0.001234 G 1024 8 app_x 12345其中比较关键的是动作类型常见的有QI/O请求进入块层队列。GI/O请求获得请求描述符。II/O请求被插入调度队列。DI/O请求被下发给驱动。CI/O请求完成。拿到时间戳和动作类型就能算出一个I/O在队列里待多久、在驱动阶段耗时多久这是btt分析的基础。4.2 btt各阶段延迟的含义btt工具会把每次I/O的完整生命周期拆成几段我简单解释几个最常见的术语Q2G从进入块层到获得请求描述符的等待时间反映请求分配器是否存在瓶颈。G2I从获得请求描述符到插入调度队列的时间通常很短。I2D在调度队列中的等待时间反映队列深度和调度器策略的影响。D2C从下发到驱动到I/O完成的时间这是设备真实服务时间的重要参考。Q2C从进入块层到完成的总时间就是端到端的I/O延迟。如果发现I2D很高说明队列堆积是主因如果D2C很高那问题多半出在设备本身。这个区分在做问题定位时价值极高很多慢I/O问题只需要跑一次btt就能看清方向。4.3 生成fio可回放的数据文件既然重点是回放这一步的产出就是前面提到的disk_trace_merge.bin。再强调一次blkparse -i disk_trace -d disk_trace_merge.bin这个-d参数把多个CPU的二进制trace合并成一个文件数据格式fio可以直接识别。很多教程没有说清楚这一步直接拿原始逐CPU文件喂给fio结果回放行为完全错误。5. fio回放trace的完整方法5.1 两种回放路线的取舍fio回放blktrace数据有两条路线一条是专属的blktrace引擎一条是通用的iolog机制。这两条路线的区别我整理成下表对比项ioengineblktraceread_iolog libaio/sync数据来源blkparse -d合并的二进制文件文本格式iolog配置复杂度低一条命令搞定需要先转换格式灵活性低参数较少高可以自由选择ioengine维护状态新版fio中标记为deprecatedfio主推方式适用场景快速回放、原样复现需要微调负载或换引擎验证我的建议是快速复现用方式一涉及二次加工或者需要换不同引擎对比时用方式二。两种方法下面都给出完整实操。5.2 方式一用ioengineblktrace直接回放这是我日常用得最多、也最省事的方案。前提是已经用blkparse合并好了二进制文件fio \ --namereplay \ --filename/dev/sdc \ --ioengineblktrace \ --read_iologdisk_trace_merge.bin \ --thread \ --direct1 \ --outputreplay_result.txt几个关键点--filename/dev/sdc指定回放的目标设备。生产环境务必确认这是你要写入的设备回放会真实落盘写错设备后果很严重。--direct1绕过页缓存直接下发I/O保证测的是设备真实性能。--thread以线程方式运行避免初始化时的fork开销影响测试结果。回放结束后fio会把IOPS、带宽、延迟分布的统计信息写在输出文件里。此外fio还支持--replay_redirect参数把原本指向原设备的重定向到新设备。在我印象中有些场景下直接指定--filename配合read_iolog就足够replay_redirect通常在处理多设备trace时才需要。5.3 方式二先把trace转成文本iolog再回放如果你需要在回放前改动负载特征比如只回放写I/O、增大I/O大小、加快时间比例用文本iolog更合适。转换过程可以借助blkparse的自定义输出格式blkparse -i disk_trace \ -f %T -1 %S %R %d %w %N\n \ -o disk_trace_iolog.txt生成的文件每行格式大致是时间戳 -1 操作类型 起始扇区 大小 进程名然后直接用通用引擎回放fio \ --namereplay_text \ --filename/dev/sdc \ --ioenginelibaio \ --read_iologdisk_trace_iolog.txt \ --direct1 \ --outputreplay_text_result.txt这条路线的优势是文本格式你可以用任意脚本去修改比如把随机写比例降低、把所有I/O大小放大4倍再回放看效果。缺点是转换格式时字段顺序定义容易出错我建议先转一个短trace用head查看前几行确认格式正确后再做全量回放。5.4 回放的核心参数逐个说fio回放blktrace数据时有几个参数决定回放行为必须理解清楚replay_no_stall默认情况下fio会严格按原始trace的时间戳来间隔下发I/O。真实业务中存在大量空闲等待导致回放时间非常长。设置--replay_no_stall1后fio忽略原始间隔以最大努力连续下发I/O回放速度会大幅提升。但代价是队列深度特征会改变。replay_scale控制回放速度的倍率默认1.0。比如--replay_scale2.0表示I/O下发速度加快一倍适合把10分钟trace在5分钟内跑完。注意这个参数是整体加速不改变I/O大小和方向只改变时间维度的压缩比。replay_align对齐参数某些场景下需要将I/O起始扇区对齐到指定边界。如果不清楚底层物理块边界建议保持默认过度对齐反而会改变I/O特征。replay_time_scale与replay_scale类似但更精细控制时间戳的缩放因子。我在需要精确匹配时间长度时更爱用它。这些参数的使用建议是第一次回放先全部保持默认跑一次看看总耗时是否可接受再决定是否启用加速。不要为了图快一上来就把no_stall打开那样可能会掩盖真实的并行行为。5.5 回放到新设备前的安全检查这里必须多说几句因为回放会真实写数据数据安全是底线。第一确认目标设备是测试盘。反复确认三遍/dev/sdc不是系统盘不是有业务数据的盘。我习惯用lsblk查看设备挂载路径后再行动。第二确认设备容量足够。原始trace里的扇区号一旦超出目标设备容量I/O会失败回放中断。可以用--replay_redirect或脚本检查最大扇区。容量不对时的做法是先用--replay_align做偏移或者在测试环境用同样大小的空白镜像。第三确认目标设备的拓扑和原设备一致。原trace里是机械盘的扇区寻址模式回放到SSD上时由于延迟分布差异巨大最终性能数据只能作为参考不能直接等同于真实业务切换后的表现。但如果目标是做新存储性能验证这反而是我们想看的——同样的负载新设备能跑多快。6. 一个完整案例从生产设备抓取到新存储回放6.1 场景描述与原trace特征为了把整个流程串起来我以一个虚构的典型案例说明。假设某业务系统使用机械硬盘存储近期响应延迟升高团队打算验证换成全闪阵列后的效果。操作计划是在业务高峰期从原数据盘抓取15分钟I/O回到新全闪阵列上重放。原始环境# 抓取15分钟900秒 blktrace -d /dev/sdb -o peak_trace -w 900 # 合并解析 blkparse -i peak_trace -d peak_trace_merge.bin抓取期间从meta信息可以看到原机械盘平均IOPS约420平均吞吐约180MB/s峰值延迟明显偏高。这些信息为后面对比提供了参照。6.2 回放命令与过程输出目标新设备是/dev/sdc一块全闪阵列。回放命令fio \ --namereplay_to_flash \ --filename/dev/sdc \ --ioengineblktrace \ --read_iologpeak_trace_merge.bin \ --thread \ --direct1 \ --replay_no_stall1 \ --outputreplay_flash_result.txt这里我开了replay_no_stall1原因是抓取到的15分钟生产trace中包含了不少业务空闲期如果完全按原始时间戳回放要跑很久才能完成。开启无间隔模式后半个多小时就跑完了。最终fio输出文件的关键部分摘录如下read: IOPS2134, BW913MiB/s, lat (msec): min0.42, max38.2, avg8.37 write: IOPS987, BW389MiB/s, lat (msec): min0.31, max29.5, avg6.11相比原机械盘平均420 IOPS的数据新阵列在同负载下IOPS和带宽都有了数量级提升平均延迟也大幅下降。这组数据基本可以支撑存储替换决策。6.3 结果解读不要只看平均数这个案例中表面上一切都很漂亮但实际解读时有个细节必须注意replay_no_stall1改变了请求的下发节奏。原始trace里业务是瞬时高并发空闲交替的而回放时变成了持续高并发。对于SSD这类没有寻道时间的设备这种差异影响不大因为SSD天然擅长处理高并发但如果回放目标也是机械盘no_stall下测出的结果会明显好于真实业务表现因为机械盘最怕的就是频繁切换寻道位置。所以最终的结果报告里应当区分原样回放和满载回放两组数据并说明使用的参数。这种严谨性做性能测试的人一定要有。另外我还习惯在回放后用iostat再看一次设备侧指标确认fio输出和设备侧数据对得上。有一次fio显示IOPS很高但iostat看设备利用率只有70%排查后发现问题出在队列深度不够不是设备瓶颈。7. 常见问题与排查技巧实录7.1 问题排查速查表现象可能原因解决办法blktrace抓下来的文件为空debugfs未挂载内核未开启跟踪支持mount -t debugfs debugfs /sys/kernel/debug重新编译内核打开CONFIG_BLK_DEV_IO_TRACEblkparse解析时报大量dropped events内核缓冲区太小高I/O下丢事件用-b调大缓冲区减少跟踪的I/O类型缩短时长fio提示无法识别trace文件直接拿了逐CPU文件给fio没有经过blkparse -d合并用blkparse -i 前缀 -d 合并文件后再回放回放时I/O全部失败trace里的最大扇区超出目标设备容量检查最大扇区使用更小偏移或换更大容量目标盘回放速度比原始trace慢得多默认按原始时间戳间隔下发包含业务空闲期加--replay_no_stall1或调整replay_time_scale回放结果和原设备性能完全不可比目标设备拓扑/接口差异direct模式未开启确认direct1明确对比基线区分队列深度影响7.2 几个值得分享的实操心得第一抓取trace后先做一次快速预检而不是急着回放。用blkparse生成文本后用grep -c D 统计一下下发事件数量和抓取期间iostat记录的IOPS总数交叉验证。两者数量级偏差过大说明trace不完整回放结果就没意义。第二文本iolog格式转换时测试小样本先行。我吃过亏有一次转换时字段顺序写错fio把所有读操作都当成了写操作差一点把一个测试盘的原有数据覆盖掉。虽然fio有护栏但这种事情宁可不发生。先用几十秒的小trace转换跑一次看行为对不对再全量操作。第三频繁回放时记得清理目标设备上的文件系统痕迹。如果回放到一个已有文件系统的块设备fio会直接操作裸设备旧文件系统元数据会让测试盘空间布局混乱。我习惯在每次回放前用wipefs清掉旧签名确保测试从干净状态开始。第四回放结果要想和原业务对比最好同时保留业务侧的延迟监控。光靠fio的延迟统计只能说明这台测试设备在相同负载下的I/O延迟无法反映真实业务栈文件系统、应用逻辑、网络里的感知延迟。两者结合才是一份完整的评估报告。7.3 新版fio的兼容性提醒新版fio已经在逐步淘汰ioengineblktrace文档里标注为deprecated推荐使用通用iolog机制。如果你用的是最新版本fio可能执行方式一的时候会看到警告但功能本身还能用。为了可持续发展有条件的话建议把流程逐步迁移到read_iolog 文本iolog的路线数据文件更透明将来fio移除旧引擎时候不会措手不及。结尾一点个人经验这套blktrace fio回放的流程我用下来最大的感受是它解决了存储评测里最尴尬的负载真实性问题但用好的前提是理解每个工具在做什么、每一步转换在做什么。把真实业务负载录下来不难难的是在回放时不被各种参数误导能清醒地判断结果到底说明什么。我个人在实际操作中的习惯是永远保留原始trace文件任何回放副本都标注参数和来源这样即使过半年回来看结果也能定位当时的复现条件。最后再分享一个小技巧无论测试结论多漂亮真正的答案是上线后让业务自己跑一遍。回放能帮你筛掉90%的错误选择但剩下10%的真实业务表现只有时间知道。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑