硬盘基础知识:原理,指标及测试

作为一个分布式数据系统的开发者,对硬件需要有一些基本的常识。对这些东西的了解程度,决定了你能在多大程度上预测系统的整体性能,而这属于一个架构师最核心的能力。今天我们来谈一谈硬盘,这个数据库系统最底层的元件的一些基本概念,以及如何评估它的性能。

1. 硬盘类型

硬盘的类型主要分为两类,SSD(固态硬盘)和 HDD(机械硬盘)。很多现代数据库对 SSD 都有很好的优化,在绝大多数情况下,一个需要考虑随机访问的系统,使用 SSD 是最好的选择。只有在使用类似 Kafka 这样的,纯顺序读写的系统时,我们才优先选择单价更低的 HDD。

1.1 机械硬盘

传统磁盘本质上一种机械装置,如FC, SAS, SATA磁盘,转速通常为5400/7200/10K/15K rpm不等。影响磁盘的关键因素是磁盘服务时间,即磁盘完成一个I/O请求所花费的时间,它由寻道时间、旋转延迟和数据传输时间三部分构成。

寻道时间

寻道时间 (Tseek) 是指将读写磁头移动至正确的磁道上所需要的时间。寻道时间越短,I/O操作越快,目前磁盘的平均寻道时间一般在3-15ms。

旋转延迟

旋转延迟 (Trotation) 是指盘片旋转将请求数据所在扇区移至读写磁头下方所需要的时间。显然,这是一个只有机械硬盘才有的参数。旋转延迟取决于磁盘转速,通常使用磁盘旋转一周所需时间的1/2表示。比如,7200 rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms,而转速为15000 rpm的磁盘其平均旋转延迟为2ms。

数据传输时间

数据传输时间(Ttransfer)是指完成传输所请求的数据所需要的时间,它取决于数据传输率,其值等于数据大小除以数据传输率。 IDE/ATA 理论上能达到133MB/s,SATA II 可达到300MB/s的接口数据传输率。在实际的数据库 transaction 中,数据传输时间往往远小于前两部分消耗时间。

一些简单计算

上面的几个数字,并不是互相独立的。首先,寻道时间和转速的关系非常密切。磁盘是旋转的,读写头是固定的。当我们需要读写磁盘某个扇区某个磁道的数据,我们需要把对应的扇区转到读写头处。平均来说,这个过程就是磁盘转半圈所需的时间,这个时间就是旋转延迟时间。比如,7200 rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms。

常见硬盘的旋转延迟时间为:

  • 7200 rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms
  • 10000 rpm的磁盘平均旋转延迟大约为60*1000/10000/2 = 3ms,
  • 15000 rpm的磁盘其平均旋转延迟约为60*1000/15000/2 = 2ms。

而寻找数据的过程,除了磁盘的转动,磁头也需要在轴上进行平移,这两个时间综合起来,就是寻道时间。这个时间和磁头移动速度,磁碟的存储密度都有关系。我们可以对比上文的旋转延迟和下面的常见磁盘平均物理寻道时间,从而得到一个感性的认识:

  • 7200转/分的STAT硬盘平均物理寻道时间是9ms
  • 10000转/分的STAT硬盘平均物理寻道时间是6ms
  • 15000转/分的SAS硬盘平均物理寻道时间是4ms

通过上面的数据,我们还可以计算理论的 IOPS 数据(IO per second,每秒访问次数): IOPS = 1000 ms/ (寻道时间 + 旋转延迟),可以忽略数据传输时间。

  • 7200 rpm的磁盘IOPS = 1000 / (9 + 4.17) = 76 IOPS
  • 10000 rpm的磁盘IOPS = 1000 / (6+ 3) = 111 IOPS
  • 15000 rpm的磁盘IOPS = 1000 / (4 + 2) = 166 IOPS

这个指标,反映了磁盘处理随机读写请求的能力,对于大多数数据库系统来说,这是一个至关重要的参数。

1.2 固态硬盘

SSD(solid state drive)本质上和 U盘比较类似,它没有活动的机械部件,靠的是一块块的闪存颗粒来存储数据。由于这些闪存彼此之间并无干扰,因此可以以类似 RAID0 的方式来提高访问速度。它的读写寻址不涉及磁头移动和磁盘转动,因此可以提供很高的 IOPS。

下面是根据我的经验,一些常见的 SSD 系统的性能指标:

  • 读 IOPS: 大概在 2,3w到10w 不等。
  • 写 IOPS: 从几千到几万不等,一般要低于读的 IOPS。
  • 吞吐量:我用过的比较老的机器,通常读吞吐量在 200-400多 MB/s,写吞吐量在100-200 MB/s 之间。比较新的系统上,读写吞吐量能达到 1000/900 MB/s
  • IO latency:各大云服务商提供的 SSD 云盘,大概是 300 μs 左右。我自己使用过的公司机房服务器,有些比较老的会低一点,总体大概在1.6ms 到300μs 间浮动。较新的 SSD,比如 MacBook Pro 上面,这个指标能到 20μs 左右。

