1. 程式人生 > 其它 >31-套路篇:磁碟I/O效能優化的幾個思路

31-套路篇:磁碟I/O效能優化的幾個思路





I/O基準測試

I/O效能優化的目標是什麼?
換句話說觀察的這些I/O效能指標(比如IOPS、吞吐量、延遲等),要達到多少才合適呢?

事實上,I/O效能指標的具體標準,每個人估計會有不同的答案,因為每個人的應用場景、使用的檔案系統和物理磁碟等,都有可能不一樣
為了更客觀合理地評估優化效果,首先應該對磁碟和檔案系統進行基準測試
得到檔案系統或者磁碟I/O的極限效能



fio測試工具

fio(Flexible I/O Tester)是最常用的檔案系統和磁碟I/O效能基準測試工具
它提供了大量的可定製化選項,可以用來測試裸盤或者檔案系統在各種場景下的I/O效能
包括了不同塊大小、不同I/O引擎以及是否使用快取等場景

fio的安裝

# centos
[root@local_sa_192-168-1-6 ~]# yum install -y fio

fio的選項非常多, 通過幾個常見場景的測試方法,介紹一些最常用的選項
這些常見場景包括隨機讀隨機寫順序讀以及順序寫

# 隨機讀
[root@local_sa_192-168-1-6 ~]# fio -name=randread -direct=1 -iodepth=64 -rw=randread -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sda

# 隨機寫
[root@local_sa_192-168-1-6 ~]# fio -name=randwrite -direct=1 -iodepth=64 -rw=randwrite -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sda

# 順序讀
[root@local_sa_192-168-1-6 ~]# fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sda

# 順序寫
[root@local_sa_192-168-1-6 ~]# fio -name=write -direct=1 -iodepth=64 -rw=write -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sda

### 引數解釋
1. direct表示是否跳過系統快取,1就表示跳過系統快取
2. iodepth表示使用非同步I/O(asynchronous I/O,簡稱AIO)時,同時發出的I/O請求上限,示例中設定的是64
3. rw表示I/O模式,read/write分別表示順序讀/寫,randread/randwrite則分別表示隨機讀/寫
4. ioengine表示I/O引擎,它支援同步(sync)、非同步(libaio)、記憶體對映(mmap)、網路(net)等各種I/O引擎
上面示例中設定的libaio表示使用非同步I/O
5. bs表示I/O的大小,示例設定成了4K(這也是預設值)
6. filename表示檔案路徑,可以是磁碟路徑(測試磁碟效能),也可以是檔案路徑(測試檔案系統性能)
示例中把它設定成了磁碟/dev/sda
不過注意用磁碟路徑測試寫,會破壞這個磁碟中的檔案系統,所以在使用前一定要事先做好資料備份

使用fio測試順序讀的一個報告示例

[root@local_sa_192-168-1-6 ~]# fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sda
read: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.7
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=193MiB/s,w=0KiB/s][r=49.5k,w=0 IOPS][eta 00m:00s]
read: (groupid=0, jobs=1): err= 0: pid=29635: Thu Dec 23 15:36:30 2021
   read: IOPS=36.0k, BW=145MiB/s (152MB/s)(1024MiB/7085msec)
    slat (nsec): min=0, max=766383, avg=7182.49, stdev=21669.78
    clat (usec): min=311, max=264215, avg=1712.73, stdev=5782.78
     lat (usec): min=316, max=264219, avg=1722.41, stdev=5782.67
    clat percentiles (usec):
     |  1.00th=[   996],  5.00th=[  1012], 10.00th=[  1106], 20.00th=[  1123],
     | 30.00th=[  1123], 40.00th=[  1139], 50.00th=[  1172], 60.00th=[  1303],
     | 70.00th=[  1614], 80.00th=[  1729], 90.00th=[  1860], 95.00th=[  1942],
     | 99.00th=[  9634], 99.50th=[ 19006], 99.90th=[ 57410], 99.95th=[128451],
     | 99.99th=[263193]
   bw (  KiB/s): min=48936, max=216416, per=100.00%, avg=148128.57, stdev=47396.09, samples=14
   iops        : min=12234, max=54104, avg=37032.14, stdev=11849.02, samples=14
  lat (usec)   : 500=0.01%, 750=0.01%, 1000=3.29%
  lat (msec)   : 2=92.23%, 4=3.13%, 10=0.43%, 20=0.52%, 50=0.26%
  lat (msec)   : 100=0.06%, 250=0.05%, 500=0.02%
  cpu          : usr=19.73%, sys=24.89%, ctx=21432, majf=0, minf=76
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%, >=64=0.0%
     issued rwts: total=262144,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
   READ: bw=145MiB/s (152MB/s), 145MiB/s-145MiB/s (152MB/s-152MB/s), io=1024MiB (1074MB), run=7085-7085msec

