Linux優化之IO子系統監控與調優

心情依舊的部落格發表於2014-09-22

Linux優化之IO子系統

作為伺服器主機來講,最大的兩個IO型別 :

1.磁碟IO

2.網路IO

這是我們調整最多的兩個部分所在

磁碟IO是如何實現的

在記憶體調優中,一直在講到為了加速效能,linux核心一般情況下都會嘗試將磁碟上的慢速裝置上的檔案快取至記憶體中,從而達到加速效果;

虛擬記憶體的概念:

讀寫都在記憶體中完成,當某一程式在cpu執行的時候,程式要訪問自己地址空間中的某一記憶體頁,當程式需要訪問頁面中的資料,而這個頁面最終是要對應在實體記憶體中的某個物理頁面,而程式只能看到自己的線性地址空間,而這個地址並不存在,一旦訪問這個地址,那麼會通過MMU(記憶體管理單元)機制中的儲存當前程式的線性地址到實體地址的對映表

由此通過MMU實現對應的地址查詢於是得到了其對映的地址,最終程式雖然訪問的資料是來自於對映過的地址,這種訪問訪問我們被稱為虛擬地址或虛擬記憶體

如果由於我們使用交換記憶體或其他方式有可能這個程式所開啟的檔案長時間沒有被訪問,這個檔案所對應的記憶體已經被清出去了,所以使用mmu地址轉換後的地址對應的資料在記憶體中不存在了,這時候會產生頁錯誤,我們也被稱為缺頁異常

缺頁異常

缺頁異常分為大異常和小異常:

如果資料不存在使得不得不在磁碟中載入頁面檔案,這時CPU就會進入核心模式,訪問磁碟,每次CPU訪問記憶體就要3個週期,訪問磁碟需要N個週期,首先需要定位資料的準確位置,而後定位實體記憶體中開闢資料空間,最後將資料匯流排貫通,從而將資料從磁碟轉入到記憶體–blockin

當我們找一個空閒空間,而事實上當程式訪問這段資料就需要訪問新位置的資料,所以我們要更新這個對映表,明確說明所要訪問的邏輯地址所要對應的空間的轉換的位置,並且讓程式重新發起一次訪問,這時需要先查詢TLB(快取緩衝器),再次進行查表

而將磁碟裝入記憶體的過程就會發生IO,如果程式修改了資料,最終資料還需要寫到磁碟中去,而寫到磁碟中去,過程使得資料比原來的檔案更大了

具體是由檔案系統模組根據程式發起的請求,核心指揮檔案系統模組開闢更多的儲存塊而後將資料儲存,這種過程被稱為 block out

#緩衝器負責將之前緩衝過的快取下來,那麼如果N個條目,而緩衝器只能快取有限的幾個,那麼命中率可能會很低,如果我們使用大頁面的話,那麼命中率可以大大提高。

機械硬碟的特性

同一方向的操作是合併起來完成的,而後在這個方向結束之後則是另外一方向的

對硬碟來講,讀寫是不同型別的操作,讀寫是不能同時進行的

 

磁碟是如何操作的

將一個或多個程式的讀操作合併到一起讀

將一個或多個程式的寫操作合併到一起寫

所以讀寫操作是兩類不同的操作而且是同一方向合併的

如果是讀檔案,這個檔案一定是來自於磁碟的

如果是寫檔案,那麼寫入到記憶體中,對於程式來講是已經完成的,那麼使用者對計算機效能感知是來自於讀,因為讀一定是與IO相互動

1.讀是在同方向合併的

2.寫也是需要合併的,而且兩者是不同方向的操作

因為在同一方向可以節省很多資源

讀必須優先滿足,而寫也不能等太久,因此必須有一種良好的演算法讓其儘可能都得到滿足,而又不能讓使用者感到效能下降

因此在IO系統上有個非常重要的模組—IO排程器

IO排程器

