測試前提
我們在進行測試時,都會分清楚:
- 測試對象:要區分硬盤、SSD、RAID、SAN、雲硬盤等,因為它們有不同的特點
- 測試指標:IOPS和MBPS(吞吐率),下面會具體闡述
- 測試工具:Linux下常用Fio、dd工具, Windows下常用IOMeter,
- 測試參數: IO大小,尋址空間,隊列深度,讀寫模式,隨機/順序模式
- 測試方法:也就是測試步驟。
測試是為了對比,所以需要定性和定量。在宣布自己的測試結果時,需要說明這次測試的工具、參數、方法,以便於比較。
存儲系統模型
為了更好的測試,我們需要先了解存儲系統,塊存儲系統本質是一個排隊模型,我們可以拿銀行作為比喻。還記得你去銀行辦事時的流程嗎?
- 去前台取單號
- 等待排在你之前的人辦完業務
- 輪到你去某個櫃台
- 櫃台職員幫你辦完手續1
- 櫃台職員幫你辦完手續2
- 櫃台職員幫你辦完手續3
- 辦完業務,從櫃台離開
如何評估銀行的效率呢:
- 服務時間 = 手續1 + 手續2 + 手續3
- 響應時間 = 服務時間 + 等待時間
- 性能 = 單位時間內處理業務數量
那銀行如何提高效率呢:
- 增加櫃台數
- 降低服務時間
因此,排隊系統或存儲系統的優化方法是
- 增加並行度
- 降低服務時間
硬盤測試
硬盤原理
我們應該如何測試SATA/SAS硬盤呢?首先需要了解磁盤的構造,並了解磁盤的工作方式:
每個硬盤都有一個磁頭(相當於銀行的櫃台),硬盤的工作方式是:
- 收到IO請求,得到地址和數據大小
- 移動磁頭(尋址)
- 找到相應的磁道(尋址)
- 讀取數據
- 傳輸數據
則磁盤的隨機IO服務時間:
服務時間 = 尋道時間 + 旋轉時間 + 傳輸時間
對於10000轉速的SATA硬盤來說,一般尋道時間是7 ms,旋轉時間是3 ms, 64KB的傳輸時間是 0.8 ms, 則SATA硬盤每秒可以進行隨機IO操作是 1000/(7 + 3 + 0.8) = 93,所以我們估算SATA硬盤64KB隨機寫的IOPS是93。一般的硬盤廠商都會標明順序讀寫的MBPS。
我們在列出IOPS時,需要說明IO大小,尋址空間,讀寫模式,順序/隨機,隊列深度。我們一般常用的IO大小是4KB,這是因為文件系統常用的塊大小是4KB。
使用dd測試硬盤
雖然硬盤的性能是可以估算出來的,但是怎么才能讓應用獲得這些性能呢?對於測試工具來說,就是如何得到IOPS和MBPS峰值。我們先用dd測試一下SATA硬盤的MBPS(吞吐量)。
#dd if=/dev/zero of=/dev/sdd bs=4k count=300000 oflag=direct
記錄了300000+0 的讀入 記錄了300000+0 的寫出 1228800000字節(1.2 GB)已復制,17.958 秒,68.4 MB/秒
#iostat -x sdd 5 10
...
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sdd 0.00 0.00 0.00 16794.80 0.00 134358.40 8.00 0.79 0.05 0.05 78.82
...
為什么這塊硬盤的MBPS只有68MB/s? 這是因為磁盤利用率是78%,沒有到達95%以上,還有部分時間是空閑的。當dd在前一個IO響應之后,在准備發起下一個IO時,SATA硬盤是空閑的。那么如何才能提高利用率,讓磁盤不空閑呢?只有一個辦法,那就是增加硬盤的隊列深度。相對於CPU來說,硬盤屬於慢速設備,所有操作系統會有給每個硬盤分配一個專門的隊列用於緩沖IO請求。
隊列深度
什么是磁盤的隊列深度?
在某個時刻,有N個inflight的IO請求,包括在隊列中的IO請求、磁盤正在處理的IO請求。N就是隊列深度。
加大硬盤隊列深度就是讓硬盤不斷工作,減少硬盤的空閑時間。
加大隊列深度 -> 提高利用率 -> 獲得IOPS和MBPS峰值 -> 注意響應時間在可接受的范圍內
增加隊列深度的辦法有很多
使用異步IO,同時發起多個IO請求,相當於隊列中有多個IO請求
多線程發起同步IO請求,相當於隊列中有多個IO請求
增大應用IO大小,到達底層之后,會變成多個IO請求,相當於隊列中有多個IO請求 隊列深度增加了。
隊列深度增加了,IO在隊列的等待時間也會增加,導致IO響應時間變大,這需要權衡。讓我們通過增加IO大小來增加dd的隊列深度,看有沒有效果:
dd if=/dev/zero of=/dev/sdd bs=2M count=1000 oflag=direct
記錄了1000+0 的讀入 記錄了1000+0 的寫出 2097152000字節(2.1 GB)已復制,10.6663 秒,197 MB/秒
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
sdd 0.00 0.00 0.00 380.60 0.00 389734.40 1024.00 2.39 6.28 2.56 97.42
可以看到2MB的IO到達底層之后,會變成多個512KB的IO,平均隊列長度為2.39,這個硬盤的利用率是97%,MBPS達到了197MB/s。(為什么會變成512KB的IO,你可以去使用Google去查一下內核參數 max_sectors_kb的意義和使用方法 )
也就是說增加隊列深度,是可以測試出硬盤的峰值的。
使用fio測試硬盤
現在,我們來測試下SATA硬盤的4KB隨機寫的IOPS。
$fio -ioengine=libaio -bs=4k -direct=1 -thread -rw=randwrite -size=1000G -filename=/dev/vdb
-name="EBS 4K randwrite test" -iodepth=64 -runtime=60
簡單介紹fio的參數
ioengine: 負載引擎,我們一般使用libaio,發起異步IO請求。
bs: IO大小
direct: 直寫,繞過操作系統Cache。因為我們測試的是硬盤,而不是操作系統的Cache,所以設置為1。
rw: 讀寫模式,有順序寫write、順序讀read、隨機寫randwrite、隨機讀randread等。
size: 尋址空間,IO會落在 [0, size)這個區間的硬盤空間上。這是一個可以影響IOPS的參數。一般設置為硬盤的大小。
filename: 測試對象
iodepth: 隊列深度,只有使用libaio時才有意義。這是一個可以影響IOPS的參數。
runtime: 測試時長
下面我們做兩次測試,分別 iodepth = 1和iodepth = 4的情況。下面是iodepth = 1的測試結果。
上圖中藍色方框里面的是測出的IOPS 230, 綠色方框里面是每個IO請求的平均響應時間,大約是4.3ms。黃色方框表示95%的IO請求的響應時間是小於等於 9.920 ms。橙色方框表示該硬盤的利用率已經達到了98.58%。
下面是 iodepth = 4 的測試:
我們發現這次測試的IOPS沒有提高,反而IO平均響應時間變大了,是17ms。
為什么這里提高隊列深度沒有作用呢,原因當隊列深度為1時,硬盤的利用率已經達到了98%,說明硬盤已經沒有多少空閑時間可以壓榨了。而且響應時間為 4ms。 對於SATA硬盤,當增加隊列深度時,並不會增加IOPS,只會增加響應時間。這是因為硬盤只有一個磁頭,並行度是1, 所以當IO請求隊列變長時,每個IO請求的等待時間都會變長,導致響應時間也變長。
常見測試方法
#順序讀
fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=read -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest
#順序寫
fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=write -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest
#隨機讀
fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=randread -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest
#隨機寫
fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest
#混合隨機讀寫
fio -filename=/dev/sda -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=100 -group_reporting -name=mytest -ioscheduler=noop