
如果你最近跟我一样在排查数据库响应变慢的问题大概率会跟磁盘性能较上劲。很多人一听到 dd 这个词第一反应是 Windows 下 Rufus 工具里的 dd 模式用来写启动盘的。其实 Linux 里那个真正的 dd 命令功能要暴力得多而它和 hdparm 搭配起来就是一套不需要额外装任何软件就能完成的快速磁盘性能测试方案。这篇文章聊聊我怎么用这两个命令给服务器磁盘做摸底包括哪些参数会骗人、什么情况下数字会虚高以及拿到结果后怎么判断磁盘到底有没有病。1. 为什么用 dd 和 hdparm 做磁盘摸底1.1 那次数据库变慢我先测盘而不是先查库有次在处理现场问题客户说数据库整体变慢日志里大量 timeout应用层指标却都正常。我当时的判断路径很简单先排除磁盘瓶颈再回头查数据库本身。原因也直接数据库这类应用对磁盘性能极其敏感慢查询、IO 等待、锁竞争最后都可能反映为磁盘扛不住。打开终端后我什么都没装先用了两条系统自带命令hdparm 和 dd。五分钟内拿到了初步结论。这不是说其他工具不好而是在快速定位是不是磁盘的问题这个场景下这两条命令自带、轻量、覆盖面足够。后来这套操作慢慢成了我的习惯不管客户环境能不能联网、有没有装额外工具这两条命令总能在最原始的 Linux 系统上跑起来。1.2 两条命令各自管哪一段链路hdparm 面向的是块设备本身它能查询磁盘的硬件参数也可以发起读取计时测试。它测的是操作系统到磁盘设备这一段的读取链路能直接看到设备的信息、缓存策略、DMA 模式等底层状态。而 dd 本质是一个数据复制工具但它可以用来构造指定大小的读写流量通过统计复制时间和数据量算出实际吞吐带宽。实用场景差别不小我整理了一张表方便对照工具面向对象能测什么测不了什么hdparm块设备 /dev/sda 等缓存读取速度、缓冲读取速度、设备硬件信息写入速度它不能安全地造写流量dd文件或块设备顺序读带宽、顺序写带宽、简单复制耗时随机 IOPS、多队列并发、延迟分布两者结合设备 文件系统路径快速判断顺序读写链路有没有明显硬件问题精细的业务负载模拟需要 fio 这类工具这对组合的价值在于互补hdparm 看读dd 看写和文件系统层表现。有不少新手拿 hdparm 测完读就说硬盘很快这是不完整的同样拿 dd 测写但没注意是否绕过缓存也会得出一个严重虚高的结果。后面我会把这里面的细节拆开讲。1.3 磁盘性能到底该看哪些数磁盘性能不是只有一个快慢指标。我一般把它拆成四个方面顺序读、顺序写、随机读、随机写。顺序读写关注的是大块数据连续搬运的能力比如文件拷贝、视频渲染、数据库备份恢复这类场景随机读写关注的是小块数据散落访问的能力比如 OLTP 数据库的频繁增删改查表现指标主要是 IOPS 和延迟。用 dd 和 hdparm 能比较精确地测出顺序读和顺序写的带宽随机 IO 只能靠小块参数间接估算测不出真正的 IOPS。所以我在快速摸底阶段会明确告诉自己现在测的是这条存储链路有没有明显毛病不是给存储做全面体检。如果顺序读写都正常那数据库变慢大概率不是磁盘硬件的问题可以安心往数据库参数、SQL 或锁方向查如果顺序读写都拉胯那基本可以确定存储层需要优先处理。2. hdparm 实操从一条 -Tt 到识别缓存欺骗2.1 最基本的测法和实测输出hdparm 最常用的测速参数是-Tt大小写有讲究-T是测缓存读取速度-t是测缓冲读取速度。直接对设备执行即可hdparm -Tt /dev/sda我拿一块典型 SATA 机械盘测过输出长这样/dev/sda: Timing cached reads: 2400 MB in 2.00 seconds 1200.00 MB/sec Timing buffered disk reads: 350 MB in 3.01 seconds 116.28 MB/sec第一行 cached reads 代表 CPU 缓存到内存缓存这条路径的读取能力数字通常非常夸张第二行 buffered disk reads 才是带着操作系统页缓存机制的从磁盘设备读上来的速度对机械盘来说 100 多 MB/s 是正常区间。如果看到第二行能到 500MB/s 以上说明设备大概率是 SSD。2.2 为什么有时会看到几十 GB/s 的离谱读数不少人刚接触 hdparm看到-T跑出来好几 GB/s 就兴奋得不行以为自己的磁盘是超级硬件。这里必须先泼一盆冷水-T的数字跟磁盘硬件几乎没有关系它测的是 CPU 到内存页缓存之间的数据搬运速度。内存本身就能跑到几十 GB/s所以这个数字只能证明你的内存和 CPU 缓存没坏。-t虽然是从设备读但也绕不过页缓存这一层。第一次读某个区域时操作系统会把数据放进 cache第二次再读就直接命中缓存不回磁盘了。换句话说同一块盘连续跑两遍hdparm -t第二遍的数字通常会比第一遍高出一大截因为部分数据已经在内存里了。这个特性用来观察有没有缓存命中没问题但要用它判断磁盘真实能力必须做额外处理。2.3 绕开缓存之后--direct 参数实测为了拿到更接近硬件真实水平的读数hdparm 提供了--direct参数它会让 IO 绕过页缓存直接发往磁盘设备hdparm --direct -t /dev/sda加了这个参数之后机械盘还是那个机械盘但 SATA SSD 在--direct下的读数通常能贴近 400~550MB/s 的接口上限NVMe 也能看到 1GB/s 以上的真实顺序读。对比一下加不加--direct的效果你会立刻理解为什么说缓存是测试最大的敌人。需要补充一点hdparm 毕竟是个老资格工具对 NVMe 设备的支持程度取决于发行版里的版本。我在 CentOS 7 的老内核上遇到过hdparm --direct -t /dev/nvme0n1直接报错的情况提示类似HDIO_DRIVE_CMD(identify) failed: Invalid argument。这种时候别死磕把读测速的任务交给 dd 的iflagdirect即可后续会讲到。2.4 顺带查设备信息避免被固件策略坑-I参数大写 i能查询设备身份信息我经常用它确认磁盘的型号、转速、缓存大小、是否支持高级 DMA 模式等hdparm -I /dev/sda输出里会有一大段字段重点关注Model Number和Rotation Rate。Rotation Rate如果是 7200 或 5400 这类具体数值就是机械盘如果是Solid State Device或者非易失性标记就是 SSD。这个信息能帮你快速校正预期测出来 450MB/s 的结果放在 SSD 上正常放在机械盘上就是有人在作弊。3. dd 读写测试最容易跑偏也最能说明问题3.1 写测试到底怎么写才不算投机取巧dd 测写性能的经典命令长这样dd if/dev/zero of/mnt/data/testfile bs1M count2048 oflagdirect convfdatasync这条命令的含义是从/dev/zero读无限零数据流写到/mnt/data/testfile每次 IO 块大小 1MB总共写 2048 次也就是 2GB 数据。oflagdirect让输出文件绕过页缓存直接以 O_DIRECT 方式写convfdatasync让命令在结束前强制把数据落盘避免 dd 退出时还有一堆脏页留在内存里。很多教程不会告诉你convfdatasync这个参数极其重要。如果不加你会发现大文件 dd 写测速能跑出好几个 GB/s但这其实是在写内存缓存真正的磁盘回写被延迟到了后台。加上convfdatasync后dd 的耗时包含了数据落盘的时间数字才贴近真实。如果连oflagdirect一起用测试结果更接近硬件本身的写能力。3.2 bs 和 count 的选法bs是单次 IO 的块大小count是块数量两者相乘就是总数据量。选 bs 时我遵循两个原则大块顺序带宽测试用 1M 起步。现代内核的 IO 调度器会把连续请求合并成大段1M 的块既能压出顺序带宽又不会因为块太大触发设备端的批处理策略差异。小块随机场景模拟用 4k 或 8k。比如数据库默认页大小通常就是 8k想看数据库场景的表现可以把 bs 设为 8k但这时候测出来的数字会比较难看因为小块 IO 的开销占比高这是正常的。count的选择取决于测试时长。我个人习惯让测试至少持续 10~20 秒太短的数据量容易被设备缓存、预热机制干扰。机械盘跑 2GB 大约十几秒SSD 可能几秒就跑完这时可以把 count 调大到 4096 或 8192让测试时间更充分。3.3 读测试与现场排障的小技巧读测试我通常会先清缓存再执行sync echo 3 /proc/sys/vm/drop_caches dd if/dev/sda of/dev/null bs1M count2048 iflagdirectiflagdirect让读取绕过页缓存直接测量磁盘到应用层的数据搬运速度。sync先保证脏页落盘再清空缓存这样测出来的读带宽基本就是硬件水平。读测试有个额外好处dd 在读到坏块时会直接报Input/output error并输出出错偏移量这对现场判断物理坏道非常有用。需要注意if/dev/sda是直接读整块设备对机械盘来说会触发全盘范围的数据读取耗时可能比预期长。如果只想测某个分区可以换成if/dev/sda1如果想测文件系统层表现而不是裸设备也可以把 if 指定成一个实际存在的文件。3.4 那些年我见过的假性能tmpfs 与压缩文件系统有一种特别坑爹的情况我踩过不止一次dd 写测试数字漂亮得吓人结果发现写到了 tmpfs 上。很多发行版把/tmp挂在 tmpfs这是内存文件系统读写速度就是内存速度跟磁盘半毛钱关系没有。执行测试前记得用df -hT确认目标路径对应的文件系统类型df -hT /mnt/data输出里如果看到tmpfs马上换路径。另一个容易被忽略的场景是 btrfs 或 ZFS 开了压缩属性。dd 从/dev/zero读出来的全是零字节压缩率极高写入的数据量被大幅压缩测试结果会夸大磁盘的真实吞吐。这种情况下我会换成伪随机数据源或者直接测试一个预先准备好的随机文件避免压缩机制干扰判断。4. 测试前的清场操作缓存清理与多轮采样4.1 一条需要 root 才能用的清缓存命令测试前清缓存是保证数据干净的关键步骤。执行下面的命令需要 root 权限sync echo 3 /proc/sys/vm/drop_cachesecho 3的含义是清空三类内核缓存数字 1 表示页缓存2 表示目录项与 inode 缓存3 表示全部清理。这里的sync必须提前执行因为 drop_caches 只回收干净缓存dirty 页需要先落盘才能被回收掉。如果不清缓存读测试时内核会把大部分读请求命中在内存上磁盘根本没被真正压到。生产环境执行这个操作要慎重。虽然 drop_caches 本身不会主动杀进程或停业务但它会让热点数据全部失效接下来一段时间内系统需要重新从磁盘加载数据可能引起瞬时 IO 飙升。我一般只在业务低峰期、或者确认这是测试专用窗口时才做。4.2 为什么多轮测试的数值会漂同一个命令连续跑三遍速度一轮比一轮快这不是磁盘超常发挥是缓存命中率在上升。第一轮冷缓存所有数据都得从介质上读数字最接近硬件水平第二轮有部分数据残留在页缓存里数字开始虚高第三轮如果读的范围完全重叠几乎全部命中缓存数字可以直接翻倍甚至更高。理解了这一点就知道为什么快速测试不能只看一次结果。我给自己定的规矩是至少测三轮每轮之间清一次缓存或者直接全程使用direct模式让缓存机制尽量不参与。读测试用iflagdirect是最省事的做法因为不需要每次清缓存系统自动绕过了页缓存。4.3 五轮采样取一个靠得住的中位数多轮采样的思路很直接跑五轮记录每次的速率去掉最高和最低取中间值或者直接看整体分布。读测试的串行命令可以写成for i in $(seq 1 5); do sync echo 3 /proc/sys/vm/drop_caches dd if/dev/sda of/dev/null bs1M count2048 iflagdirect done写测试每轮最好使用不同文件名结束后立即删除避免后续轮次写到已经被缓存预热的数据。示例如下for i in $(seq 1 5); do dd if/dev/zero of/mnt/data/t$i bs1M count2048 oflagdirect convfdatasync rm -f /mnt/data/t$i done写测试前务必确认剩余空间足够大我习惯预留至少 3 倍于测试文件大小的空间。否则一个 10GB 的测试文件可能把数据盘写满生产环境也不能幸免地会出问题。5. 拿到数据后知道好坏比跑出数字更难5.1 常见介质的大致带宽区间跑完测速后你需要一个参考系来判断数字正常不正常。以下是我的经验区间不同批次硬件会有差异但大体不会跑偏介质类型顺序读顺序写备注机械盘 SATA80~160 MB/s80~160 MB/s7200 转通常比 5400 转快单碟密度影响很大SATA SSD400~550 MB/s300~500 MB/sSATA 3.0 接口理论上限约 600MB/sNVMe SSD Gen31500~3500 MB/s800~3000 MB/s受主控、温度、剩余空间影响明显NVMe SSD Gen44000~7000 MB/s3000~6000 MB/s高温降速后可能掉到几百 MB/s如果你测得的结果严重低于这个区间比如一块 SATA SSD 顺序读只有 100MB/s那基本可以判断设备处于异常状态需要进一步排查。5.2 数字远低于预期时我会往哪几个方向查第一看接口协商速率。机械盘或 SSD 如果接在降速的 SATA 接口上速率会直接受限。用dmesg | grep -i sata能看到 link up 信息如果是 1.5Gbps 而非 3.0Gbps那问题多半出在接口或线缆上。第二看磁盘写缓存策略。很多阵列卡默认关闭了写缓存导致写入性能比正常值掉一个量级。比如一块机械盘阵列开写缓存前可能只有 50MB/s开启后能到 500MB/s 以上差距极大。第三看分区对齐和系统配置。老系统的磁盘分区如果没按 4K 对齐SSD 的小块 IO 性能会受到严重影响不过 dd 这种大块顺序测试往往看不出来要配合 fio 才明显。另外系统 IO 调度器、固件版本也可能造成性能波动这个需要结合具体环境判断。5.3 什么时候应该从 dd/hdparm 升级到 fio虽然标题写的是 dd 和 hdparm但我必须诚实这两个工具测不了真正的随机 IOPS、多队列并发和混合读写压力。如果目标是评估数据库业务负载我会在初筛通过后上 fio。比如随机读测试fio -namerandread -rwrandread -bs4k -iodepth32 -runtime30 -numjobs4 -filename/mnt/data/fio_testfio 能模拟队列深度 32 甚至更高的并发 IO厂商标称的 IOPS 数字也大多是在这种条件下测出来的。我的工作节奏是先花五分钟用 dd 和 hdparm 做健康性初筛确认没有明显的顺序读写瓶颈和硬件报错如果初筛正常但业务仍然慢再花十分钟用 fio 做业务级压测。这样既有速度又有深度不会一上来就被 fio 的输出信息淹没了判断力。最后分享一个我自己的操作习惯无论测试结果多好看动手之前先确认三件事——目标路径挂在哪个文件系统上、磁盘剩余空间是多少、当前系统的 IO 负载高不高。曾经有一次在客户环境里没注意测试路径是 tmpfsdd 写了几 GB 数据下去数字漂亮得吓人但一卸载就什么都没了。测完也别急着走生产环境记得删掉测试文件把速率记录存档下次再做同类测试时才有对比基础。这套流程跑熟了五分钟给一台服务器做完磁盘健康初筛并不是夸张的说法。