用來實現合併同一方向的讀寫操作並且將讀寫操作儘可能理想的這種狀況,IO排程器本身的完成,終端使用者實現寫的時候程式級別所看到的資料是檔案介面,那麼檔案介面輸出的時候就意味著將磁碟空間以檔案介面的方式輸出,其需要檔案系統,也就意味著程式與磁碟上的資料打交道是依賴檔案系統的,所以使用者的請求先到檔案系統,而檔案系統通過核心輸出是虛擬檔案介面(VFS) 通過VFS找到各特定檔案系統相關模組,當然對應的檔案是哪個那麼則通過vfs轉換成什麼即可,檔案系統將資料接下來之後,最終儲存為磁碟塊的方式儲存在磁碟上,因此這些檔案系統最終還要轉換資料為磁碟塊,所以接下來還要有塊層

塊層主要是將資料轉換為磁碟塊格式,而後再由磁碟塊格式轉換成排程以後儲存在磁碟上

如下圖所示:

wKiom1QdhnPy6EQQAAH_P6Mmz0A257.jpg

  • (1)使用者程式實現寫操作 實現系統呼叫
  • (2)使用者的寫操作一定是跟VFS進行互動的
  • (3)VFS需要將其換換為特定的檔案系統
  • (4)單個檔案在虛擬檔案系統存放都會轉換成頁面方式(page cache)
  • (5)寫完之後通過block buffer快緩衝(知所以進行緩衝是因為磁碟太慢了,所以寫的時候需要緩衝下來)
  • (6)然後由bio將每個page cache轉換成塊,並且在塊緩衝這個層次上快取下來

這就是緩衝佇列,而在塊層實現緩衝之後每個塊最終都要交給塊層來處理,塊層中最重要的一個元件就是IO排程器,IO排程器接收blockbuffer中所傳送過來的多個請求塊,這多個請求塊需要排序的:同方向合併,圖中都是寫操作的

至於如何排序,一定是最靠近寫請求的最優先滿足

而IO排程器主要功能就是將隨機IO儘可能合併為順序IO 本文來自http://yijiu.blog.51cto.com 轉載請說明,翻版可恥

但是我們有說過,儘可能同一方向合併儘可能會隨機變為順序,但是我們又不得不讀飢餓也不能寫飢餓,所以要交替進行的

所以:

(10)由IO排程器排程完成之後,提交給Device Driver ,由Device Driver控制磁碟控制器,由控制器將電器訊號轉換為磁訊號寫入到磁碟中去

為何隨機讀寫比順序讀寫要慢:

·隨機讀寫:

我們可能寫任意一個磁軌的任意一個扇區,那麼硬碟磁頭可能來回晃動才能完成一次寫

·順序讀寫:

在一個方向轉動即可完成,不用再去移動磁臂的

磁頭操作是電磁運動,而磁臂操作是機械運動,所以任何時候隨機讀寫效能都比順序讀寫都要差的很多

排程演算法

IO排程器事實上是用程式完成的排程演算法,對linux來講,2.6的核心一共有4個

1、CFQ

完全公平佇列,比較適合於互動式場景

2、Deadline

最後期限,任何一個讀寫請求,都有自己的滿足期限,當期限到來時之前,必須達到需求的滿足(一般建議在資料庫伺服器上使用此排程演算法)

3、anticpatory

預期的,任何一個資料讀完之後,有可能與其相鄰的資料也可能被讀到,所以它大致所實現的方法就是,讀完之後先不滿足,則不處理,需等一段時間後檢視是否有相近資料訪問過,如果有馬上先滿足,所以這隻能在行為預估的場景下可用

4、Noop

不排隊不合並,先到先得

#像固態硬碟,因為它不是機械硬碟,它的讀寫就算是隨機IO那麼它的效能跟順序IO差別也不是很大,反而如果想讓排程器去調取它的演算法,那麼排程器本身執行會佔用很高的CPU的時鐘週期,有可能會得不償失,所以noop在這種場景下是最好的演算法

#有些RAID裝置控制器在硬體裝置上自己就有讀寫操作排序的,也就意味著在硬體級別排好序之後在作業系統級別會將其打散重新排序,得不償失,所以RAID裝置有自己的排程器的話,最好也使用noop

一般來講,預設是CFQ的

本文來自http://yijiu.blog.51cto.com 轉載請說明,翻版可恥

有時候在不同場景下,他們所最佳所適用的演算法可能不一樣,比如:

