Kafka的高吞吐率 —— 为什么顺序 I/O 这么快?
实测:顺序的磁盘 I/O 到底有多快?
下面先用 redis 实测一下,看看纯内存读写 100w 条数据耗时几何:
可以看到:
| 操作 | 耗时 |
|---|---|
100w 数据:内存写 | 960 ms |
100w 数据:内存读 | 560 ms |
100w 数据:内存读 + 内存写 | 1520 ms |
下面用 sed 实测一下,看看基于磁盘 IO 的顺序读写操作速度如何:
实测结果:
| 操作 | 耗时 |
|---|---|
100w 数据:磁盘读 + 磁盘写 | 55ms |
100w 数据:磁盘读 | 42ms |
1000w 数据:磁盘读 + 磁盘写 | 514ms |
1000w 数据:磁盘读 | 359ms |
对比结果:
| 100w 数据读写操作 | 耗时对比 |
|---|---|
| 读 | 内存/磁盘 = 560/42 ≈ 13 |
| 读 + 写 | 内存/磁盘 = 1520/55 ≈ 28 |
| 写 | 内存/磁盘 = 960/13 ≈ 74 |
出乎意料地,磁盘读写速度比内存还要快???
介于我用的是固态硬盘,于是我对比了固态硬盘和机械硬盘的读写速率,如下:
最终,对比结果如下:
| 读写操作 | 读写速率比 |
|---|---|
| 顺序读 | 固态:机械 ≈ 18 |
| 顺序写 | 固态:机械 ≈ 25 |
| 4K 随机读 | 固态:机械 ≈ 175 |
| 4K 随机写 | 固态:机械 ≈ 633 |
通过换算,推断出使用机械硬盘时,磁盘读写速度与内存读写速度的差距如下:
| 100w 数据读写操作 | 耗时对比 |
|---|---|
| 读 | 内存/磁盘 = 560/42*18 ≈ 0.74 |
| 读 + 写 | 内存/磁盘 = 1520/(42*18+13*25) ≈ 1.4 |
| 写 | 内存/磁盘 = 960/13*25 ≈ 2.95 |
通过各项数据对比,总结如下:
- 磁盘顺序读比内存读慢 35% 左右
- 磁盘顺序写比内存写更快,大约是内存写的 3 倍
- 最终,磁盘顺序读写的速度比内存读写的速度还要快 40% 左右
这还仅仅是机械硬盘,如果是固态硬盘,速度更快,以我自己的电脑为例 —— 顺序读写速度是内存读写速度的二十几倍。
通过上面数据对比,
发现固态硬盘的顺序读写和随机读写相差几十倍;
机械硬盘的顺序读和随机读相差一百多倍,而顺序写和随机写相差了几百倍。
【思考】
不管是机械硬盘还是固态硬盘,顺序写总是比顺序读慢。
但是为什么固态硬盘随机写比随机读快,机械硬盘随机写比随机读慢?
【解答】
固态硬盘分带缓存和不带缓存的,对于带有动态随机存取存储器(Dynamic Random Access Memory,DRAM, 一种半导体存储器)的固态硬盘:
随机读操作是真的要去操作磁盘,但随机写却不一定立刻落盘,实际上直接操作的对象是 DRAM。所以固态硬盘的随机写看上去比随机读还要快。
【为什么读就理所当然要比写快呢?】
抛开缓存不谈,磁盘读操作利用索引可以让读取速度非常快;而写操作需要根据寻道策略去寻找可用扇区,还要确认没有数据后才能写入,还要维护标志位,所以耗时比读操作更长。
而且写操作需要给电子提供更大的能量,功耗更高。
顺序的磁盘 I/O 为什么快?
Kafka 能够以 O(1) 的时间复杂度(不使用随机读写,使用顺序读写)提供消息持久化能力,即使对 TB 级以上的数据也能保持常数时间的访问性能。
顺序读写为何能那么快呢?
下面是磁盘的内部结构图
磁盘的物理地址由【柱面号 + 盘面号 + 扇区号】来定义,磁盘核心部位详解:
根据地址读写一个“磁盘块”的流程如下:
- 根据
柱面号移动磁臂,让磁头指向指定柱面; - 激活
盘面号对应的磁头; - 磁盘旋转,
扇区号所代表的扇区从磁头划过就完成了对指定扇区的读/写。
下面我们以 7200rpm 的硬盘为例,先来计算1 次随机读写耗时:
-
首先是磁臂根据柱面号移动到指定柱面的时间,称为寻道时间,一般为
7.5 ~ 14 ms。 -
然后是磁盘旋转,找到指定扇区:
- 每分钟 7200 转,则每秒转数:
7200 / 60 = 120; - 转 1 圈花费时间:
120 / 1000ms = 8.33 ms; - 平均只需转半圈即可找到指定扇区,所以旋转延迟时间取
4.165 ms。
- 每分钟 7200 转,则每秒转数:
-
最后是磁头进行读写操作的传输时间,这取决于要读写的字节数。
那么,N 次随机读写的耗时就是:
N 次寻道时间 + N 次旋转延迟时间 + N 次传输时间
而N 次顺序读写的耗时为:
1 次寻道时间 + 1 次旋转延迟时间 + N 次传输时间
也就是说,寻道时间和旋转延迟时间被均摊到 N 次顺序 IO 中,在大量的 IO 次数面前,几乎可以忽略不记。
这就是为什么顺序磁盘 I/O 比随机磁盘 I/O 要快得多。
假设:
- 每次读取 1 字节的数据
- 扇区大小 512 字节
- 扇区数 63
- 转一圈耗时 8.33 ms
可以算出读取 1 字节的数据 100w 次,需要时间:
# 1 次寻道时间 + 1 次旋转延迟时间 + 1字节数据传输时间 * 1000000
11 + 4.165 + 8.33 * 1 / (512*63) * 1000000 = 15.165 + 258.2465 ≈ 273.4 ms