图系统磁盘选型和性能测试

这里记录一下图系统的机器磁盘选型相关分析. 包括如何测试磁盘性能, 如何简单选择合适的磁盘搭配 (结论是建议1SSD[主] + 2HDD[副本])

0x00. 背景

HDD和SSD的原理对比:

结构图

HDD:
  1. HDD用N/S极代表0/1, 要读取数据,首先磁头需要摆动到相应的同心圆上,这又称为seek(寻道), 大概需要7-15ms , 而随机寻道, 就成了HDD最大的性能瓶颈之一. 它基于温彻斯特的热学架构.
  2. 磁盘寻道完成后,仍然还会转一段时间,这个过程可以称为潜伏(latency)
  3. 最后,当数据块滑过磁头的下方的时候,才能被取走,这个耗时大概在2-7ms,这个时间跟磁盘转速有关, 所以谈HDD的性能时, 转速这个指标是很重要的.
SSD:

大家都知道SSD比HDD要快得多, 但是快在哪呢?

SSD工作原理

  1. 固态不同之处是它采用的nand flash芯片, 没有机械架构
  2. 图中其中的浮置栅极相当一个小电池,有电荷的时候处于导电状态(表示数据0),无电荷时呈现截止转态(表示1).
  3. 通过控制栅极往其中充电,就可以把1转为0,这就是写入操作
  4. 要想从0转为1, 就好比乘船逆流而上要额外动力,需要在浮置栅极下面加一个20v偏置电压,把电荷拉出来,这就是特殊的擦除电路.

SSD磁盘的读写:

首先要明白, 到了闪存芯片时代, 严格来说, 已经没有传统的Sector(扇区)这种物理定义了, 实际的名称换成了Flash(page)

但是对操作系统而言, 它是不需要关心底层块设备的物理单位的, 所以它还是用统一的logic sector 来标识/兼容, 很多人常说的固态磁盘4K扇区对齐, 扇区是说的逻辑意义上的名称, 因为说4K页(page)对齐又容易与OS的页概念混淆, 所以就成了目前这种尴尬的局面…. (但是我们得要搞清楚来龙去脉)

所以切勿用传统文章中谈磁盘(HDD)的概念照套入SSD, 很多都是不严谨或错的, 也不要把SSD自身的page/block 概念和OS中的混淆, 但我们可以简单认为SSD中的page, 近似我们理解的磁盘最小操作单位–>扇区就好了.

然后就是弄清SSD内部的关联关系, 一个SSD盘有多块flash芯片, 每个flash芯片有多个block, 每个block又是由多个page组成的, 如图所示:

ssdFlash00

然后抽取某个block, 它是由多个(通常)是4K的page组成, 红色标识为损坏的page.

磁盘读写

  • 写是4k为单位, 一个个page写(n个4k), SSD有专门的磨损均衡控制器, 避免某个page被频繁访问. (OS里一般也是4K为一个page)
  • 擦除必须是以(block)为单位, 一般是1MB大小, 只能整体的进行擦除.(所以SSD必须记得4k对齐) –> 比如如果想只更新10字节的数据, 是不能直接做到的
  • SSD也有垃圾回收机制(GC), 在SSD空间较小的时候, 因为此时容易频繁触发GC (整理碎片), 写放大效应会非常严重, 所以好的SSD都会有预留空间

注意 : 不同厂商flash芯片不同, page/block容量不一定相同 (只能说SSD通常是4K)

我们常用SSD就是随机读, 但是数据有冷热, 如果我们反复读取一个page, 那么这个hot page 会很容易损坏, 所以在FS(文件系统) –> 磁盘物理快之间存在一个映射. 使得SSD的修改操作实际按下面的方式在进行: (蓝色块为已存在数据)

ssdFS00

第4个白色块为例, 假设它已经写入数据(变为蓝色 ), 然后之后每一次的修改. 并不会擦除旧数据, 而是先写到了下一个物理page, SSD控制器修改映射关系就可以了.

而在FS和SSD物理块之间的映射表, 就称为FTL(Flash Translation Layer), 它为了实现这种控制管理, 常有两个List管理:

  • Free List 管理空闲的page (白色方块)
  • Bad List 管理有损坏的page (黑色方块)