如果是web伺服器,這裡只是訪問放web上的分割槽的頁面資料

如果是db資料庫,訪問的是db資料庫的檔案,他們最適用的演算法未必會一樣,因為他們的訪問風格不同,所以這時候我們就需要修改他們的排程器演算法

CFQ比較適合於互動式場景,於是在很多時候會將伺服器設定為Deadline,當然只是一種假定,具體需要自己測試然後做決定

觀測當前磁碟IO活動

一般 ethstatus iotio pt-ioprofile sar等查工具看哪些程式引起的io比較高等

這裡我們使用sar來觀察其狀態資訊 本文 來自http://yijiu.blog.51cto.com 轉載 請說明,翻版 可恥

例:

[root@node3 ~]# sar -d 1 5
Linux2.6.32-431.20.3.el6.x86_64 (node3.test.com) 09/20/2014 _x86_64_(4 CPU)
09:16:00 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
09:16:01 PM dev252-0 46.46 0.00 46795.96 1007.13 2.65 580.00 2.26 10.51
09:16:01 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
09:16:02 PM dev252-0 3.00 0.00 144.00 48.00 0.00 1.33 1.00 0.30
09:16:02 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
09:16:03 PM dev252-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
09:16:03 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
09:16:04 PM dev252-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
09:16:04 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
09:16:05 PM dev252-0 72.73 0.00 59967.68 824.56 2.61 35.88 1.21 8.79

用引數-p可以列印出sda,hdc等磁碟裝置名稱,如果不用引數-p,裝置節點則有可能是dev8-0,dev22-0

引數解釋:

tps:每秒從物理磁碟I/O的次數.多個邏輯請求會被合併為一個I/O磁碟請求,一次傳輸的大小是不確定的

rd_sec/s:每秒讀扇區的次數.

avgrq-sz:平均每次裝置I/O操作的資料大小(扇區).

avgqu-sz:磁碟請求佇列的平均長度.

await:從請求磁碟操作到系統完成處理,每次請求的平均消耗時間,包括請求佇列等待時間,單位是毫秒(1秒=1000毫秒).(一次完成的任務,它的IO完成的平均耗時)

svctm:系統處理每次請求的平均時間,不包括在請求佇列中消耗的時間,

%util:I/O請求佔CPU的百分比,比率越大,說明越飽

我們通常經驗值是:

svctm不超過0.5;

await不超過5;

主要看當前裝置

核心要點:

1、tps(iops)越高,但%util越低,說明io能力容量越大

2、await、svctm越低越好,說明io響應延遲很低,iops能力很高

調整buffer,提高效能

無非就是調整佇列數,以及增加預讀數,下面我們來手動做一下

·增加佇列長度

 

格式:

/sys/block/vda(特定某裝置)/queue/nr_requests

由於我這裡跑的是kvm虛機,所以裝置號預設都以vdx開頭

預設佇列為128個長度

[root@node3 ~]# cat /sys/block/vda/queue/nr_requests

128

這個值是可以調大一點的

2.增加預讀數

/sys/block/vda(特定某裝置)/queue/read_ahead_kb

表示事先預讀資料的kb數,預設也是128

[root@node3 ~]# cat /sys/block/vda/queue/read_ahead_kb

128

這個值也是可以調大的,具體多少自行而定

本文 來自http://yijiu.blog.51cto.com 轉載 請說明,翻版 可恥

CFQ完全公平佇列

IO排程是在各程式之間平均分配的,主要是根據程式的IO需求來講IO能力平均分配排程

所以在互動式環境中,這種方式是比較實用的

但是在RHEL6.4上 它又提供了三個不同的排程等級:

1.實時 RT

2.最佳效果 BE

3.閒置

我們可以使用ionice命令手動分配排程等級,或者使用iopro_set系統呼叫程式設計分配,當然涉及到開發層面了

在實時排程等級和最佳效果兩個級別都有8個IO等級,

數字越小優先順序越高,最佳效果是預設排程等級 也就是4,不用更改

修改CFQ,以調節其效能

涉及引數檔案:/sys/block/vda/queue/iosched/

