作為一個分布式數據系統的開發者,對硬件需要有一些基本的常識。對這些東西的了解程度,決定了你能在多大程度上預測系統的整體性能,而這屬于一個架構師最核心的能力。今天我們來談一談硬盤,這個數據庫系統最底層的元件的一些基本概念,以及如何評估它的性能。
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
參考文獻
- https://wiki.mikejung.biz/Benchmarking#How_to_break_in_SSDs_before_benchmarking
- https://www.linux.com/learn/tutorials/442451-inspecting-disk-io-performance-with-fio
- https://www.binarylane.com.au/support/solutions/articles/1000055889-how-to-benchmark-disk-i-o
- https://linux.die.net/man/1/fio
- https://malcont.net/2017/07/apfs-and-hfsplus-benchmarks-on-2017-macbook-pro-with-macos-high-sierra/
- https://bartsjerps.wordpress.com/2011/03/04/io-bottleneck-linux/