☰
dd与hdparm实战:快速排查磁盘性能问题,避开缓存陷阱
2026/9/30 3:05:47 网站建设 项目流程

如果你最近跟我一样在排查数据库响应变慢的问题,大概率会跟磁盘性能较上劲。很多人一听到 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 的iflag=direct即可,后续会讲到。

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 bs=1M count=2048 oflag=direct conv=fdatasync

这条命令的含义是:从/dev/zero读无限零数据流,写到/mnt/data/testfile,每次 IO 块大小 1MB,总共写 2048 次,也就是 2GB 数据。oflag=direct让输出文件绕过页缓存,直接以 O_DIRECT 方式写;conv=fdatasync让命令在结束前强制把数据落盘,避免 dd 退出时还有一堆脏页留在内存里。

很多教程不会告诉你,conv=fdatasync这个参数极其重要。如果不加,你会发现大文件 dd 写测速能跑出好几个 GB/s,但这其实是在写内存缓存,真正的磁盘回写被延迟到了后台。加上conv=fdatasync后,dd 的耗时包含了数据落盘的时间,数字才贴近真实。如果连oflag=direct一起用,测试结果更接近硬件本身的写能力。

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 bs=1M count=2048 iflag=direct

iflag=direct让读取绕过页缓存,直接测量磁盘到应用层的数据搬运速度。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_caches

echo 3的含义是清空三类内核缓存:数字 1 表示页缓存,2 表示目录项与 inode 缓存,3 表示全部清理。这里的sync必须提前执行,因为 drop_caches 只回收干净缓存,dirty 页需要先落盘才能被回收掉。如果不清缓存,读测试时内核会把大部分读请求命中在内存上,磁盘根本没被真正压到。

生产环境执行这个操作要慎重。虽然 drop_caches 本身不会主动杀进程或停业务,但它会让热点数据全部失效,接下来一段时间内系统需要重新从磁盘加载数据,可能引起瞬时 IO 飙升。我一般只在业务低峰期、或者确认这是测试专用窗口时才做。

4.2 为什么多轮测试的数值会漂

同一个命令连续跑三遍,速度一轮比一轮快,这不是磁盘超常发挥,是缓存命中率在上升。第一轮冷缓存,所有数据都得从介质上读,数字最接近硬件水平;第二轮有部分数据残留在页缓存里,数字开始虚高;第三轮如果读的范围完全重叠,几乎全部命中缓存,数字可以直接翻倍甚至更高。

理解了这一点,就知道为什么快速测试不能只看一次结果。我给自己定的规矩是:至少测三轮,每轮之间清一次缓存,或者直接全程使用direct模式,让缓存机制尽量不参与。读测试用iflag=direct是最省事的做法,因为不需要每次清缓存,系统自动绕过了页缓存。

4.3 五轮采样,取一个靠得住的中位数

多轮采样的思路很直接:跑五轮,记录每次的速率,去掉最高和最低,取中间值或者直接看整体分布。读测试的串行命令可以写成:

for i in $(seq 1 5); do sync echo 3 > /proc/sys/vm/drop_caches dd if=/dev/sda of=/dev/null bs=1M count=2048 iflag=direct done

写测试每轮最好使用不同文件名,结束后立即删除,避免后续轮次写到已经被缓存预热的数据。示例如下:

for i in $(seq 1 5); do dd if=/dev/zero of=/mnt/data/t$i bs=1M count=2048 oflag=direct conv=fdatasync 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/s
NVMe 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 -name=randread -rw=randread -bs=4k -iodepth=32 -runtime=30 -numjobs=4 -filename=/mnt/data/fio_test

fio 能模拟队列深度 32 甚至更高的并发 IO,厂商标称的 IOPS 数字也大多是在这种条件下测出来的。我的工作节奏是:先花五分钟用 dd 和 hdparm 做健康性初筛,确认没有明显的顺序读写瓶颈和硬件报错;如果初筛正常但业务仍然慢,再花十分钟用 fio 做业务级压测。这样既有速度,又有深度,不会一上来就被 fio 的输出信息淹没了判断力。

最后分享一个我自己的操作习惯:无论测试结果多好看,动手之前先确认三件事——目标路径挂在哪个文件系统上、磁盘剩余空间是多少、当前系统的 IO 负载高不高。曾经有一次在客户环境里没注意测试路径是 tmpfs,dd 写了几 GB 数据下去,数字漂亮得吓人,但一卸载就什么都没了。测完也别急着走,生产环境记得删掉测试文件,把速率记录存档,下次再做同类测试时才有对比基础。这套流程跑熟了,五分钟给一台服务器做完磁盘健康初筛,并不是夸张的说法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询