修改預設排程器演算法:

[root@node3 ~]# cd /sys/block/vda/queue/
[root@node3 queue]# ls
add_random hw_sector_size max_hw_sectors_kb minimum_io_size physical_block_size scheduler
discard_granularity iosched max_sectors_kb nomerges read_ahead_kb unpriv_sgio
discard_max_bytes iostats max_segments nr_requests rotational
discard_zeroes_data logical_block_size max_segment_size optimal_io_size rq_affinity

而在其上層目錄裡,有一scheduler檔案

檢視scheduler檔案

[root@node3 queue]# cat scheduler
noop anticipatory deadline [cfq]

因此更改磁碟IO排程器則去找這個目錄下所對應的scheduler,注意的是,它只是針對每個磁碟進行調整的,如果有多塊磁碟的話則需要對應每個磁碟進行修改

它沒有辦法使用sysctl進行控制,如果想開機生效,只能寫到rc.local 或init指令碼中

一旦更改排程演算法之後,再來檢視目錄中的檔案

[root@node3 queue]# ls /sys/block/vda/queue/iosched/
back_seek_max fifo_expire_async group_idle low_latency slice_async slice_idle
back_seek_penalty fifo_expire_sync group_isolation quantum slice_async_rq slice_sync

修改演算法

[root@node3 queue]# echo deadline > scheduler
[root@node3 queue]# cat scheduler
noop anticipatory [deadline] cfq

再次觀察iosched目錄,並檢視其是否有變化

[root@node3 queue]# ls/sys/block/vda/queue/iosched/
fifo_batch front_merges read_expire write_expire writes_starved

所以我們更改排程演算法後,每個排程演算法在此目錄都有很多可調整引數,每個引數都有值,只不過都表現為其檔案內容而已,而每個排程器的值通過修改是可以優化排程器的工作特性的

比如對CFQ來講,有以下幾個值可以調整:

back_seek_max

反向尋道可能有負面影響,負載小的時候可以啟用,否則不要使用反向尋道太多值

back_seek_penal

反向尋道做懲罰,如果不得不使用反向尋道的話,那麼必須對其做出一定懲罰,一旦做完懲罰之後,必須要正向尋道更多次數

fifo_expire_async

用來控制非同步請求等待時間長度,預設是250毫秒,過期之後無法滿足的非同步請求將會被移動到排程佇列中,也就意味著要重新排程。通常這些值不需要調整

fifo_expire_sync

用於同步請求的,

嚴格來講寫操作都是在記憶體中完成 過週期之後才會同步至硬碟中,站在計算機角度來說這種操作都被稱為非同步,而同步則是為了儘可能保證資料會被第一時間寫到磁碟上來,資料不會在記憶體上逗留,直接寫入磁碟

low_latecy

低延遲,簡單來講,每個程式都有可能發起讀寫請求,也就意味著最終滿足使用者讀寫請求是按程式為單位劃分,在滿足這個前提下,需要考慮每個程式都需要得到滿足,所以必須關注每個程式發起IO請求之後最多等待多長時間,如果啟動此值就意味著每個程式只要發起讀寫請求都要儘可能快速得到滿足,預設就啟用了低延遲

在桌面系統環境,低延遲是非常有必要的

quantum

CFQ一次可以發出的IO請求數,一批最大可以排程的IO數,限制IO佇列深度的,簡單來說就是定義裝置一次可以接收的IO請求的佇列長度,預設為8

增加反而會有負面影響,因此謹慎調整

如果隨機IO請求數非常的多,這個值可以適當調大,如果順序寫非常多,那麼不建議調整

 

設定IO允許消耗的時間

一次IO請求的操作,一次執行多久,應該執行多久,按理說硬碟只要是沒有損壞,能正常運作,在正常範圍內,那麼它就應該寫完、讀完所以我們要定義好每次讀寫請求所最大允許消耗的時間,那麼就是以下幾個引數的意義了:

slice_async

定義非同步寫入的超時值,每次非同步寫操作最長時長是多少,預設值為40秒

slice_idle

等待IO請求的閒置時長

slice_sync