通过这种地址映射的方式, 每次读写都是通过逻辑地址算出物理地址, 而不用和HDD那样机械寻道+潜伏了… 节省了大量时间, 从而极大提高了随机读写的速度. 但是需要注意的是, 大量的更新操作, 对SSD的寿命是影响较大的.

写入放大效应

再回到上面那个问题, 如果想更新仅仅10字节数据, 之所以不能直接做到就是因为, 写入操作(1-->0)是容易的, 而更新相当于(0-->1), 这在SSD中须先大面积擦除(比如1MB的块空间), 显然代价太大, 那么实际要达到这个效果, 还是利用传统的 “读 -> 改 -> 写”的方式, 先从SSD中1个4K的page到内存修改, 再写入到下一个4k的page.

那么实际只更新了10字节数据, 却花了总8K的IO, 这种大量的IO浪费就是写入放大效应的体现了, 如下动图所示:

ssdWA00

除了8K的IO, 这次更新实际还导致1份数据存在了两个page中 (之前的page是旧数据), 我们又称它为“脏页(面)”, 而如果读到这个脏页的数据, 就称为“脏读” (也就是读到了过时的数据) ,那么如果我们反复改写这个10字节的数据, 就会出现许多的脏页, 自然我们就希望清理一下它们, 那么实际在SSD中是这样做的:

ssdGC00

  1. 比如修改第四个蓝块3次, 它就多了3个脏页. 导致原本的块空间利用率变低.
  2. 我们想把第一个块擦除, 但是块里还有其他4个page是有数据的, 那就必须先把这四个page的映射修改, 移动到空白的块的page里
  3. 最后再整体的把第一个块擦除掉. (整体类似JVM的”复制-删除”的GC模式)

这样的GC机制显然是可以很大提高SSD的磁盘利用率的, 但是同样它也有个隐患, 就是上面提到的—-当SSD空间很小的时候, 会出现极端的写入放大, 就算只写入几k的数据, 也会触发多次GC, 而每次GC都是1读1写1擦, 直到清理出充分的空间, 才会写入这几k的数据, 如下图是SSD剩余空间和写耗时的对比 :

ssdWA01

所以就算是设计结构和性能远胜于HDD的固态硬盘, 也需要理解它大体的运行机制和原理. 才能避免出现这种隐藏的性能问题.

性能指标:

  • IOPS (每秒磁盘读写次数—> I/O per second)
  • Latency(延迟): 简单可以理解为处理每个请求的时间.
  • Throughput(吞吐量) : 简单说可以理解为并发数, 某个服务1秒能接受的请求数.

传统HDD和SSD在这三个指标上的差别都非常大, 但是SSD成本比HDD高不少, 所以要根据具体场景合适的选择/搭配.

0x01. 基础信息

查看磁盘的原始信息有时候也是很不容易的事, 比如最简单的, 这盘是机械还是固态, 多少的转速, 什么接口, 接口的版本, 使用的协议… 都可能很大影响实际的体验, 有一些集成好的命令帮你查看:

1
2
3
4
5
6
7
8
9
10
# 准确信息还是要查官网 (通过型号)
smartctl --all /dev/sdx #这里sdx代表挂载的磁盘,比如下面是一块intel很早的SSD

# 下面几个是需要重点关注的信息
=== START OF INFORMATION SECTION ===
Model Family: Intel 730 and DC S35x0/3610/3700 Series SSDs # 通过此去google,可以得到详细数据
Device Model: INTEL SSDSC2BB300G4
User Capacity: 300,069,052,416 bytes [300 GB]
Sector Sizes: 512 bytes logical, 4096 bytes physica # 逻辑512B,物理4k
SATA Version is: SATA 2.6, 6.0 Gb/s (current: 3.0 Gb/s) # 支持/当前使用接口对吞吐影响很大,也要注意(一般至少是SATA3.1 6Gb/s)

不过需要注意的是, 较新的硬件(3D-Xpoint, 或者Flash卡类的). 通过smartctl 很可能是无法直接识别的, 需要另外用单独的驱动/命令来识别.

0x02. 性能测试

这里要注意的是, IOPS-wiki怎么去定义, 怎么测试差别是非常大的… Wiki写的比较全面, 并附有常见的IOPS指标(当然仅供参考) .