Disk stats (read/write):
  sda: ios=20458/0, merge=235263/0, ticks=11379/0, in_queue=11379, util=98.67%
  
##
這個報告中需要重點關注的是slat、clat、lat以及bw和iops這幾行
事實上slat、clat、lat都是指I/O延遲(latency),不同之處在於
1. slat是指從I/O提交到實際執行I/O的時長(Submission latency)
2. clat是指從I/O提交到I/O完成的時長(Completion latency)
3. lat是指從fio建立I/O到I/O完成的總時長
##
4. bw是指吞吐量,在示例中平均吞吐量大約是145MB(148128KiB/1024)
5. iops是指每秒I/O的次數,上面示例中的平均IOPS為36000
##
對同步I/O來說,由於I/O提交和I/O完成是一個動作,所以slat實際上就是I/O完成的時間,而clat是0
使用非同步I/O(libaio)時,lat近似等於slat+clat之和


通常情況下應用程式的I/O都是讀寫並行的,而且每次的 I/O 大小也不一定相同
所以,剛剛這幾種場景,並不能精確模擬應用程式的I/O模式
那怎麼才能精確模擬應用程式的I/O模式呢?

fio支援I/O的重放
藉助blktrace,再配合上fio,就可以實現對應用程式I/O模式的基準測試
需要先用blktrace ,記錄磁碟裝置的I/O訪問情況
然後使用fio ,重放blktrace的記錄

# 使用blktrace跟蹤磁碟I/O,注意指定應用程式正在操作的磁碟
[root@local_sa_192-168-1-6 ~]# blktrace /dev/sda

# 檢視blktrace記錄的結果
[root@local_sa_192-168-1-6 ~]# ls
sda.blktrace.0 sda.blktrace.1

# 將結果轉化為二進位制檔案
[root@local_sa_192-168-1-6 ~]# blkparse sda -d sda.bin

# 使用fio重放日誌
[root@local_sa_192-168-1-6 ~]# fio --name=replay --filename=/dev/sda --direct=1 --read_iolog=sda.bin

##
這樣就通過blktrace+fio的組合使用,得到了應用程式I/O模式的基準測試報告




I/O效能優化

得到I/O基準測試報告後,再用上一節總結的效能分析套路,找出I/O的效能瓶頸並優化,就是水到渠成的事情了
當然想要優化I/O效能,肯定離不開Linux系統的I/O棧圖的思路輔助,可以結合下面的 I/O 棧圖再回顧一下



應用程式優化

應用程式處於整個I/O棧的最上端,它可以通過系統呼叫,來調整 I/O 模式(如順序還是隨機、同步還是非同步)
同時,它也是I/O資料的最終來源,可以有這麼幾種方式來優化應用程式的I/O效能

  1. 可以用追加寫代替隨機寫,減少定址開銷,加快I/O寫的速度
  2. 可以藉助快取I/O,充分利用系統快取,降低實際I/O的次數
  3. 可以在應用程式內部構建自己的快取,或者用Redis這類外部快取系統
    這樣一方面,能在應用程式內部,控制快取的資料和生命週期
    另一方面,也能降低其他應用程式使用快取對自身的影響
  4. 在需要頻繁讀寫同一塊磁碟空間時,可以用mmap代替read/write,減少記憶體的拷貝次數
  5. 在需要同步寫的場景中,儘量將寫請求合併,而不是讓每個請求都同步寫入磁碟
    即可以用fsync()取代O_SYNC
  6. 在多個應用程式共享相同磁碟時,為了保證I/O不被某個應用完全佔用
    推薦使用cgroups的I/O子系統,來限制程序/程序組的IOPS以及吞吐量
  7. 在使用CFQ排程器時,可以用ionice來調整程序的I/O排程優先順序,特別是提高核心應用的I/O優先順序
    ionice支援三個優先順序類:Idle、Best-effort和Realtime
    其中Best-effort和Realtime還分別支援 0-7 的級別,數值越小,則表示優先級別越高


檔案系統優化