定義同步寫入操作超時值,因為同步比較慢,所以其預設值是100秒,因為是從程式直接到磁碟的,所以超時時間會長一點

在桌面環境和在服務環境下,他們如果都使用CFQ排程器,他們工作特性不一定,也就意味著我們關注其背後工作機制引數也不一樣,所以要調整某些值做一些測試的評判

Deadline最後期限排程

最後期限

wKiom1Qdhp2A5fkHAACT0L3TiWU207.jpg

如圖所示,其分為了3個佇列,分別是:

  • ·讀佇列
  • ·寫對列
  • ·排序佇列

而後這些佇列都被整合到派發佇列中去而後由磁碟得到滿足,我們從中任何一佇列中選出一個操作得到滿足之前必須要保證這類操作不能超期

簡單來講deadline就是將每個讀寫操作放到佇列的時候都給他一個倒數計時的計時器,將倒數計時的計時器消耗完之前需要趕緊放到派發佇列中,而後再同步至硬碟

而對伺服器來講,這種方式是比較理想的

常用可調引數

fifo_batch

單批發出的讀寫數,在其最後期限滿足之前將佇列中的資料拿出並滿足,但有寫操作是需要排序的;預設為16,設定更多的值會獲得更好的流量,但是會增加延遲

比如一批讀為16個,那麼我們講其改為32個,那意味著寫的時間會更高

當然所有都取決於測試資料,無論怎麼調都不如換一塊SSD硬碟

front_merges

可以將多個請求合併在一起,但是有些請求壓根不連續,不可能被合併在一起,那麼我們可以禁止在滿足IO之前進行合併的,禁止合併有可能會帶來隨機讀寫的特性的

但允許合併也有一定的副作用,就是必須花時間去排序

read_expire

每個讀操作必須在多少期限內得到滿足,預設為半秒鐘

write_expire

每個寫操作必須在多久內得到滿足,預設為5秒鐘

#寫操作是可以延遲滿足的

writes_starved

定義一批可以處理多少個讀取批,這個值越高,讀的效果就越好。預設為2,意味著2批讀,一批寫。如果伺服器讀多寫少,記憶體緩衝足夠大,那麼可以將其調大

Noop

如果系統與cpu繫結,且使用高速儲存(SSD),這就是最佳的IO排程程式

只要使用固態硬碟就要將其改為noop

如果調引數的話則需要直接去挑戰/sys/block/vda/queue下的引數,而不是排程器的引數

add_random

其作用是否使用商池

max_sectors_kb

預設傳送到磁碟的最大請求是多少,預設為512kb。我們知道每個扇區是512位元組,那麼每次傳送512kb 意味著傳送N個扇區,我們可以調整或增大減小該值,對於固態硬碟來講不是所謂的扇區概念,由此可調。

在此類情況下建議將max_hw_sectors_kb降低刪除塊大小

我們可以使用壓力測試工具對其做測試,記錄大小從512kb到1MB不等,哪個值的效果好就設定為哪個值,當然壓力測試跟實際場景有出入的,所以要充分考慮環境儘可能模擬真實場景,儘可能要模擬隨機讀寫

nr_request

請求的佇列的值,可以降低其值

rotational

如果是SSD硬碟要將此值設定為0,禁用輪轉模式

rq_affinity

在觸發IO不同的CPU中處理IO,一般來講在同CPU上處理同IO是最好的

因為CPU處理的僅僅是中斷而已

因此對於SSD環境中常用的調整引數有:

max_sectors_kb

nr_requests

optimal_io_size

rotational

調整後如何測試效能是否提高

比較常用的硬碟壓力測試工具

·aio-stress

·iozone

·fio

瞭解磁碟IO活動狀況分析工具

blktrace

磁碟IO瓶頸分析工具

blkparse

gnuplot

本文 來自http://yijiu.blog.51cto.com 轉載 請說明,翻版 可恥

總結:IO優化大致思路

  • ·最好換SSD
  • ·調整raid級別
  • ·選擇IO排程器
  • ·根據場景選擇合適的檔案系統
  • ·配置選定排程器的引數
  • ·優化結果是否理想,則使用工具進行分析
  • ·寫在開機啟動項裡

相關文章