超好用IO Testing Tool-fio

測試硬碟讀寫效能有非常多因素要考慮,大部份軟體都只能測試循序或隨機存取,在實際的情況卻往往是資料庫同時被insert, update又需要select,並且還會有背景程式紀錄log,甚至還會有多個process要進行IO,這樣複雜的IO行為很難知道效能如何,fio則是一個非常方便的工具可以模擬出這類型的情況也可以做單一測試,顯示的資訊也非常詳細,想要測試IO的話fio絕對會是首選。
安裝方法,Debian系列上面使用

 sudo apt-get install fio

其他系統則是用其他套件管理軟體,或著可以到FIO Project下載

 

首先我們先做簡單的測試,跳過Memory直接向印碟循序讀取10MB的資料

  • name代表這個job的名稱為read_test
  • rw則是指定read,可以設成read/write/randread/randwrite
  • size指定10MB的資料
  • direct指定1代表直接向硬碟讀寫跳過Memory Swap機制
 fio --name=read_test --rw=read --size=10m --direct=1

可以看到結果如下,其中

  • bw: 平均頻寬=總資料量/總執行時間下一個範例會設定每一定時間才存取一次,時間的周期愈長頻寬就會愈低,因為此頻寬的分母是總執行時間,這要注意!
  • iops: IO operation per second,通常看硬碟得存取速度會看的一個指標。
  • clat: Completion Latency,從submit IO access到等待IO再到收到完成訊號的延遲時間,在asynchronous IO時是包含等待時間的喔!
read_test: (g=0): rw=read, bs=4K-4K/4K-4K, ioengine=sync, iodepth=1
2.0.8
Starting 1 process

read_test: (groupid=0, jobs=1): err= 0: pid=19687
read_test : io=10240KB, bw=50945KB/s, iops=12736 , runt= 201msec
clat (usec): min=34 , max=17765 , avg=76.40, stdev=459.68
lat (usec): min=34 , max=17766 , avg=76.54, stdev=459.70
clat percentiles (usec):
| 1.00th=[ 36], 5.00th=[ 42], 10.00th=[ 45], 20.00th=[ 46],
| 30.00th=[ 47], 40.00th=[ 48], 50.00th=[ 50], 60.00th=[ 55],
| 70.00th=[ 79], 80.00th=[ 80], 90.00th=[ 87], 95.00th=[ 98],
| 99.00th=[ 137], 99.50th=[ 165], 99.90th=[ 3344], 99.95th=[14784],
| 99.99th=[17792]
lat (usec) : 50=47.11%, 100=49.41%, 250=3.20%, 500=0.12%, 750=0.04%
lat (msec) : 4=0.04%, 20=0.08%
cpu : usr=0.00%, sys=22.00%, ctx=2564, majf=0, minf=26
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued : total=r=2560/w=0/d=0, short=r=0/w=0/d=0

Run status group 0 (all jobs):
READ: io=10240KB, aggrb=50945KB/s, minb=50945KB/s, maxb=50945KB/s, mint=201msec, maxt=201msec

Disk stats (read/write):
md127: ios=1441/0, merge=0/0, ticks=0/0, in_queue=0, util=0.00%, aggrios=1280/0, aggrmerge=0/0, aggrticks=78/0, aggrin_queue=78, aggrutil=29.58%
sda: ios=1280/0, merge=0/0, ticks=64/0, in_queue=64, util=20.58%
sdb: ios=1280/0, merge=0/0, ticks=92/0, in_queue=92, util=29.58%