速率测试工具:

  • dd (自带工具, 适合简单测试)

  • hdparm ( 测试读和缓存速度, 单独安装, 更精准方便)

  • fio (全面测试磁盘性能, 时延/iops等, 推荐, 但需要注意每次读写的大小设置很可能不是4K)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    # cent上安装,fio的详细参数很多,需要查文档
    sudo yum install -y epel-release #安装EPEL库
    sudo yum install -y fio

    # 1.测试随机读,不过数值比实际场景看似乎要高不少,可能需要进一步细化参数..
    # 在当前磁盘测试目录下4k随机读50G文件测试 (超时时间120s)
    fio -bs=4k -ioengine=libaio -iodepth=8 -direct=1 -rw=randread -runtime=120 -numjobs=1 -norandommap -randrepeat=0 -group_reporting -name=randread -size=50G -lockmem=1G -filename=./testRead1

    # 2.顺序读
    fio -filename=./testRead1 -direct=1 -iodepth 1 -thread -rw=read -ioengine=psync -bs=512k -size=100G -numjobs=30 -runtime=1000 -group_reporting -name=mytest

    # 3.随机写
    fio -filename=./testWrite1 -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=4k -size=10G -numjobs=30 -runtime=1000 -group_reporting -name=mytest

    # 4.顺序写
    fio -filename=./testWrite2 -direct=1 -iodepth 1 -thread -rw=write -ioengine=psync -bs=512k -size=100G -numjobs=30 -runtime=1000 -group_reporting -name=mytest

    # 5. 混合随机读写
    fio -filename=./testReadWrite -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=4k -size=10G -numjobs=30 -runtime=100 -group_reporting -name=mytest -ioscheduler=noop

    # 备注: 常用参数说明
    iodepth=8 # 默认1,读写文件的深度,暂时不确定
    direct=1 # 测试过程绕过机器自带的buffer (不走内存)
    rw=randwrite # 测试随机写的I/O
    rw=randrw # 测试随机写和读的I/O
    bs=16k # 单次io的块文件大小为16k, 随机读一般设为4k
    bsrange=512-2048 # 同上,提定数据块的大小范围
    size=5g # 本次的测试文件大小为5g
    numjobs=30 # 本次的测试线程为30
    runtime=1000 # 测试超时时间为1000秒,如果不指定则一直将5g文件写完为止
    rwmixwrite=30 # 混合读写测试模式, 写占30%
    lockmem=1g # 只使用1g内存进行测试
    nrfiles=8 # 每个进程生成文件的数量为8
    zero_buffers #用0初始化系统buffer
    group_reporting # 汇总结果,显示每个进程
  • iops (python写的快速测试磁盘IOPS性能的脚本. 推荐) 使用最简单, ./iops /dev/xx 就能测这块磁盘(建议加-t 10参数)

效果如下图: (4T-HDD)
iopsHDD00\

对比容量300G的12年intel SATA3-SSD, IOPS都是接近百倍的4k差别. (可以看到SSD原生4K单位物理扇区, 而HDD通常是512B)
iopsSSD00

对比1T容量的17年Intel SATA3.1-6Gb/s 的SSD :

ssd1T00

最后, 对比2T的PCIe接口的flash卡, 时延这里没有体现, 但是吞吐差别接近10倍, 而且中等大小文件的IOPS也比普通SSD高的多.

iopsFlash00

这里有个比较关键的地方, 就是我们实际读数据的时候, 是不是都按4K来计算? 大家都知道OS设计中一般是以4K为page的单位, SSD也是以4K为最小的page单位去读的, 但是这是不是意味着我们要随机读一段40K的数据, 就一定需要分开读10次page呢? 显然不是..

所以实际测试里, 我们需要特别关注程序本身, 到底是一次读多大的数据? 很可能并不是4K, 而是24K, 64K, 甚至128K… 这样做IOPS测试的时候, 就一定需要全面的考察一下, 而不是直接用fio测4k的读写, 得出的结论可能是有巨大偏差的 (普通SSD在4K和64K随机读相差了10倍的IOPS..)

更新: Hbase这里测试, 应该至少以64K为每次读的block单位对比, 而不是4K为大小.

补充 : fio也有一个可视化版本, 但是我觉得有点麻烦而且没啥必要.. 其他可视化的磁盘性能输出可以参考磁盘性能分析

