存储基础:存储层次结构、I/O路径与性能指标

核心原理:存储层次结构与I/O路径

存储系统并非单一组件,而是由多层硬件与软件协同构成的复杂体系。理解存储层次结构,是定位I/O瓶颈的第一步。从CPU寄存器到远端存储,每一层的访问延迟相差数个数量级,而带宽与容量则呈反向分布。下图展示了典型的存储层次结构与一次I/O请求的完整路径。

存储层次结构与I/O路径 CPU / 寄存器 延迟: ~1ns Cache (L1/L2/L3) 延迟: 1~30ns 内存 (DRAM) 延迟: ~100ns 本地存储 (SSD/HDD) 延迟: 0.1~10ms 网络存储 (SAN/NAS) 延迟: 1~20ms 应用程序 (MySQL / Nginx / Java) 文件系统 (ext4 / xfs / NTFS) 块设备层 (Device Mapper / RAID) SCSI / NVMe 驱动 磁盘 / SSD 控制器 常见瓶颈点 ① 锁竞争 / 上下文切换 ② 页缓存命中率低 ③ RAID重建 / 快照 ④ 队列深度不足 ⑤ 磁盘碎片 / 坏道 ⑥ 网络延迟 / 协议开销 ⑦ 控制器固件Bug → 逐层排查定位 延迟逐级增大 容量与持久性逐级增强

一次典型的读I/O请求路径为:应用程序 → 文件系统 → 页缓存 → 块设备层 → 设备驱动 → 磁盘控制器 → 物理介质。写路径略有不同,通常先写入页缓存(write-back),由内核的pdflush/flusher线程异步刷盘。理解这条路径,有助于在排查时判断瓶颈究竟发生在用户态、内核态还是硬件层。

性能指标:IOPS、吞吐量与延迟

衡量存储性能有三个核心指标,它们相互制约,必须结合业务场景综合评估:

指标 定义 典型单位 适用场景 常见误区
IOPS 每秒完成的I/O操作次数 次/秒 高并发小数据块随机读写(如OLTP数据库) 只看峰值IOPS,忽略延迟分布
吞吐量 每秒传输的数据量 MB/s 或 GB/s 大块顺序读写(如数据仓库、视频流) 用大块顺序吞吐衡量随机小I/O性能
延迟 单个I/O请求从发出到完成的时间 毫秒(ms) / 微秒(μs) 对响应时间敏感的业务(如交易系统) 只关注平均延迟,忽略P99/P99.9

三者之间存在内在联系:IOPS × 单次I/O大小 = 吞吐量。例如,若每次I/O为4KB,要达到1000 IOPS,则吞吐量约为4MB/s。但实际中,由于协议开销、队列竞争等因素,这个等式只是理想近似。

方法流程:性能基准测试与瓶颈定位

定位存储瓶颈,建议遵循以下流程:

  1. 明确业务特征:是读多写少还是写多读少?I/O块大小是4KB还是1MB?是随机访问还是顺序访问?
  2. 建立基线:使用 fio 等工具在空闲状态下测试存储的极限性能。
  3. 监控实时指标:使用 iostat、sar、pidstat 等工具采集IOPS、吞吐量、队列深度、等待时间。
  4. 分层排查:从应用层→文件系统→块层→驱动→硬件,逐层确认瓶颈。
  5. 验证假设:通过调整队列深度、更换调度算法、优化文件系统挂载参数等方式进行对照实验。

以下是一个典型的 fio 随机读测试命令,用于评估4KB随机读性能:

fio --name=randread --ioengine=libaio --direct=1 \
    --rw=randread --bs=4k --size=4G --numjobs=4 \
    --iodepth=32 --runtime=60 --time_based \
    --group_reporting

输出中重点关注 iops、clat (us) 的 p99 值以及 slat(设备服务时间)。若 slat 明显增大,说明设备本身处理能力接近上限;若 clat 增大但 slat 正常,则瓶颈可能在队列等待或上层调度。

案例/场景:数据库“突然变慢”的排查

某在线交易系统反馈数据库查询延迟从平均5ms飙升至200ms。通过 iostat -x 1 观察发现:

Device:  rrqm/s wrqm/s r/s   w/s   rkB/s   wkB/s  avgrq-sz avgqu-sz await r_await w_await svctm %util
sdb      0.00   0.00  0.00  1200  0.00    4800   4.00    45.0   37.5   0.00   37.5  0.83  100.0

关键信息解读:

  • w/s 为1200,wkB/s 为4800,即每次写约4KB,属于典型的小块随机写。
  • avgqu-sz 高达45,说明I/O请求在队列中大量堆积。
  • await 为37.5ms,而 svctm 仅0.83ms,表明设备本身处理很快,但请求排队时间过长。
  • %util 为100%,磁盘已饱和。

进一步排查发现,该磁盘为机械硬盘(HDD),而数据库的 redo log 和 binlog 都存放在同一块盘上。大量并发小写导致磁盘寻道成为瓶颈。解决方案:将 redo log 迁移至SSD,并使用 O_DIRECT 绕过文件系统缓存,减少双写开销。优化后,写入延迟降至2ms以内。

提示:当 %util 接近100%时,并不一定意味着磁盘“坏”了,更可能是I/O调度算法或队列深度配置不当。对于SSD,建议使用 noop 或 none 调度器;对于HDD,deadline 通常优于 cfq。

误区/复盘:常见认知陷阱

在存储性能分析中,以下误区经常导致误判:

  • 误区一:IOPS越高越好。实际上,高IOPS伴随高延迟同样不可接受。例如,某SSD标称100万IOPS,但P99延迟达到50ms,这对交易系统是灾难性的。
  • 误区二:忽略I/O大小的差异。用4KB随机读的IOPS去推算1MB顺序写的性能,毫无意义。不同I/O大小下,设备表现差异巨大。
  • 误区三:只看平均值。平均延迟5ms可能掩盖了P99为200ms的严重长尾问题。必须关注延迟分布,尤其是P99和P99.9。
  • 误区四:混淆缓存命中与真实I/O。如果数据大量命中页缓存,iostat显示的磁盘I/O会很低,但应用感知的延迟可能仍然很高——此时瓶颈可能在CPU或锁竞争,而非存储设备。
  • 误区五:忽视文件系统开销。ext4的日志模式(data=ordered vs data=writeback)对写性能影响可达30%以上。排查时需结合 mount 参数分析。
重要提醒:在压测或排查前,务必确认 direct=1 是否启用。若未启用,fio的结果可能只是页缓存的性能,而非真实磁盘性能。同理,生产环境中的“慢查询”有时并非存储问题,而是由于文件系统缓存未命中导致的真实磁盘访问。

复盘一次典型的误判案例:某团队发现数据库写延迟升高,立即更换了更贵的SSD,但问题依旧。后来通过 perf 分析发现,瓶颈在于文件系统日志提交的 fsync 频率过高,每次事务提交都触发一次全日志刷盘。最终通过调整 group commit 参数和 innodb_flush_log_at_trx_commit=2(允许每秒刷盘一次),将写吞吐提升了5倍,而硬件并未更换。这说明:存储性能优化应遵循“先软件后硬件”的原则,避免盲目升级设备。