用内存当缓存,存储空间是不够用的,大部分时间,请求还是要打到硬盘上,所以来看看IO性能的事情。
IO性能、顺序访问和随机访问
硬盘的两个重要指标:响应时间和数据传输率。
数据传输率
硬盘分类
- HDD硬盘,即机械硬盘,用的是SATA 3.0的接口。
- SSD硬盘,即固态硬盘,有SATA3.0、PCI Express两种接口
SATA 3.0 的接口,带宽是6Gb/s,b是比特,相当于768MB/s。
- 日常使用的HDD硬盘的传输率,差不多在200MB/s。
- SSD( Crucial MX500型号)硬盘的数据传输速率能到差不多 500MB/s,比 HDD 的硬盘快了一倍不止。
不过 SATA 接口的硬盘,差不多到这个速度,性能也就到顶了。因为 SATA 接口的速度也就这么快。
运行 AS SSD 测算 SATA 接口 SSD 硬盘性能的结果,第一行的 Seq 就是顺序读写硬盘得到的数据传输率的实际结果如下
SSD 硬盘如果是PCI Express 的接口能够更快,使用了 PCI Express 的三星 SSD 硬盘的数据传输率在读取的时候就能做到 2GB/s 左右,差不多是 HDD 硬盘的 10 倍,而在写入的时候也能有 1.2GB/s。
响应时间
响应时间,测试结果中的 Acc.Time 指标,是程序发起一个硬盘的写入请求,直到这个请求返回的时间。
- 两种接口的SSD硬盘,大概都是几十微妙的级别
- HDD磁盘一般都是几毫秒到十几毫秒
这个性能的差异,在几十倍至几百倍
随机读写
只看响应时间和吞吐率这两个指标,似乎我们的硬盘性能很不错。
即使是廉价的 HDD 硬盘,接收一个来自 CPU 的请求,也能够在几毫秒时间返回。
一秒钟能够传输的数据,也有 200MB 左右。你想一想,我们平时往数据库里写入一条记录,也就是 1KB 左右的大小。
我们拿 200MB 去除以 1KB,那差不多每秒钟可以插入 20 万条数据呢。
但是这个计算出来的数字,似乎和我们日常的经验不符合啊?
原因在于顺序读写和随机读写下,硬盘的性能是完全不同的。
上面的 AS SSD 的性能指标,“4K“的指标,代表是我们程序去随机读取磁盘上某一个4KB大小的数据,1秒之内可以读到多少数据。
在这个指标上,我们使用 SATA 3.0 接口的硬盘和 PCI Express 接口的硬盘,性能差异变得很小,接口本身的速度已经不是我们硬盘访问速度的瓶颈了。
使用PCI Express的接口,随机读写的时候,数据的传输只能到40MB/s左右,是顺序读写情况下的几十分之一。
我们拿这个 40MB/s 和一次读取 4KB 的数据算一下,40MB / 4KB = 10,000,一秒之内,这块 SSD 硬盘可以随机读取 1 万次的 4KB 的数据。如果是写入的话呢,会更多一些,90MB /4KB 差不多是 2 万多次。
每秒输入和输出操作的的次数,称之为IOPS,这个指标比响应时间更重要。
IOPS和DTR(Data Transfer Rate,数据传输率)是输入和输出性能的核心指标。
在我们的实际开发中,对于数据的访问,更多的是随机读写,而不是顺序读写。平时所说的服务器承受的“并发”,其实是在说,会有很多个不同的进程和请求来访问服务器。自然,它们在硬盘上访问的数据,是很难顺序放在一起的,所以随机读写的IOPS才是服务器性能的核心指标。
HDD硬盘的IOPS通常也就在100左右,而不是在20万次。
如何定位IO_WAIT
IO瓶颈与指标
用上PCI Express 接口的 SSD 硬盘,IOPS 也就是在 2 万左右,CPU 的主频通常在 2GHz 以上,也就是每秒可以做 20 亿次操作。
即使 CPU 向硬盘发起一条读写指令,需要很多个时钟周期,一秒钟 CPU 能够执行的指令数,和我们硬盘能够进行的操作数,也有好几个数量级的差异。
这也是为什么,我们在应用开发的时候往往会说“性能瓶颈在 I/O 上”。因为很多时候,CPU 指令发出去之后,不得不去“等”我们的 I/O 操作完成,才能进行下一步的操作。
实际遇到服务端程序的性能问题的时候,我们怎么知道这个问题是不是来自于 CPU 等 I/O 来完成操作呢?
在 top 命令的输出结果里面,有一行是以 %CPU 开头的。这一行里,有一个叫作 wa 的指标,这个指标就代表着 iowait,也就是 CPU 等待 IO 完成操作花费的时间占 CPU 的百分比。
输入“iostat”,就能够看到实际的硬盘读写情况。
这个命令里,不仅有 iowait 这个 CPU 等待时间的百分比,还有一些更加具体的指标了,并且它还是按照你机器上安装的多块不同的硬盘划分的
tps 指标,其实就对应着我们上面所说的硬盘的 IOPS 性能。而 kB_read/s 和 kB_wrtn/s 指标,就对应着我们的数据传输率的指标。
要找出到底是哪一个进程是这些 I/O 读写的来源了。这个时候,你需要“iotop”这个命令。
通过 iotop 这个命令,你可以看到具体是哪一个进程实际占用了大量 I/O,那么你就可以有的放矢,去优化对应的程序了。
模拟IO操作
单个 CPU 核心的机器上输入“stress -i 2”,让 stress 这个程序模拟两个进程不停地从内存里往硬盘上写数据。
stress -i 2
top 的输出里面,CPU 就有大量的 sy 和 wa,也就是系统调用和 iowait。
top - 06:56:02 up 3 days, 19:34, 2 users, load average: 5.99, 1.82, 0.63
Tasks: 88 total, 3 running, 85 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3.0 us, 29.9 sy, 0.0 ni, 0.0 id, 67.2 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1741304 total, 1004404 free, 307152 used, 429748 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 1245700 avail Mem
iostat 2 5
avg-cpu: %user %nice %system %iowait %steal %idle
5.03 0.00 67.92 27.04 0.00 0.00
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
sda 39762.26 0.00 0.00 0 0
通过 iostat,查看硬盘的 I/O,你会看到,里面的 tps 很快就到了 4 万左右,占满了对应硬盘的 IOPS。
如果这个时候我们去看一看 iotop,你就会发现,我们的 I/O 占用,都来自于 stress 产生的两个进程了。
iotop
Total DISK READ : 0.00 B/s | Total DISK WRITE : 0.00 B/s
Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
29161 be/4 xuwenhao 0.00 B/s 0.00 B/s 0.00 % 56.71 % stress -i 2
29162 be/4 xuwenhao 0.00 B/s 0.00 B/s 0.00 % 46.89 % stress -i 2
1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init
总结
硬盘的两个核心指标:响应时间和数据传输率,通过这两个指标可以理解IO性能问题。
- 顺序读写的情况下,无论是HDD硬盘还是SSD硬盘,性能都是不错的。
- 随机读写的情况下,性能都不太高,使用PCI Express 接口的 SSD 硬盘,IOPS(每秒钟能够输入和输出的操作次数)也就只是到了2万左右。这个操作和CPU每秒20亿次的操作比起来,差的远了,所以很多时候,我们的程序对外响应慢,都是CPU在等待IO操作完成。
在Linux下,可以通过这样的方式定位IO问题
- 使用top命令,查看整个服务器的负载,应用响应慢的时候,通过这个命令来看CPU是否在等待IO完成自己的操作。
- 可以通过iostat这个命令,来看各个硬盘这个时候的读写情况。
- iotop这个命令,能够帮我们定位到底哪一个进程在进行大量的IO操作