如果只要當作一般的IO測試工具只要學到這樣就足夠使用了,接下來要講的是進階的應用,使用腳本來指定這次要模擬的環境,腳本中可以設定的項目有

  • [名稱] : 指定job的名稱,global為保留字,用來指定每個job都有的共同屬性
  • blocksize : 一次IO的大小,單位為Bytes,用逗號隔開分別指定read, write的block size
  • direct : 是否使用Direct IO
  • directory : 指定要被測試的裝置或目錄
  • filename : IO的檔案名稱,不指定的話每個process都是分開的,指定的話可以強迫每個process都存取同一份檔案(例如database的某個table同時被存取)。
  • ioengine : 指定產生IO的方法,可以指定
    • sync : 一般的IO,page swap才會寫入disk
    • libaio : Linux AIO
    • osixaio : 用glibc操作POSIX AIO
    • mmap : 用mmap, memcpy進行讀寫
    • cpuio : 不進行IO,用來模擬CPU Job時設的,可以模擬出CPU的loading,測試對於其他IO access的影響程度
  • iodepth : 使用AIO時同時進行的IO數量上限。可以從結果看出大部份時間的IO平行度如何,大部份時間都可以同時有多少個processes存取IO
  • numjobs : 執行這個job的process數量
  • runtime : 指定執行的時間
  • rw : 指定IO的方式
    • read : 循序讀取
    • write : 循序寫入
    • randread : 隨機讀取
    • randwrite : 隨機寫入
    • rw : 循序讀寫
    • randrw : 隨機讀寫
  • size : 這個Job要存取的IO大小
  • thinktime : 這次的job結束之後到下一次job之間sleep的時間,單位為us
  • thinktime_blocks : 每多少次IO之後等一次thinktime
  • thinktime_spin : 在thinktime的等待時間內花多少時間模擬CPU讓他忙碌。 thinktime = thinktime_spin + CPU閒置時間
  • time_based : 使腳本到達指定停止時間之前會不停重複執行jobs

 

接下來我們來模擬一個真實的情境,測試看看硬碟再這種情況下存取效能如何,情境是一個簡單的資料庫系統,同時有4個processes再存取IO,兩個是資料庫的查詢,一個是資料庫紀錄log,最後一個則是再做資料備份的動作,要注意的是,腳本內每一個job都會被同時執行的喔!!不是一個接著一個執行的。

  • global : 設定目錄還有執行時間,最後設定終止條件為時間到。
  • query_test : 設定資料大小,讀寫方式,這邊設定存取同樣的檔案query.dat,numjobs=2則是這個job會fork出另一個同樣的processes執行同樣的這個事情(job),也就是會有兩個processes對query.dat做隨機讀取的動作,這邊假設資料庫是會。
  • data_log : 設定隨機讀寫做資料庫的log存取
  • copy : 做資料庫的資料搬移和備份
[global]
# set directory/device of testing target
directory=./
# set the simulation time (repeat jobs in 40s)
runtime=40
# set repeating jobs till timeout instead of job finished.
time_based

[query_test]
size=128m
rw=randread
ioengine=mmap
# sleep 10ms after job done
thinktime=10k
# process query returned data for 2ms
thinktime_spin=2k
# default use different file for testing. here I force it to use the same file
filename=query.dat
# set two clients for quering
numjobs=2
# swap(write back) when IO page fault (default value)
direct=0

[data_log]
size=16m
rw=randrw
ioengine=libaio
iodepth=32
# sleep 2s after job done
thinktime=2m
# sleep per two jobs, instead of one job
thinktime_blocks=2
blocksize=1k,4k
# direct IO read/write
direct=1

[copy]
size=100m
rw=randrw
# sleep 2ms after job done
thinktime=2k

其執行結果如下:

query_test: (g=0): rw=randread, bs=4K-4K/4K-4K, ioengine=mmap, iodepth=1
query_test: (g=0): rw=randread, bs=4K-4K/4K-4K, ioengine=mmap, iodepth=1
data_log: (g=0): rw=randrw, bs=1-1/4K-4K, ioengine=libaio, iodepth=32
copy: (g=0): rw=randrw, bs=4K-4K/4K-4K, ioengine=sync, iodepth=1
2.0.8
Starting 4 processes
fio: io_u error on file .//data_log.3.0: Invalid argument
     read offset=1646241, buflen=1
fio: io_u error on file .//data_log.3.0: Invalid argument
     read offset=13740788, buflen=1
