1. Tổng quan
Bài này tổng hợp kinh nghiệm “đo cho ra ngô ra khoai” với fio: cách dựng kịch bản test (random/sequential), chọn bs
, iodepth
(QD), numjobs
, hiểu và điều khiển các lớp cache (OS/RAID/SSD), đọc – diễn giải kết quả (IOPS/BW/latency/percentiles), cùng các mẹo thực chiến cho môi trường server (RAID controller, HBA, NVMe, SSD SATA, HDD). Mục tiêu: bạn có thể tự tin thiết kế bài test đúng câu hỏi, chạy đúng cách, đọc đúng kết quả và tránh bẫy.
2. Nguyên tắc cốt lõi
Định luật Little (điểm tựa chọn QD):
Concurrency (QD tổng) ≈ Throughput (IOPS) × Latency (giây)
Trong fio: QD tổng = iodepth × numjobs
.
→ Muốn đạt IOPS mục tiêu, QD phải đủ lớn; nhưng QD quá cao sẽ đội p99/p99.9.
Workload quyết định cấu hình:
- Random nhỏ (4K/8K) → đo IOPS/latency.
- Sequential lớn (128K/256K/1M) → đo throughput (MB/s).
- Mixed (r/w) → mô phỏng OLTP, VM, file server.
- Latency-first → test QD=1.
- Path I/O phải sạch:
- kiểm soát cache.
- Chọn ioengine phù hợp, đảm bảo không có bottleneck ngoài ý muốn.
3. Các lớp cache & cách kiểm soát
3.1. OS Page Cache (Linux):
- Bị bỏ qua khi
--direct=1
. - Nếu test trên filesystem và muốn cache: không khai báo
--direct=1
; nếu không muốn cache khai báo--direct=1
. - Dọn cache (thận trọng):
echo 3 > /proc/sys/vm/drop_caches
(chỉ khi cần và hiểu rõ hệ đang chạy gì).
3.2. RAID Controller Cache (ví dụ HPE Smart Array P440):
- Read cache / Write-back cache do firmware điều khiển.
--direct=1
không bypass cache phần cứng này.- Bật/tắt bằng công cụ (vd
ssacli
). - Cache OFF → phản ánh “raw SSD”
- Cache ON → phản ánh hiệu năng thực chiến của hệ lưu trữ có tính luôn cache của card RAID.
3.3. SSD Internal Cache (DRAM/SLC, FTL):
- Không bypass được bằng fio; đó là bản chất SSD.
- Cần precondition đối với SSD enterprise/NVMe khi so sánh nghiêm ngặt.
Sơ đồ luồng I/O (điển hình):
fio → [libaio/io_uring] → kernel block layer → driver → (RAID cache?) → SSD cache/FTL → NAND
↑
(OS page cache - bị bỏ qua nếu --direct=1)
4. Chọn ioengine & đường đi I/O
4.1. --ioengine=io_uring
(kernel 5.x trở lên):
- Overhead thấp, batching tốt, latency mượt → khuyến nghị mặc định.
- Nếu hệ cũ: dùng
--ioengine=libaio
.
4.2. --direct=1
:
- Bỏ qua OS page cache, phù hợp test raw.
- Không ảnh hưởng cache RAID/SSD.
4.3. Các tùy chọn latency thấp (khi cần):
iodepth_batch=
/iodepth_batch_complete_min=
(tối ưu submit/complete).cpus_allowed
/numa_cpu_nodes
/pin IRQ để giảm jitter (môi trường nhạy latency).
5. Chọn bs
, iodepth
(QD) và numjobs
5.1. bs
(block size):
- 4K–8K: random IOPS/latency (DB/metadata).
- 16K–32K: mixed OLTP.
- 64K–256K–1M: throughput (backup/scan).
- Nếu có RAID: cân nhắc khớp stripe (vd stripe 256K → test seq với
bs=256k
).
5.2. iodepth
(QD mỗi job) & numjobs
(số job):
- QD tổng =
iodepth × numjobs
. - SSD SATA: QD hữu ích per-disk thường 16–32 (giới hạn NCQ ~32).
- NVMe: QD cao hơn nhiều (64–256+).
- HDD: QD nhỏ (1–4) đủ bão hòa.
- Quy tắc: tăng
iodepth
trước, sau đó tăngnumjobs
nếu 1 thread không đủ bơm I/O.
5.3. Công thức áp dụng:
- Chạy QD1 lấy L (latency trung bình, giây).
- Ước lượng IOPS mục tiêu I.
- Tính QD cần ≈ I × L, rồi chọn
numjobs × iodepth ≈ QD cần
. - Sweep quanh điểm đó và theo dõi p99/p99.9.
6. Bộ kịch bản test mẫu (đủ “tất cả trường hợp” hay gặp)
6.1. Latency QD1 (4K random read – đo độ trễ “thật”)
fio --name=lat-randread --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=randread --bs=4k --numjobs=1 --iodepth=1 \
--time_based --runtime=120 --ramp_time=30 --group_reporting
6.2. Max IOPS random 4K (read/write)
# Read
fio --name=iops-randread --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=randread --bs=4k --numjobs=4 --iodepth=32 \
--time_based --runtime=120 --ramp_time=30 --group_reporting
# Write (không dùng --fdatasync=1 nếu đo “raw performance”)
fio --name=iops-randwrite --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=randwrite --bs=4k --numjobs=4 --iodepth=32 \
--time_based --runtime=120 --ramp_time=30 --group_reporting
6.3. Throughput sequential (khớp stripe, ví dụ 256K)
# Read
fio --name=seq-read --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=read --bs=256k --numjobs=1 --iodepth=32 \
--time_based --runtime=120 --ramp_time=30 --group_reporting
# Write
fio --name=seq-write --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=write --bs=256k --numjobs=1 --iodepth=32 \
--time_based --runtime=120 --ramp_time=30 --group_reporting
6.4. Random throughput lớn (64K/128K/256K)
# Random read 128K
fio --name=randread-128k --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=randread --bs=128k --numjobs=4 --iodepth=32 \
--time_based --runtime=120 --ramp_time=30 --group_reporting
# Random write 128K (tương tự)
6.5. Mixed OLTP (ví dụ 70% read / 30% write, 4K)
fio --name=mixed-4k --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=randrw --rwmixread=70 --bs=4k --numjobs=4 --iodepth=32 \
--time_based --runtime=180 --ramp_time=30 --group_reporting
6.6. Sweep QD để tìm plateau (IOPS tăng chậm lại)
# Ví dụ bash loop
for i in 1 2 4 8 16 32; do
fio --name=rd4k-QD$i --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=randread --bs=4k --numjobs=4 --iodepth=$i \
--time_based --runtime=60 --ramp_time=20 --group_reporting \
--output=rd4k-QD$i.json --output-format=json
done
6.7. Test “durability” (đo flush chi phí thực)
# Trên filesystem (không --direct) + fdatasync để mô phỏng WAL/log
fio --name=wal-like --filename=/mnt/fs/testfile --ioengine=io_uring \
--rw=randwrite --bs=4k --numjobs=1 --iodepth=1 \
--fdatasync=1 --size=10G --time_based --runtime=120 --ramp_time=30
6.8. Preconditioning SSD (khi cần so sánh nghiêm ngặt)
# Viết tuần tự full device trước khi test để ổ định trạng
fio --name=precond --filename=/dev/<dev> --ioengine=io_uring --direct=1 \
--rw=write --bs=1M --iodepth=32 --numjobs=1 --size=100% --loops=1
7. Đọc & diễn giải kết quả fio
7.1. Trường quan trọng:
IOPS
,BW
(MB/s),clat
(completion latency),slat
(submit latency).- Percentiles: p50/p90/p95/p99/p99.9 (tail latency).
cpu
(usr/sys),ctx
(context switch) → overhead.Disk stats
/util=...%
: thiết bị có bị “đầy” không.
7.2. Cách nhìn nhanh:
- Random 4K: xem IOPS và p99/p99.9 (jitter/tail).
- Sequential: xem BW; nếu ~trần giao tiếp (SATA ~550 MB/s), coi như OK.
- Mixed: chú ý độ cân bằng r/w, p99 write thường xấu hơn read.
7.3. Khi so sánh A/B:
- Giữ mọi thứ cố định trừ một biến (vd bật/tắt cache).
- Chạy ≥2–3 lần, bỏ lượt đầu (warm-up).
- Dùng
--output-format=json
+ ghi log (--write_bw_log
,--write_lat_log
) để vẽ biểu đồ.
8. Tối ưu phần cứng/firmware cho test
8.1. RAID vs HBA:
- Muốn “raw passthrough”: HBA/IT mode (nếu controller hỗ trợ).
- Smart Array P440 (Gen9) không HBA thực sự → dùng RAID0 1-disk để OS nhìn thấy disk.
- Bật/tắt cache trên logical drive tùy mục tiêu (raw vs thực chiến).
8.2. Stripe size & bs:
- Sequential: đặt
bs
bằng hoặc bội số stripe (vd 256K) để đẹp. - Random nhỏ: stripe ít ảnh hưởng nếu 1 disk; với nhiều disk, mismatch có thể gây phân phối kém.
8.3. Kernel/block layer:
- Scheduler:
none
cho NVMe,mq-deadline
/none
cho SATA; tránhcfq
trên server mới. read_ahead_kb
: tăng khi test seq; giảm khi test random.nr_requests
/queue_depth
: đừng vượt trần thiết bị (SATA NCQ ~32).
8.4. CPU/NUMA/IRQ:
Pin CPU cho fio, cân bằng IRQ NVMe/SAS, bật performance
governor để giảm drift.
9. Bẫy thường gặp
- Dùng
--fdatasync=1
trên raw + cache OFF và mong IOPS giảm: sẽ không khác mấy (không có gì để flush). - Quên
--direct=1
khi muốn đo raw: số đo cao giả do page cache. - Dùng nhiều job cho sequential: biến seq thành semi-random, throughput xấu đi.
- Không đặt
--ramp_time
: lấy số khi hệ chưa ổn định. randrepeat=1
(mặc định) khi bạn muốn dữ liệu ngẫu nhiên khác nhau giữa các lần → đặtrandrepeat=0
.- Test trên thiết bị đang mount/đang dùng: có thể mất dữ liệu hoặc số đo nhiễu.
- Không precondition SSD khi so sánh mẫu khác nhau → kết quả “đẹp” bất thường.
10. Lời khuyên
Xác định mục tiêu trước (IOPS? throughput? latency? durability?) rồi mới chọn bs/QD/job/cache
.
Quy trình 5 bước:
- Test latency QD1 lấy baseline.
- Sweep QD để tìm điểm bão hòa (plateau).
- Chạy random 4K (IOPS) và sequential 256K (BW).
- Chạy random throughput 128K–256K (VM/file server).
- Chạy mixed (70/30 hoặc theo app).
Test A/B cache OFF vs ON để biết bạn được lợi bao nhiêu trong thực tế.
Luôn lưu JSON/log, đặt tên rõ ràng, có ngữ cảnh (firmware, nhiệt độ, kernel, cache state).
12. Sơ đồ logic chọn tham số
[Hỏi mục tiêu?] --> [Latency?] --> QD=1, bs=4k, read/write
\-> [Max IOPS?] --> bs=4k, tăng QD (iodepth×numjobs) đến plateau
\-> [Max BW?] --> bs=256k (hoặc 128k/1M), numjobs=1 (seq), iodepth=32
\-> [Mixed?] --> randrw rwmixread=..., bs theo app (4k–64k), QD vừa phải
\-> [Durability?]--> trên FS, không --direct, dùng --fdatasync=1
[Cache mục tiêu?] --> Raw (cache OFF) | Thực chiến (cache ON)
13. Checklist trước khi chạy
- Xác định: random/seq, bs, r/w mix, thời gian chạy, ramp.
- Chọn ioengine (
io_uring
nếu có),--direct=1
nếu cần raw. - Kiểm tra cache RAID/SSD theo mục tiêu.
- Đảm bảo thiết bị không dùng bởi dịch vụ khác; biết rõ rủi ro dữ liệu.
- Cài đặt scheduler/CPU governor hợp lý.
- Thiết lập log/JSON để lưu kết quả.
14. Kết luận
Thiết kế bài test fio đúng mục tiêu quan trọng hơn mọi thứ khác. Hãy dùng định luật Little để ước lượng QD, chọn bs
theo workload, điều khiển cache đúng ý (OS/RAID/SSD) và đọc kỹ percentiles để hiểu tail latency. Với quy trình – kịch bản ở trên, bạn sẽ có số đo tin cậy, tái lập và hữu ích để ra quyết định (tối ưu, so sánh phần cứng, đặt kỳ vọng cho ứng dụng).