需要注意的是,很多云服务商,如 Amazon,AliYun,它们提供的 SSD 的 IOPS 是和容量挂钩的。这个也很好理解,因为大容量的 SSD 背后是更多的 NAND Flash,相当于更多的磁盘组成了 Raid0,速度自然更快。

2 测试工具和方法

硬盘的测试工具有很多,我们一般推荐用的是两个工具:ioping 和 fio。也有不少其他的工具可以选用,如 dd,hdparm 等,但这些工具或多或少都存在着问题。

以 dd 为例,它只能测单线程,顺序写的性能,而且只能测少量数据,这就使得它的结果会被缓存影响。hdparm 这个老古董只能用在 ATA 硬盘上,现在基本上也被淘汰了。因此我们主要介绍 ioping 和 fio。

2.1 fio

fio 是个比较现代化的测试工具,可以利用多线程/多进程来进行并发 IO 测试,支持混合读写,支持自定义载荷的大小。

在 Ubuntu 下面,可以用 apt-get 来安装这个工具:

sudo apt-get install libaio1
sudo apt-get install libaio-dev

fio 的命令行用法如下:

$ fio -ioengine=libaio -bs=4k -direct=1 -thread -rw=randwrite -size=4G -filename=/dev/vdb -name="EBS 4K randwrite test" -iodepth=64 -runtime=60

$fio -ioengine=libaio -bs=4k -direct=1 -thread -rw=randread -size=4G -filename=/ssd/test -name="EBS 4K randwrite test" -iodepth=64 -runtime=60

$fio -ioengine=libaio -bs=4k -direct=1 -thread -rw=randrw –rwmixread=75-size=4G -filename=/ssd/test -name="EBS 4K randwrite test" -iodepth=64 -runtime=60

上面的三条命令,是使用4k 的块大小,4g 的文件大小,多线程模式,io 队列深度64,分别测试随机读,随机写,以及读写混合(读占75%,写占25%)的性能。

下面是 fio 的主要参数:

  • ioengine: IO引擎,我们一般使用libaio,发起异步IO请求,MacOS 下用 posixaio。
  • bs: IO大小
  • direct: 直写,绕过操作系统Cache。因为我们测试的是硬盘,而不是操作系统的Cache,所以设置为1。
  • rw: 读写模式,有顺序写write、顺序读read、随机写randwrite、随机读randread等。
  • size: 表示测试数据的大小
  • filename: 测试对象
  • iodepth: 队列深度,只有使用libaio时才有意义。这是一个可以影响IOPS的参数。在测试时,队列深度为1是主要指标,大多数时候都参考1就可以。实际运行时队列深度也一般不会超过4。
  • runtime: 测试时长
  • randrepeat:以同一个 rand seed 来产生随机数,使多次运行的结果更加一致
  • rwmixread:如果用读写混合模式测试的话,该参数指定混合中读的百分比,一般设为75

fio 是主流磁盘测试工具中最简明好用的一个,强烈推荐。

2.2 ioping

ioping 这个工具主要用来测试 io 的 latency。使用方法如下:

# 测延迟
ioping -c 100 /dev/sdc1

# 测寻道速度,这个命令能够更好的反映硬件性能,不受 ioengine 的影响
ioping -R /dev/sdc1

# 测顺序读的速度,这个能够反映吞吐量大小
ioping -RL /dev/rdisk1

ioping 主要参数如下:

  • -A use asynchronous I/O
  • -C use cached I/O (no cache flush/drop)
  • -D use direct I/O (O_DIRECT)
  • -W 测写性能
  • -G 测读写 ping pong
  • -Y 使用 O_SYNC 模式
  • -y 使用 O_DSYNC 模式

这些参数在某些时候会较大的影响测试结果。比如我们公司的一台机器上,打开 direct IO 会导致顺序读的速度从 350mb/s -> 450 mb/s。 但是 ioping 最主要的用途,还是测试 io 延迟。

2.3 dstat

传统上在磁盘使用过程中,我们可以用 iostat 来监控磁盘使用情况。但 iostat 很多人不明白怎么用,常见错误用法,是直接跑: iostat 10,这个命令返回的结果第一条,是从开机以来的总体统计结果,如果你在运行一些 heavy task,这个结果会让你非常惊讶。你可以用 -y 参数来忽略这条输出,但更好的办法,则是换用 dstat 这样的工具,如图。

命令行:

# 指定监控 sdb 和 sdc 两个块设备
dstat -D sdb,sdc
Screenshot 2018-08-04 15.28.58.png

参考文献

  1. https://wiki.mikejung.biz/Benchmarking#How_to_break_in_SSDs_before_benchmarking
  2. https://www.linux.com/learn/tutorials/442451-inspecting-disk-io-performance-with-fio
  3. https://www.binarylane.com.au/support/solutions/articles/1000055889-how-to-benchmark-disk-i-o
  4. https://linux.die.net/man/1/fio
  5. https://malcont.net/2017/07/apfs-and-hfsplus-benchmarks-on-2017-macbook-pro-with-macos-high-sierra/
  6. https://bartsjerps.wordpress.com/2011/03/04/io-bottleneck-linux/

推荐阅读更多精彩内容