0x03. 监控与分析

1. 磁盘监控三板斧:

  1. iostat : 一般我常用 -cdmx 5 的组合参数, 5s刷新一次. 观察整体磁盘使用情况.
1
2
3
4
5
6
7
8
iostat -cdmx 5
# 显示如下
avg-cpu: %user %nice %system %iowait %steal %idle
0.02 0.00 0.01 0.00 0.00 99.98

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
nvme0n1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
  1. iotop : 定位哪些进程在使用磁盘 (本质是python从/proc中获取进程IO信息.)
  2. ioprofile : 又称pt-ioprofile , 本质是strace进程然后进行IO分析, 用于更进一步定位使用磁盘的线程在读写什么文件.

(可选)dstat : 查看网卡/磁盘/CPU汇总信息 (彩色)

2. 磁盘信息查看

因为看FS的时候发现磁盘和文件系统. 以及Linux/内核源码自身都有一些混用的概念, 所以需要区分一下..

  • 硬盘: 最小存储单位是”扇区“ , HDD之前每个扇区默认是512字节, SSD常见4K默认, 这是物理定义存在的 (在linux又称physics sector , 物理扇区)
  • OS: 这里有几个关键点, 有问题请及时通知修正~
    1. 首先, 操作系统不一定有磁盘, 但它仍有一个”逻辑扇区“ (Logic sector)的概念, 或者可能称为Logic Block , 这里就混用了”block“ 和 “sector“ 的概念, 大家要注意甄别.. 它默认值一般是512字节的倍数, 但是为了兼容旧磁盘, 一般取的512字节, 所以你会发现SSD盘物理扇区4K, 逻辑扇区仍然是512字节, 才有大家常说的给SSD进行4K对齐的说法.
    2. 其次, 操作系统也不一定有FS, 但是它也有一个自身的block 定义, 比如在Page --> Buffer --> Block 这个映射关系里, 最后block对应的是Linux自身的, 并非FS定义的, 只不过在有FS的时候, 它一般就取的FS的block size (存疑?)
    3. 要求sector size ≤ block size ≤ page size (一般4K)
  • FS: 文件系统有很清晰的block 定义, 并且这一定是逻辑上的抽象, 它是一个映射和软件层管理的概念, 默认值一般是4K

注意: 新的HDD磁盘可能也是4K为物理扇区单位的(硬盘协会更新了标准, 但是不一定都采用了), SSD可能已经到了16K为page单位(此时就应该是16K对齐), 所以文章中说的/画的图以后也很可能会过时, 但是只要搞清楚了本质和来龙去脉, 再自己查一下确定就没啥问题, 其次SSD的页对齐的意义远比HDD大得多(因为闪存特性所致)

然后Linux上有个非常不错的命令blockdev来查看详细的这种磁盘相关信息, 还能查预读大小, 都是很关键的IO细节参数.

1
2
3
4
5
6
7
8
9
# 查看/dev/sda设备的逻辑扇区大小
sudo blockdev -getss /dev/sda
512 # 单位字节

sudo blockdev -getbsz /dev/sda
4096 # 4k

sudo blockdev -getra /dev/sda
256 # 这里单位是个,代表预读256个扇区也就是128K

iopsFlash00

然后还有个常见的查看所有盘以及汇总信息的fdisk 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看所有磁盘汇总信息
sudo fdisk -lu
# 显示信息常见, 用start % (physical/unit) == 0? 看是否能整除判断是否physical对齐
# 比如这里就是 2048 % (4096/512) = 0
Units = 1 * 512 bytes
Sector size (logical/physical) : 512B/4096B
...
# 以下信息可能SSD不一定显示,看fdisk版本和实际情况.
Device Boot Start End
/dev/sda1 2048 xxxx
/dev/sda2 104859648 xxxx

# fdisk还能给磁盘做重新分区..以进行校正

参考资料:

  1. SSD基本原理
  2. Linux基本性能监控
  3. Linux进阶之性能调优
  4. Linux下IO监控分析
  5. Linux性能汇总图2017
  6. 冷热分离原则
  7. Coolshell性能分析
  8. TiKV性能分析
  9. 磁盘分区4K对齐相关-DG