fio: pid=20322, err=22/file:io_u.c:1305, func=io_u error, error=Invalid argument
Jobs: 3 (f=3): [rrXm] [100.0% done] [1158K/595K /s] [289 /148  iops] [eta 00m:00s]
query_test: (groupid=0, jobs=1): err= 0: pid=20320
  read : io=10736KB, bw=274793 B/s, iops=67 , runt= 40007msec
    clat (usec): min=3 , max=327742 , avg=4583.50, stdev=9637.55
     lat (usec): min=3 , max=327742 , avg=4583.84, stdev=9637.53
    clat percentiles (usec):
     |  1.00th=[    4],  5.00th=[    6], 10.00th=[    6], 20.00th=[    7],
     | 30.00th=[  108], 40.00th=[  145], 50.00th=[ 1976], 60.00th=[ 3888],
     | 70.00th=[ 5856], 80.00th=[ 7840], 90.00th=[10304], 95.00th=[14912],
     | 99.00th=[35584], 99.50th=[48384], 99.90th=[79360], 99.95th=[113152],
     | 99.99th=[329728]
    bw (KB/s)  : min=  112, max=  326, per=27.26%, avg=269.29, stdev=48.70
    lat (usec) : 4=0.52%, 10=22.65%, 20=1.01%, 50=0.04%, 100=1.94%
    lat (usec) : 250=18.74%, 500=0.48%, 750=0.52%, 1000=0.60%
    lat (msec) : 2=3.69%, 4=10.43%, 10=28.87%, 20=7.27%, 50=2.76%
    lat (msec) : 100=0.41%, 250=0.04%, 500=0.04%
  cpu          : usr=13.85%, sys=0.23%, ctx=5547, majf=2033, minf=681
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=2684/w=0/d=0, short=r=0/w=0/d=0
query_test: (groupid=0, jobs=1): err= 0: pid=20321
  read : io=10728KB, bw=274540 B/s, iops=67 , runt= 40014msec
    clat (usec): min=2 , max=341345 , avg=4597.91, stdev=9680.45
     lat (usec): min=3 , max=341346 , avg=4598.26, stdev=9680.43
    clat percentiles (usec):
     |  1.00th=[    4],  5.00th=[    6], 10.00th=[    6], 20.00th=[    7],
     | 30.00th=[  110], 40.00th=[  147], 50.00th=[ 2040], 60.00th=[ 4192],
     | 70.00th=[ 6112], 80.00th=[ 7904], 90.00th=[10048], 95.00th=[14912],
     | 99.00th=[35584], 99.50th=[42752], 99.90th=[78336], 99.95th=[119296],
     | 99.99th=[342016]
    bw (KB/s)  : min=  117, max=  322, per=27.24%, avg=269.18, stdev=45.21
    lat (usec) : 4=0.48%, 10=21.77%, 20=0.60%, 50=0.07%, 100=1.90%
    lat (usec) : 250=19.65%, 500=0.41%, 750=0.48%, 1000=0.67%
    lat (msec) : 2=3.69%, 4=9.36%, 10=30.61%, 20=7.31%, 50=2.61%
    lat (msec) : 100=0.30%, 250=0.04%, 500=0.04%
  cpu          : usr=13.87%, sys=0.19%, ctx=5511, majf=2067, minf=644
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=2682/w=0/d=0, short=r=0/w=0/d=0
data_log: (groupid=0, jobs=1): err=22 (file:io_u.c:1305, func=io_u error, error=Invalid argument): pid=20322
  cpu          : usr=0.00%, sys=0.00%, ctx=2, majf=0, minf=53
  IO depths    : 1=3.1%, 2=6.2%, 4=12.5%, 8=25.0%, 16=50.0%, 32=3.1%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=16/w=16/d=0, short=r=0/w=0/d=0
copy: (groupid=0, jobs=1): err= 0: pid=20323
  read : io=18096KB, bw=463164 B/s, iops=113 , runt= 40008msec
    clat (usec): min=77 , max=291391 , avg=4573.79, stdev=7897.14
     lat (usec): min=77 , max=291392 , avg=4574.17, stdev=7897.14
    clat percentiles (usec):
     |  1.00th=[   92],  5.00th=[  107], 10.00th=[  114], 20.00th=[  141],
     | 30.00th=[  151], 40.00th=[  215], 50.00th=[ 2672], 60.00th=[ 4320],
     | 70.00th=[ 5984], 80.00th=[ 7776], 90.00th=[ 9792], 95.00th=[14656],
     | 99.00th=[32128], 99.50th=[39168], 99.90th=[66048], 99.95th=[68096],
     | 99.99th=[292864]
    bw (KB/s)  : min=   63, max=  604, per=45.81%, avg=452.59, stdev=122.68
  write: io=18024KB, bw=461322 B/s, iops=112 , runt= 40008msec
    clat (usec): min=3 , max=71 , avg=18.09, stdev= 5.18
     lat (usec): min=3 , max=72 , avg=18.58, stdev= 5.25
    clat percentiles (usec):
     |  1.00th=[    6],  5.00th=[    9], 10.00th=[   12], 20.00th=[   15],
     | 30.00th=[   16], 40.00th=[   18], 50.00th=[   19], 60.00th=[   19],
     | 70.00th=[   20], 80.00th=[   20], 90.00th=[   23], 95.00th=[   26],
     | 99.00th=[   34], 99.50th=[   39], 99.90th=[   53], 99.95th=[   65],
     | 99.99th=[   71]
    bw (KB/s)  : min=   76, max=  696, per=100.00%, avg=451.32, stdev=140.19
    lat (usec) : 4=0.02%, 10=2.96%, 20=30.17%, 50=16.67%, 100=1.53%
    lat (usec) : 250=18.76%, 500=0.23%, 750=0.16%, 1000=0.39%
    lat (msec) : 2=2.07%, 4=6.17%, 10=16.09%, 20=3.36%, 50=1.32%
    lat (msec) : 100=0.10%, 250=0.01%, 500=0.01%
  cpu          : usr=0.31%, sys=1.00%, ctx=13555, majf=0, minf=24
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=4524/w=4506/d=0, short=r=0/w=0/d=0

