NVMe SSD以其卓越的性能而聞名,因此性能驗證是測試流程中至關重要的一環。這不僅僅是為了確認SSD是否達到其標稱的IOPS和吞吐量,更是為了評估其在各種真實工作負載下的實際表現和穩定性。FIO(Flexible I/O Tester)是業界公認的、功能強大的I/O測試工具,廣泛應用於儲存設備的性能基準測試。
3.1 測試工具:FIO (Flexible I/O Tester) 的深入使用
FIO是一個開源的、高度可配置的I/O基準測試工具,支持多種I/O引擎、I/O模式和數據模式。它能夠模擬各種複雜的I/O工作負載,並提供詳細的性能統計數據。
3.1.1 FIO的安裝
在大多數Linux發行版中,FIO可以通過包管理器安裝:sudo apt update
sudo apt install fio # Debian/Ubuntu
sudo yum install fio # CentOS/RHEL
3.1.2 FIO常用參數配置
FIO的強大之處在於其豐富的參數配置。以下是一些常用且關鍵的參數:
- --name=<job_name> :定義測試任務的名稱。
- --ioengine=<engine> :指定I/O引擎。對於NVMe SSD,常用的是 libaio (Linux Asynchronous I/O)或 io_uring (Linux Kernel 5.1+)。 libaio 是傳統的異步I/O, io_uring 是更現代、性能更好的異步I/O接口。
- --direct=<bool> :設置為 1 表示使用直接I/O,繞過操作系統的頁面緩存。這對於測試SSD的原始性能至關重要,避免了緩存對結果的干擾。
- --rw=<pattern> :指定讀寫模式。常用模式包括:
- randread :隨機讀取
- randwrite :隨機寫入
- read :循序讀取
- write :循序寫入
- randrw :隨機混合讀寫
- rw :循序混合讀寫
- --bs=<block_size> :指定I/O塊大小(Block Size),例如 4k 、 64k 、 1m 。不同的塊大小會影響IOPS和吞吐量。
- --numjobs=<num> :指定並行運行的I/O任務數量。每個job會創建一個獨立的I/O線程或進程。
- --iodepth=<depth> :指定每個I/O任務的I/O隊列深度(Queue Depth)。隊列深度越大,並行I/O請求越多,通常能更好地發揮SSD的性能。
- --size=<total_size> :指定每個I/O任務要讀寫的總數據量。例如 10g 。
- --filename=<path> :指定測試文件或設備路徑。對於NVMe SSD,通常直接指定設備節點,如 /dev/nvme0n1 。
- --runtime=<seconds> :指定測試運行時間,例如 300 秒。
- --time_based :使測試基於時間運行,而不是基於 --size 完成。
- --group_reporting :將所有job的統計數據匯總報告。
- --output-format=<format> :指定輸出格式,如 json 、 normal 。 json 格式便於腳本解析和數據分析。
- --fallocate=<type> :指定文件預分配方式,如 none 、 posix 、 truncate 。對於直接I/O到設備,此參數不適用。
- --fsync=<int> :每隔多少次寫入執行一次 fsync 。設置為 0 表示不執行 fsync ,這對於測試原始寫入性能很重要。
- --offset=<offset> :指定I/O操作的起始偏移量。用於測試SSD的特定區域。
- --randrepeat=<bool> :設置為 0 表示隨機I/O模式下不重複使用相同的隨機數序列,確保每次運行都是新的隨機模式。
- --norandommap :禁用隨機映射,確保隨機I/O是真正的隨機,而不是預先生成的映射。
- --rwmixread=<percent> :當 rw 或 randrw 模式時,指定讀取操作的百分比,例如70 表示70%讀取,30%寫入。
3.1.3 FIO測試模式與範例
以下是一些常見的FIO測試模式及其範例,這些範例可以直接應用於 /dev/nvme0n1 設備。
範例1:4KB隨機讀取性能 (IOPS)
這是衡量SSD小文件隨機讀取性能的標準測試,常用於評估資料庫、虛擬化等應用場景的響應速度。
fio --name=4k_randread_iops \
--ioengine=libaio \
--direct=1 \
--rw=randread \
--bs=4k \
--numjobs=4 \
--iodepth=32 \
--size=10g \
--filename=/dev/nvme0n1 \
--runtime=300 \
--group_reporting \
--output-format=json
--numjobs=4 :模擬4個並行應用程式或線程。
--iodepth=32 :每個job的隊列深度為32。總隊列深度為4 * 32 = 128,這通常能充分發揮NVMe SSD的並行處理能力。
--size=10g :每個job讀取10GB數據。由於是 time_based ,實際讀取量可能更多。
--runtime=300 :測試運行300秒。
範例2:4KB隨機寫入性能 (IOPS)
衡量SSD小文件隨機寫入性能,對於日誌記錄、資料庫寫入等應用至關重要。
fio --name=4k_randwrite_iops \
--ioengine=libaio \
--direct=1 \
--rw=randwrite \
--bs=4k \
--numjobs=4 \
--iodepth=32 \
--size=10g \
--filename=/dev/nvme0n1 \
--runtime=300 \
--group_reporting \
--output-format=json
範例3:128KB循序讀取性能 (Throughput)
衡量SSD大文件循序讀取性能,常用於評估文件傳輸、影音串流等應用場景的帶寬。
fio --name=128k_seqread_throughput \
--ioengine=libaio \
--direct=1 \
--rw=read \
--bs=128k \
--numjobs=1 \
--iodepth=1 \
--size=50g \
--filename=/dev/nvme0n1 \
--runtime=300 \
--group_reporting \
--output-format=json
--numjobs=1 --iodepth=1 :模擬單線程、單隊列深度,更接近單個大文件傳輸的場景。
範例4:128KB循序寫入性能 (Throughput)
衡量SSD大文件循序寫入性能,對於數據備份、大文件拷貝等應用至關重要。
fio --name=128k_seqwrite_throughput \
--ioengine=libaio \
--direct=1 \
--rw=write \
--bs=128k \
--numjobs=1 \
--iodepth=1 \
--size=50g \
--filename=/dev/nvme0n1 \
--runtime=300 \
--group_reporting \
--output-format=json
範例5:4KB隨機混合讀寫 (70%讀取, 30%寫入)
模擬常見的混合工作負載,如Web伺服器、VDI(虛擬桌面基礎設施)等。
fio --name=4k_randrw_70read_30write \
--ioengine=libaio \
--direct=1 \
--rw=randrw \
--rwmixread=70 \
--bs=4k \
--numjobs=4 \
--iodepth=32 \
--size=10g \
--filename=/dev/nvme0n1 \
--runtime=300 \
--group_reporting \
--output-format=json
腳本範例:自動化FIO測試套件
以下是一個Python腳本,用於自動化執行一系列FIO測試,並解析JSON輸出,提取關鍵性能指標。這使得測試結果的收集和分析更加高效。
import subprocess
import json
import os
import datetime
def run_fio_test(test_name, params, device_path, output_dir="fio_results"):
"""執行 FIO 測試並返回解析後的 JSON 結果"""
print(f"\n--- Running FIO test: {test_name} ---")
os.makedirs(output_dir, exist_ok=True)
# 構建 FIO 命令
fio_cmd = [
"fio",
f"--name={test_name}",
f"--filename={device_path}",
"--output-format=json",
"--group_reporting",
]
for key, value in params.items():
if value is not None:
fio_cmd.append(f"--{key}={value}")
else:
fio_cmd.append(f"--{key}")
try:
result = subprocess.run(fio_cmd, capture_output=True, text=True, check=True)
fio_output = json.loads(result.stdout)
# 儲存原始 JSON 結果
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = os.path.join(output_dir, f"{test_name}_{timestamp}.json")
with open(output_filename, "w") as f:
json.dump(fio_output, f, indent=4)
print(f"FIO raw output saved to {output_filename}")
return fio_output
except subprocess.CalledProcessError as e:
print(f"Error running FIO test {test_name}: {e}")
print(f"Stderr: {e.stderr}")
return None
except json.JSONDecodeError:
print(f"Error decoding FIO JSON output for {test_name}")
print(f"Raw output: {result.stdout}")
return None
def parse_fio_results(fio_output):
"""解析 FIO JSON 輸出,提取關鍵性能指標"""
if not fio_output or "jobs" not in fio_output:
return None
results = {}
for job in fio_output["jobs"]:
job_name = job["jobname"]
if "read" in job:
read_data = job["read"]
results[f"{job_name}_read_iops"] = read_data["iops"]
results[f"{job_name}_read_bw_mbps"] = read_data["bw"] / 1024 # KB/s to MB/s
results[f"{job_name}_read_lat_ns_mean"] = read_data["lat_ns"]["mean"]
results[f"{job_name}_read_lat_ns_99th"] = read_data["lat_ns"]["percentile"]["99.000000"]
if "write" in job:
write_data = job["write"]
results[f"{job_name}_write_iops"] = write_data["iops"]
results[f"{job_name}_write_bw_mbps"] = write_data["bw"] / 1024
results[f"{job_name}_write_lat_ns_mean"] = write_data["lat_ns"]["mean"]
results[f"{job_name}_write_lat_ns_99th"] = write_data["lat_ns"]["percentile"]["99.000000"]
return results
# --- 主流程 --- #
if __name__ == "__main__":
NVME_DEVICE = "/dev/nvme0n1" # 根據實際裝置調整
test_cases = {
"4k_randread": {
"ioengine": "libaio", "direct": 1, "rw": "randread", "bs": "4k",
"numjobs": 4, "iodepth": 32, "size": "10g", "runtime": 60, "time_based": None
},
"4k_randwrite": {
"ioengine": "libaio", "direct": 1, "rw": "randwrite", "bs": "4k",
"numjobs": 4, "iodepth": 32, "size": "10g", "runtime": 60, "time_based": None
},
"128k_seqread": {
"ioengine": "libaio", "direct": 1, "rw": "read", "bs": "128k",
"numjobs": 1, "iodepth": 1, "size": "50g", "runtime": 60, "time_based": None
},
"128k_seqwrite": {
"ioengine": "libaio", "direct": 1, "rw": "write", "bs": "128k",
"numjobs": 1, "iodepth": 1, "size": "50g", "runtime": 60, "time_based": None
},
"4k_randrw_70_30": {
"ioengine": "libaio", "direct": 1, "rw": "randrw", "rwmixread": 70,
"bs": "4k", "numjobs": 4, "iodepth": 32, "size": "10g", "runtime": 60, "time_based": None
},
}
all_test_results = {}
for name, params in test_cases.items():
fio_output = run_fio_test(name, params, NVME_DEVICE)
if fio_output:
parsed_results = parse_fio_results(fio_output)
if parsed_results:
all_test_results.update(parsed_results)
print("Parsed Results:")
for k, v in parsed_results.items():
print(f" {k}: {v:.2f}")
else:
print(f"Failed to parse results for {name}")
else:
print(f"FIO test {name} failed.")
print("\n--- All Test Results Summary ---")
for k, v in all_test_results.items():
print(f"{k}: {v:.2f}")
# 可選:保存結果為 CSV
# import pandas as pd
# df = pd.DataFrame([all_test_results])
# df.to_csv("fio_summary_results.csv", index=False)
# print("Summary results saved to fio_summary_results.csv")
將上述Python腳本保存為 run_nvme_fio_tests.py 。運行前請確保FIO已安裝,並將NVME_DEVICE 變量修改為您的NVMe SSD設備路徑(例如 /dev/nvme0n1 )。
3.2 關鍵性能指標 (KPIs)
在FIO測試結果中,我們需要關注以下幾個關鍵性能指標:
- IOPS (Input/Output Operations Per Second):每秒輸入/輸出操作數。衡量SSD處理小文件隨機讀寫請求的能力。IOPS越高,表示SSD響應請求的速度越快,對於資料庫、虛擬化等應用至關重要。
- 測量方法:通常通過執行小塊(如4KB)隨機讀寫測試來測量。FIO會直接在輸出中給出IOPS值。
- 業界標準:消費級SSD通常在數萬到數十萬IOPS,企業級SSD可達數十萬甚至百萬級IOPS。
- Throughput (吞吐量,或稱帶寬):每秒傳輸的數據量,通常以MB/s或GB/s表示。衡量SSD處理大文件循序讀寫的能力。吞吐量越高,表示SSD傳輸數據的速度越快,對於文件拷貝、影音編輯、大數據分析等應用至關重要。
- 測量方法:通常通過執行大塊(如128KB、1MB)循序讀寫測試來測量。FIO會直接在輸出中給出帶寬(bw)值。
- 業界標準:SATA SSD最高約550MB/s,PCIe Gen3 NVMe SSD可達3.5GB/s,PCIe Gen4 NVMe SSD可達7GB/s,PCIe Gen5 NVMe SSD可達14GB/s甚至更高。
- Latency (延遲):完成一個I/O操作所需的時間,通常以微秒(μs)或毫秒(ms)表示。延遲越低,表示SSD響應越迅速。對於實時應用、資料庫事務處理等對響應時間敏感的場景至關重要。
- 測量方法:FIO會提供平均延遲(mean latency)以及各種百分位延遲(如99thpercentile latency)。99th percentile latency表示99%的I/O請求都能在這個時間內完成,這對於評估最壞情況下的性能非常重要。
- 業界標準:消費級SSD通常在數十微秒到數百微秒,企業級SSD可達數微秒到數十微秒。
- Write Amplification (寫入放大,WA):實際寫入NAND Flash的數據量與主機發出的寫入數據量之比。WA值越接近1,表示SSD的寫入效率越高,NAND Flash的磨損越小,壽命越長。WA值過高會加速NAND Flash的損耗。
- 測量方法:通常通過監控SSD的SMART日誌中的Host Writes和NAND Writes來計算: WA = NAND Writes / Host Writes 。FIO本身不直接提供WA值,需要結合SMART日誌分析。
- Quality of Service (QoS):服務質量。除了平均性能,QoS更關注性能的穩定性和一致性,特別是在高負載下的延遲抖動。例如,99.99%延遲(即萬分之9999的請求延遲)是衡量企業級SSD QoS的重要指標。
3.3 不同工作負載下的性能測試
單純的隨機讀寫或循序讀寫測試不足以全面評估SSD在真實應用中的表現。我們需要模擬各種典型的工作負載,以更貼近實際使用場景。
- 數據庫工作負載:
- 特點:通常是小塊(4KB-16KB)隨機讀寫,讀寫比例不固定,對IOPS和低延遲要求極高。
- FIO參數建議: --rw=randrw , --bs=4k 或 8k 或 16k , --iodepth 較高(如64、128、256), --rwmixread 根據數據庫類型調整(OLTP通常讀多寫少,OLAP讀多)。
- 範例:模擬OLTP數據庫負載(70%隨機讀,30%隨機寫,4KB塊大小,高隊列深度)
bash fio --name=oltp_db_workload \ --ioengine=libaio \ --direct=1 \ --rw=randrw \ --rwmixread=70 \ --bs=4k \ --numjobs=8 \--iodepth=128 \ --size=50g\ --filename=/dev/nvme0n1 \ --runtime=600 \ --group_reporting \ --output-format=json
- 虛擬化工作負載 (VDI/VMware):
- 特點:多個虛擬機同時運行,I/O模式高度隨機,塊大小多樣(4KB-64KB),混合讀寫,對IOPS和延遲敏感。
- FIO參數建議: --rw=randrw , --bsrange=4k-64k (混合塊大小), --iodepth 極高(如256、512甚至更高), --numjobs 模擬虛擬機數量。
- 範例:模擬VDI負載(混合塊大小,高並發) bash fio --name=vdi_workload\ --ioengine=libaio \ --direct=1 \ --rw=randrw \ --rwmixread=80 \ --bsrange=4k-64k \ --numjobs=16 \ --iodepth=64 \ --size=100g \ --filename=/dev/nvme0n1 \ --runtime=900 \ --group_reporting \ --output-format=json
- 大數據分析工作負載 (Hadoop/Spark):
- 特點:通常涉及大量循序讀取和寫入,塊大小較大(256KB-1MB),但也有隨機讀取元數據的需求。對吞吐量要求高。
- FIO參數建議: --rw=read 或 write 或 rw , --bs=256k 或 1m , --iodepth 適中。
- 範例:模擬大數據循序讀取 bash fio --name=bigdata_seqread \ --ioengine=libaio \ --direct=1 \ --rw=read \ --bs=1m \ --numjobs=4\ --iodepth=8 \ --size=200g \ --filename=/dev/nvme0n1 \ --runtime=600 \ --group_reporting \ --output-format=json
- Web伺服器工作負載:
- 特點:通常是小塊隨機讀取(靜態文件、緩存),寫入較少(日誌)。對IOPS和低延遲要求高。
- FIO參數建議: --rw=randread 或 randrw , --bs=4k 或 8k , --rwmixread 高(如90%)。
3.4 性能一致性 (Performance Consistency)
性能一致性是衡量SSD在長時間運行或不同負載下的性能穩定性。一個優秀的SSD不僅要有高的峰值性能,更要在持續工作負載下保持性能的穩定,避免出現性能驟降(掉速)的情況。
- 預處理 (Preconditioning):
- 在進行性能測試之前,特別是隨機寫入測試,必須對SSD進行預處理。這是因為SSD內部有垃圾回收(Garbage Collection, GC)和磨損均衡(Wear Leveling)機制。新盤或空盤的性能通常很高,因為有大量空閒塊可供寫入。但隨著寫入數據的增多,空閒塊減少,GC活動會增加,導致性能下降。預處理就是通過持續寫入,將SSD填充到一個穩態(Steady State),使其內部GC活動達到一個穩定的水平,這樣測得的性能才更具代表性。
- 方法:通常使用FIO對整個SSD進行兩倍容量的循序寫入或隨機寫入,直到SMART日誌中的 Percentage Used 達到一定水平(如50%以上),或者性能曲線趨於平穩。
- 範例:對SSD進行預處理(兩倍容量的循序寫入) bash fio --name=precondition_write \ --ioengine=libaio \ --direct=1 \ --rw=write \ --bs=1m \ --numjobs=1 \ --iodepth=32 \ --size=2t \ --filename=/dev/nvme0n1 \ --runtime=3600 \ --group_reporting \ --output-format=json
- 注意: --size=2t 表示寫入2TB數據,請根據您的SSD容量調整。 --runtime可以設置足夠長的時間,確保寫入量達到預期。
- 穩態性能測試 (Steady State Performance):
- 在預處理之後,進行長時間的隨機寫入測試,並記錄每個時間段(如每隔10秒或1分鐘)的IOPS和延遲數據。觀察性能曲線是否平穩,是否存在明顯的性能抖動或驟降。
- 範例:穩態4KB隨機寫入測試 bash fio --name=steady_state_4k_randwrite \ --ioengine=libaio \ --direct=1 \--rw=randwrite \ --bs=4k \ --numjobs=4 \ --iodepth=32 \ --size=100g \ --filename=/dev/nvme0n1 \ --runtime=3600 \ --group_reporting \ --output-format=json \ --latency_percentiles=1\ --hdr_file=steady_state_latency.log
- --latency_percentiles=1 和 --hdr_file 參數可以讓FIO輸出更詳細的延遲分佈數據,便於後續分析延遲抖動。
- 性能衰減與恢復 (Performance Degradation and Recovery):
- 測試SSD在長時間高強度寫入後性能衰減的程度,以及在空閒一段時間後(讓GC和磨損均衡有機會運行)性能恢復的能力。這對於評估SSD的韌體優化程度非常重要。
- 方法:先進行長時間的隨機寫入,觀察性能下降。然後讓SSD空閒一段時間(如數小時),再進行相同的測試,觀察性能是否恢復到接近初始水平。
性能驗證是確保NVMe SSD滿足應用需求的核心環節。通過FIO等工具的靈活運用,結合對關鍵性能指標的深入理解,以及對不同工作負載和性能一致性的考量,我們可以全面評估SSD的性能表現。