應用程式訪問普通檔案時,實際是由檔案系統間接負責,檔案在磁碟中的讀寫
所以,跟檔案系統中相關的也有很多優化I/O效能的方式

  1. 可以根據實際負載場景的不同,選擇最適合的檔案系統
    比如Ubuntu預設使用ext4檔案系統,而CentOS 7預設使用xfs檔案系統
    相比於ext4,xfs支援更大的磁碟分割槽和更大的檔案數量,如xfs支援大於16TB的磁碟
    但是xfs檔案系統的缺點在於無法收縮,而ext4則可以
  2. 在選好檔案系統後,還可以進一步優化檔案系統的配置選項
    包括檔案系統的特性(如ext_attr、dir_index)
    日誌模式(如journal、ordered、writeback)
    掛載選項(如noatime)等等
    比如使用tune2fs這個工具,可以調整檔案系統的特性(tune2fs 也常用來檢視檔案系統超級塊的內容)
    而通過/etc/fstab或者mount命令列引數,我們可以調整檔案系統的日誌模式和掛載選項等
  3. 可以優化檔案系統的快取
    比如可以優化pdflush髒頁的重新整理頻率(比如設定dirty_expire_centisecs和dirty_writeback_centisecs)
    以及髒頁的限額(比如調整dirty_background_ratio和dirty_ratio等)
    再如還可以優化核心回收目錄項快取和索引節點快取的傾向
    即調整vfs_cache_pressure(/proc/sys/vm/vfs_cache_pressure預設值100)
    數值越大,就表示越容易回收
    最後在不需要持久化時,還可以用記憶體檔案系統 tmpfs,以獲得更好的I/O效能
    tmpfs把資料直接儲存在記憶體中,而不是磁碟中
    比如/dev/shm/,就是大多數Linux預設配置的一個記憶體檔案系統,它的大小預設為總記憶體的一半


磁碟優化

資料的持久化儲存,最終還是要落到具體的物理磁碟中,同時,磁碟也是整個I/O棧的最底層
從磁碟角度出發,自然也有很多有效的效能優化方法

  1. 最簡單有效的優化方法,就是換用效能更好的磁碟,比如用SSD替代HDD
  2. 使用RAID把多塊磁碟組合成一個邏輯磁碟,構成冗餘獨立磁碟陣列
    這樣做既可以提高資料的可靠性,又可以提升資料的訪問效能
  3. 針對磁碟和應用程式I/O模式的特徵,可以選擇最適合的I/O排程演算法
    比方說SSD和虛擬機器中的磁碟,通常用的是noop排程演算法
    而資料庫應用,推薦使用deadline演算法
  4. 可以對應用程式的資料,進行磁碟級別的隔離
    比如可以為日誌、資料庫等I/O壓力比較重的應用,配置單獨的磁碟
  5. 在順序讀比較多的場景中,可以增大磁碟的預讀資料
    比如可以通過下面兩種方法,調整/dev/sdb的預讀大小
    1. 調整核心選項/sys/block/sdb/queue/read_ahead_kb,預設大小是128KB,單位為KB
    2. 使用blockdev工具設定,比如blockdev --setra 8192 /dev/sdb,注意這裡的單位是512B(0.5KB),所以它的數值總是read_ahead_kb的兩倍
  6. 可以優化核心塊裝置I/O的選項
    比如可以調整磁碟佇列的長度/sys/block/sdb/queue/nr_requests
    適當增大佇列長度,可以提升磁碟的吞吐量(當然也會導致I/O延遲增大)
  7. 要注意磁碟本身出現硬體錯誤,也會導致I/O效能急劇下降,所以發現磁碟效能急劇下降時
    需要確認,磁碟本身是不是出現了硬體錯誤
    比如可以檢視dmesg中是否有硬體I/O故障的日誌
    還可以使用badblocks、smartctl 等工具,檢測磁碟的硬體問題
    或用e2fsck等來檢測檔案系統的錯誤
    如果發現問題可以使用fsck等工具來修復



小結

常見的檔案系統和磁碟I/O的效能優化思路和方法
發現I/O效能問題後,不要急於動手優化
而要先找出最重要的、可以最大程度提升效能的問題
然後再從I/O棧的不同層入手,考慮具體的優化方法

記住,磁碟和檔案系統的I/O,通常是整個系統中最慢的一個模組
所以在優化I/O問題時,除了可以優化I/O的執行流程
還可以藉助更快的記憶體、網路、CPU等,減少I/O呼叫

可以充分利用系統提供的Buffer、Cache
或是應用程式內部快取, 再或者Redis這類的外部快取系統


轉載請註明出處喲~ https://www.cnblogs.com/lichengguo