Run status group 0 (all jobs):
   READ: io=39560KB, aggrb=988KB/s, minb=268KB/s, maxb=452KB/s, mint=40007msec, maxt=40014msec
  WRITE: io=18024KB, aggrb=450KB/s, minb=450KB/s, maxb=450KB/s, mint=40008msec, maxt=40008msec

Disk stats (read/write):
    md127: ios=9112/4603, merge=0/0, ticks=0/0, in_queue=0, util=0.00%, aggrios=4554/2170, aggrmerge=15/188, aggrticks=25858/119378, aggrin_queue=145230, aggrutil=45.25%
  sda: ios=4578/2159, merge=5/196, ticks=25780/117456, in_queue=143236, util=44.49%
  sdb: ios=4531/2182, merge=25/181, ticks=25936/121300, in_queue=147224, util=45.25%

可以看到前四行顯示出了我們總共有4個processes在跑,兩個query_test一個data_log一個copy。

query_test:

query 的頻寬為0.27MB左右,這數字會這麼小的原因是因為分母的總執行時間是包含query和query間的等待時間的,所以頻寬會顯的很低,調整thinktime為0才會是全速讀取的頻寬。

clat 最大0.3秒,平均4ms這個速度算很快的,也可以接受。

clat percentiles : 這是執行時間的百分統計計圖值的意義為For example, if a score is in the 86th percentile, it is higher than 86% of the other scores.,詳細可以參考Wiki,所以說99.99th=[342016]代表0.34秒的執行時間比其他99.99%的點都還要久,換句話說就是只有0.01%的機會執行時間大於等於0.34秒。

IO depths : 100%都在1,因為這是沒有asynchronous access的測試,所以平行度為1

data_log:

IO depths : 8=25.0%, 16=50.0%,代表絕大部分的時間都有8~16個IO再同時進行,平行度大約接近16,是一個平行度很好的數據。

copy:

clat 最大0.3秒平均4ms結果和query的差不多,代表IO access的latency沒有受到太大影響

以上就是簡單的腳本測試和一些數值的說明,詳細說明可以參考man fio的說明~

另外,剛剛這樣的測試還不夠,通常都會想要測試自己系統的極限再哪,到底怎樣的配置才會導致效能大幅度下降,例如上面的例子,這種時候就要開始調整access的頻率、大小、諸如此類的參數直到找到latency變化量大或是太低的地方,這樣做完測試後就會對系統有更全面的了解,不過為了方便,已經有人提供出這樣子類型的測試腳本,其實可以直接使用,例如可以使用fio_scripts來做自動測試以及圖形化的輸出或表格整理,是一個非常好用的工具喔!善加利用github上大家open source的資源能夠更有效率的做效能分析。

 

4 thoughts on “超好用IO Testing Tool-fio

    1. 你說的兩台以上server同時跑其實也不難,不過理論上沒有辦法可以作到完美同時,最準確的作法是每台server都對npl time server做校準,然後到某一個時間點同時一起跑,如果server數量不多,求方便的話可以透過ssh遠端執行腳本掃過每台電腦就好,畢竟這種壓力測試把背景的壓力設定好之後壓力是固定的,只要透過ssh把每台都開啟壓力測試之後再執行想測試的程式即可

    2. Где — нибудь в северо — американских штатах ты на таких картинках столько капусты бы нарубил… Куда там Биллу Гейтсу с его АœÂµÃ»кÃþМягким…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s