Linux 內核包含4個IO調度器:
Noop IO scheduler
Anticipatory IO scheduler
Deadline IO scheduler
CFQ IO scheduler。
anticipatory, 預期的;提早發生的;期待著的
通常磁盤的讀寫影響是由磁頭到柱面移動造成了延遲,解決這種延遲內核主要采用兩種策略:緩存和IO調度算法來進行彌補。
本文做一簡單介紹。
調度算法概念
當向設備寫入數據塊或是從設備讀出數據塊時,請求都被安置在一個隊列中等待完成。
每個塊設備都有它自己的隊列。
I/O調度程序負責維護這些隊列的順序,以更有效地利用介質.I/O調度程序將無序的I/O操作變為有序的I/O操作。
內核必須首先確定隊列中一共有多少個請求,然后才開始進行調度。
IO 調度器(IO Scheduler)
IO調度器(IO Scheduler)是操作系統用來決定塊設備上IO操作提交順序的方法。存在的目的有兩個,一是提高IO吞吐量,二是降低IO響應時間。然而IO吞吐量和IO響應時間往往是矛盾的,為了盡量平衡這兩者,IO調度器提供了多種調度算法來適應不同的IO請求場景。其中,對數據庫這種隨機讀寫的場景最有利的算法是DEANLINE。
IO調度器在內核棧中所處位置如下:
塊設備最悲劇的地方就是磁盤轉動,這個過程會很耗時間。
每個塊設備或者塊設備的分區,都對應有自身的請求隊列(request_queue),而每個請求隊列都可以選擇一個I/O調度器來協調所遞交的request。I/O調度器的基本目的是將請求按照它們對應在塊設備上的扇區號進行排列,以減少磁頭的移動,提高效率。每個設備的請求隊列里的請求將按順序被響應。實際上,除了這個隊列,每個調度器自身都維護有不同數量的隊列,用來對遞交上來的request進行處理,而排在隊列最前面的request將適時被移動到請求隊列中等待響應。
IO scheduler 的作用主要是為了減少磁盤轉動的需求。主要通過2種方式實現: ? ? ? 1. 合并
? ? ? 2. 排序
每個設備都會自己對應請求隊列,所有的請求在被處理之前都會在請求隊列上。在新來一個請求的時候如果發現這個請求和前面的某個請求請求的位置是相鄰的,那么就可以合并為一個請求。如果不能找到合并的,就會按照磁盤的轉動方向進行排序。通常IO scheduler 的作用就是為了在進行合并和排序的同時,也不會太影響單個請求的處理時間。
1、NOOP
FIFO
1. noop是什么? noop是一種輸入輸出調度算法。NOOP, No Operation. 什么都不做,請求來一個處理一個。這種方式實施起來簡單,也更有效。問題就是disk seek 太多,對于傳統磁盤,這是不能接受的。但對于SSD 磁盤就可以,因為SSD 磁盤不需要轉動。
2. noop的別稱 又稱為電梯調度算法。
3. noop原理是怎樣的?
將輸入輸出請求放到一個FIFO隊列中,然后按次序執行隊列中的輸入輸出請求:當來一個新請求時: 1. 如果能合并就合并; 2. 如果不能合并,就會嘗試排序。如果隊列上的請求都已經很老了,這個新的請求就不能插隊,只能放到最后面。否則就插到合適的位置; 3. 如果既不能合并,又沒有合適的位置插入,就放到請求隊列的最后; 4. 適用場景
4.1 在不希望修改輸入輸出請求先后順序的場景下;
4.2 在輸入輸出之下具有更加智能調度算法的設備,如NAS存儲設備;
4.3 上層應用程序已經精心優化過的輸入輸出請求;
4.4 非旋轉磁頭式的磁盤設備,如SSD磁盤
2、CFQ(Completely Fair Queuing, 完全公平排隊)
CFQ(Completely Fair Queuing)算法,顧名思義,絕對公平算法。它試圖為競爭塊設備使用權的所有進程分配一個請求隊列和一個時間片,在調度器分配給進程的時間片內,進程可以將其讀寫請求發送給底層塊設備,當進程的時間片消耗完,進程的請求隊列將被掛起,等待調度。
每個進程的時間片和每個進程的隊列長度取決于進程的IO優先級,每個進程都會有一個IO優先級,CFQ調度器將會將其作為考慮的因素之一,來確定該進程的請求隊列何時可以獲取塊設備的使用權。
IO優先級從高到低可以分為三大類: RT(real time) BE(best try)
IDLE(idle)
其中RT和BE又可以再劃分為8個子優先級。可以通過ionice 去查看和修改。優先級越高,被處理得越早,用于這個進程的時間片也越多,一次處理的請求數也會越多。
實際上,我們已經知道CFQ調度器的公平是針對于進程而言的,而只有同步請求(read或syn write)才是針對進程而存在的,他們會放入進程自身的請求隊列,而所有同優先級的異步請求,無論來自于哪個進程,都會被放入公共的隊列,異步請求的隊列總共有8(RT)+8(BE)+1(IDLE)=17個。
從Linux 2.6.18起,CFQ作為默認的IO調度算法。對于通用的服務器來說,CFQ是較好的選擇。具體使用哪種調度算法還是要根據具體的業務場景去做足benchmark來選擇,不能僅靠別人的文字來決定。
3、DEADLINE
DEADLINE在CFQ的基礎上,解決了IO請求餓死的極端情況。
除了CFQ本身具有的IO排序隊列之外,DEADLINE額外分別為讀IO和寫IO提供了FIFO隊列。
讀FIFO隊列的最大等待時間為500ms,寫FIFO隊列的最大等待時間為5s(當然這些參數都是可以手動設置的)。
FIFO隊列內的IO請求優先級要比CFQ隊列中的高,而讀FIFO隊列的優先級又比寫FIFO隊列的優先級高。優先級可以表示如下:
FIFO(Read) > FIFO(Write) > CFQ
deadline 算法保證對于既定的 IO 請求以最小的延遲時間,從這一點理解,對于 DSS 應用應該會是很適合的。
deadline 實際上是對Elevator 的一種改進:?
1. 避免有些請求太長時間不能被處理。
2. 區分對待讀操作和寫操作。
deadline IO 維護3個隊列。第一個隊列和Elevator 一樣, 盡量按照物理位置排序。第二個隊列和第三個隊列都是按照時間排序,不同的是一個是讀操作一個是寫操作。
deadline IO 之所以區分讀和寫是因為設計者認為如果應用程序發了一個讀請求,一般就會阻塞到那里,一直等到的結果返回。而寫請求則不是通常是應用請求寫到內存即可,由后臺進程再寫回磁盤。應用程序一般不等寫完成就繼續往下走。所以讀請求應該比寫請求有更高的優先級。
在這種設計下,每個新增請求都會先放到第一個隊列,算法和Elevator的方式一樣,同時也會增加到讀或者寫隊列的尾端。這樣首先處理一些第一隊列的請求,同時檢測第二/三隊列前幾個請求是否等了太長時間,如果已經超過一個閥值,就會去處理一下。這個閥值對于讀請求時 5ms, 對于寫請求時5s。
個人認為對于記錄數據庫變更日志的分區,例如oracle 的online log, mysql 的binlog 等等,最好不要使用這種分區。因為這類寫請求通常是調用fsync 的。如果寫完不成,也會很影響應用性能的。
4、ANTICIPATORY
CFQ和DEADLINE考慮的焦點在于滿足零散IO請求上。對于連續的IO請求,比如順序讀,并沒有做優化。
為了滿足隨機IO和順序IO混合的場景,Linux還支持ANTICIPATORY調度算法。ANTICIPATORY的在DEADLINE的基礎上,為每個讀IO都設置了6ms的等待時間窗口。如果在這6ms內OS收到了相鄰位置的讀IO請求,就可以立即滿足。
小結
IO調度器算法的選擇,既取決于硬件特征,也取決于應用場景。
在傳統的SAS盤上,CFQ、DEADLINE、ANTICIPATORY都是不錯的選擇;對于專屬的數據庫服務器,DEADLINE的吞吐量和響應時間都表現良好。
然而在新興的固態硬盤比如SSD、Fusion IO上,最簡單的NOOP反而可能是最好的算法,因為其他三個算法的優化是基于縮短尋道時間的,而固態硬盤沒有所謂的尋道時間且IO響應時間非常短。
編輯:黃飛
?
評論
查看更多