一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

聊聊磁盤I/O那些事

 mickeychow805 2017-06-14
背景

計(jì)算機(jī)硬件性能在過去十年間的發(fā)展普遍遵循摩爾定律,通用計(jì)算機(jī)的 CPU 主頻早已超過 3GHz,內(nèi)存也進(jìn)入了普及 DDR4 的時(shí)代。然而傳統(tǒng)硬盤雖然在存儲(chǔ)容量上增長迅速,但是在讀寫性能上并無明顯提升,同時(shí) SSD 硬盤價(jià)格高昂,不能在短時(shí)間內(nèi)完全替代傳統(tǒng)硬盤。傳統(tǒng)磁盤的 I/O 讀寫速度成為了計(jì)算機(jī)系統(tǒng)性能提高的瓶頸,制約了計(jì)算機(jī)整體性能的發(fā)展。

硬盤性能的制約因素是什么?如何根據(jù)磁盤 I/O 特性來進(jìn)行系統(tǒng)設(shè)計(jì)?

針對這些問題,本文將介紹硬盤的物理結(jié)構(gòu)和性能指標(biāo),以及操作系統(tǒng)針對磁盤性能所做的優(yōu)化,最后討論下基于磁盤 I/O 特性設(shè)計(jì)的技巧。

硬盤的物理結(jié)構(gòu)

硬盤內(nèi)部主要部件為磁盤盤片、傳動(dòng)手臂、讀寫磁頭和主軸馬達(dá)。實(shí)際數(shù)據(jù)都是寫在盤片上,讀寫主要是通過傳動(dòng)手臂上的讀寫磁頭來完成。實(shí)際運(yùn)行時(shí),主軸讓磁盤盤片轉(zhuǎn)動(dòng),然后傳動(dòng)手臂可伸展讓讀取頭在盤片上進(jìn)行讀寫操作。磁盤物理結(jié)構(gòu)如下圖所示:

由于單一盤片容量有限,一般硬盤都有兩張以上的盤片,每個(gè)盤片有兩面,都可記錄信息,所以一張盤片對應(yīng)著兩個(gè)磁頭。盤片被分為許多扇形的區(qū)域,每個(gè)區(qū)域叫一個(gè)扇區(qū),硬盤中每個(gè)扇區(qū)的大小固定為 512 字節(jié)。盤片表面上以盤片中心為圓心,不同半徑的同心圓稱為磁道,不同盤片相同半徑的磁道所組成的圓柱稱為柱面。磁道與柱面都是表示不同半徑的圓,在許多場合,磁道和柱面可以互換使用。磁盤盤片垂直視角如下圖所示:

早期的硬盤每磁道扇區(qū)數(shù)相同,此時(shí)由磁盤基本參數(shù)可以計(jì)算出硬盤的容量:存儲(chǔ)容量=磁頭數(shù)*磁道(柱面)數(shù)*每道扇區(qū)數(shù)*每扇區(qū)字節(jié)數(shù)。

由于每磁道扇區(qū)數(shù)相同,外圈磁道半徑大,里圈磁道半徑小,外圈和里圈扇區(qū)面積自然會(huì)不一樣。同時(shí),為了更好的讀取數(shù)據(jù),即使外圈扇區(qū)面積再大也只能和內(nèi)圈扇區(qū)一樣存放相同的字節(jié)數(shù)(512 字節(jié))。這樣一來,外圈的記錄密度就要比內(nèi)圈小,會(huì)浪費(fèi)大量的存儲(chǔ)空間。

如今的硬盤都使用 ZBR(Zoned Bit Recording,區(qū)位記錄)技術(shù),盤片表面由里向外劃分為數(shù)個(gè)區(qū)域,不同區(qū)域的磁道扇區(qū)數(shù)目不同,同一區(qū)域內(nèi)各磁道扇區(qū)數(shù)相同,盤片外圈區(qū)域磁道長扇區(qū)數(shù)目較多,內(nèi)圈區(qū)域磁道短扇區(qū)數(shù)目較少,大體實(shí)現(xiàn)了等密度,從而獲得了更多的存儲(chǔ)空間。此時(shí),由于每磁道扇區(qū)數(shù)各不相同,所以傳統(tǒng)的容量計(jì)算公式就不再適用。實(shí)際上如今的硬盤大多使用 LBA(Logical Block Addressing)邏輯塊尋址模式,知道 LBA 后即可計(jì)算出硬盤容量。

影響硬盤性能的因素

影響磁盤的關(guān)鍵因素是磁盤服務(wù)時(shí)間,即磁盤完成一個(gè) I/O 請求所花費(fèi)的時(shí)間,它由尋道時(shí)間、旋轉(zhuǎn)延遲和數(shù)據(jù)傳輸時(shí)間三部分構(gòu)成。

1. 尋道時(shí)間

Tseek 是指將讀寫磁頭移動(dòng)至正確的磁道上所需要的時(shí)間。尋道時(shí)間越短,I/O 操作越快,目前磁盤的平均尋道時(shí)間一般在 3-15ms。

2. 旋轉(zhuǎn)延遲

Trotation 是指盤片旋轉(zhuǎn)將請求數(shù)據(jù)所在的扇區(qū)移動(dòng)到讀寫磁盤下方所需要的時(shí)間。旋轉(zhuǎn)延遲取決于磁盤轉(zhuǎn)速,通常用磁盤旋轉(zhuǎn)一周所需時(shí)間的 1/2 表示。比如:7200rpm 的磁盤平均旋轉(zhuǎn)延遲大約為 60*1000/7200/2 = 4.17ms,而轉(zhuǎn)速為 15000rpm 的磁盤其平均旋轉(zhuǎn)延遲為 2ms。

3. 數(shù)據(jù)傳輸時(shí)間

Ttransfer 是指完成傳輸所請求的數(shù)據(jù)所需要的時(shí)間,它取決于數(shù)據(jù)傳輸率,其值等于數(shù)據(jù)大小除以數(shù)據(jù)傳輸率。目前 IDE/ATA 能達(dá)到 133MB/s,SATA II 可達(dá)到 300MB/s 的接口數(shù)據(jù)傳輸率,數(shù)據(jù)傳輸時(shí)間通常遠(yuǎn)小于前兩部分消耗時(shí)間。簡單計(jì)算時(shí)可忽略。

衡量性能的指標(biāo)

機(jī)械硬盤的連續(xù)讀寫性能很好,但隨機(jī)讀寫性能很差,這主要是因?yàn)榇蓬^移動(dòng)到正確的磁道上需要時(shí)間,隨機(jī)讀寫時(shí),磁頭需要不停的移動(dòng),時(shí)間都浪費(fèi)在了磁頭尋址上,所以性能不高。衡量磁盤的重要主要指標(biāo)是 IOPS 和吞吐量。

1. IOPS

IOPS(Input/Output Per Second)即每秒的輸入輸出量(或讀寫次數(shù)),即指每秒內(nèi)系統(tǒng)能處理的 I/O 請求數(shù)量。隨機(jī)讀寫頻繁的應(yīng)用,如小文件存儲(chǔ)等,關(guān)注隨機(jī)讀寫性能,IOPS 是關(guān)鍵衡量指標(biāo)??梢酝扑愠龃疟P的 IOPS = 1000ms / (Tseek + Trotation + Transfer),如果忽略數(shù)據(jù)傳輸時(shí)間,理論上可以計(jì)算出隨機(jī)讀寫最大的 IOPS。常見磁盤的隨機(jī)讀寫最大 IOPS 為:

  • 7200rpm 的磁盤 IOPS = 76 IOPS

  • 10000rpm 的磁盤 IOPS = 111 IOPS

  • 15000rpm 的磁盤 IOPS = 166 IOPS

2. 吞吐量

吞吐量(Throughput),指單位時(shí)間內(nèi)可以成功傳輸?shù)臄?shù)據(jù)數(shù)量。順序讀寫頻繁的應(yīng)用,如視頻點(diǎn)播,關(guān)注連續(xù)讀寫性能、數(shù)據(jù)吞吐量是關(guān)鍵衡量指標(biāo)。它主要取決于磁盤陣列的架構(gòu),通道的大小以及磁盤的個(gè)數(shù)。不同的磁盤陣列存在不同的架構(gòu),但他們都有自己的內(nèi)部帶寬,一般情況下,內(nèi)部帶寬都設(shè)計(jì)足夠充足,不會(huì)存在瓶頸。磁盤陣列與服務(wù)器之間的數(shù)據(jù)通道對吞吐量影響很大,比如一個(gè) 2Gbps 的光纖通道,其所能支撐的最大流量僅為 250MB/s。最后,當(dāng)前面的瓶頸都不再存在時(shí),硬盤越多的情況下吞吐量越大。

操作系統(tǒng)層的優(yōu)化

雖然 15000rpm 的磁盤計(jì)算出的理論最大 IOPS 僅為 166,但在實(shí)際運(yùn)行環(huán)境中,實(shí)際磁盤的 IOPS 往往能夠突破 200 甚至更高。這其實(shí)就是在系統(tǒng)調(diào)用過程中,操作系統(tǒng)進(jìn)行了一系列的優(yōu)化。

那么操作系統(tǒng)是如何操作硬盤的呢?類似于網(wǎng)絡(luò)的分層結(jié)構(gòu),下圖顯示了 Linux 系統(tǒng)中對于磁盤的一次讀請求在核心空間中所要經(jīng)歷的層次模型。從圖中看出:對于磁盤的一次讀請求,首先經(jīng)過虛擬文件系統(tǒng)層(VFS Layer),其次是具體的文件系統(tǒng)層(例如 Ext2),接下來是 Cache 層(Page Cache Layer)、通用塊層(Generic Block Layer)、I/O 調(diào)度層(I/O Scheduler Layer)、塊設(shè)備驅(qū)動(dòng)層(Block Device Driver Layer),最后是物理塊設(shè)備層(Block Device Layer)。

虛擬文件系統(tǒng)層(VFS Layer

VFS(Virtual File System)虛擬文件系統(tǒng)是一種軟件機(jī)制,更確切的說扮演著文件系統(tǒng)管理者的角色,與它相關(guān)的數(shù)據(jù)結(jié)構(gòu)只存在于物理內(nèi)存當(dāng)中。它的作用是:屏蔽下層具體文件系統(tǒng)操作的差異,為上層的操作提供一個(gè)統(tǒng)一的接口。正是因?yàn)橛辛诉@個(gè)層次,Linux 中允許眾多不同的文件系統(tǒng)共存并且對文件的操作可以跨文件系統(tǒng)而執(zhí)行。

VFS 中包含著向物理文件系統(tǒng)轉(zhuǎn)換的一系列數(shù)據(jù)結(jié)構(gòu),如 VFS 超級塊、VFS 的 Inode、各種操作函數(shù)的轉(zhuǎn)換入口等。Linux 中 VFS 依靠四個(gè)主要的數(shù)據(jù)結(jié)構(gòu)來描述其結(jié)構(gòu)信息,分別為超級塊、索引結(jié)點(diǎn)、目錄項(xiàng)和文件對象。

1. 超級塊(Super Block):

超級塊對象表示一個(gè)文件系統(tǒng)。它存儲(chǔ)一個(gè)已安裝的文件系統(tǒng)的控制信息,包括文件系統(tǒng)名稱(比如 Ext2)、文件系統(tǒng)的大小和狀態(tài)、塊設(shè)備的引用和元數(shù)據(jù)信息(比如空閑列表等等)。VFS 超級塊存在于內(nèi)存中,它在文件系統(tǒng)安裝時(shí)建立,并且在文件系統(tǒng)卸載時(shí)自動(dòng)刪除。同時(shí)需要注意的是對于每個(gè)具體的文件系統(tǒng)來說,也有各自的超級塊,它們存放于磁盤。

2. 索引結(jié)點(diǎn)(Inode):

索引結(jié)點(diǎn)對象存儲(chǔ)了文件的相關(guān)元數(shù)據(jù)信息,例如:文件大小、設(shè)備標(biāo)識符、用戶標(biāo)識符、用戶組標(biāo)識符等等。Inode 分為兩種:一種是 VFS 的 Inode,一種是具體文件系統(tǒng)的 Inode。前者在內(nèi)存中,后者在磁盤中。所以每次其實(shí)是將磁盤中的 Inode 調(diào)進(jìn)填充內(nèi)存中的 Inode,這樣才是算使用了磁盤文件 Inode。當(dāng)創(chuàng)建一個(gè)文件的時(shí)候,就給文件分配了一個(gè) Inode。一個(gè) Inode 只對應(yīng)一個(gè)實(shí)際文件,一個(gè)文件也會(huì)只有一個(gè) Inode。

3. 目錄項(xiàng)(Dentry):

引入目錄項(xiàng)對象的概念主要是出于方便查找文件的目的。不同于前面的兩個(gè)對象,目錄項(xiàng)對象沒有對應(yīng)的磁盤數(shù)據(jù)結(jié)構(gòu),只存在于內(nèi)存中。一個(gè)路徑的各個(gè)組成部分,不管是目錄還是普通的文件,都是一個(gè)目錄項(xiàng)對象。如,在路徑 /home/source/test.java 中,目錄 /, home, source 和文件 test.java 都對應(yīng)一個(gè)目錄項(xiàng)對象。VFS 在查找的時(shí)候,根據(jù)一層一層的目錄項(xiàng)找到對應(yīng)的每個(gè)目錄項(xiàng)的 Inode,那么沿著目錄項(xiàng)進(jìn)行操作就可以找到最終的文件。

4. 文件對象(File):

文件對象描述的是進(jìn)程已經(jīng)打開的文件。因?yàn)橐粋€(gè)文件可以被多個(gè)進(jìn)程打開,所以一個(gè)文件可以存在多個(gè)文件對象。一個(gè)文件對應(yīng)的文件對象可能不是惟一的,但是其對應(yīng)的索引節(jié)點(diǎn)和目錄項(xiàng)對象肯定是惟一的。

Ext2 文件系統(tǒng)

VFS 的下一層即是具體的文件系統(tǒng),本節(jié)簡要介紹下 Linux 的 Ext2 文件系統(tǒng)。

一個(gè)文件系統(tǒng)一般使用塊設(shè)備上一個(gè)獨(dú)立的邏輯分區(qū)。對于 Ext2 文件系統(tǒng)來說,硬盤分區(qū)首先被劃分為一個(gè)個(gè)的 Block,一個(gè) Ext2 文件系統(tǒng)上的每個(gè) Block 都是一樣大小的。但是不同 Ext2 文件系統(tǒng),Block 大小可能不同,這是在創(chuàng)建 Ext2 系統(tǒng)決定的,一般為 1k 或者 4k。由于 Block 數(shù)量很多,為了方便管理,Ext2 將這些 Block 聚集在一起分為幾個(gè)大的塊組(Block Group),每個(gè)塊組包含的等量的物理塊,在塊組的數(shù)據(jù)塊中存儲(chǔ)文件或目錄。Ext2 文件系統(tǒng)存儲(chǔ)結(jié)構(gòu)如下圖所示:

Ext2 中的 Super Block 和 Inode Table 分別對應(yīng) VFS 中的超級塊和索引結(jié)點(diǎn),存放在磁盤。每個(gè)塊組都有一個(gè)塊組描述符 GDT(Group Descriptor Table),存儲(chǔ)一個(gè)塊組的描述信息,例如在這個(gè)塊組中從哪里開始是 Inode 表,從哪里開始是數(shù)據(jù)塊等等。Block Bitmap 和 Inode Bitmap 分別表示 Block 和 Inode 是否空閑可用。Data Block 數(shù)據(jù)塊是用來真正存儲(chǔ)文件內(nèi)容數(shù)據(jù)的地方,下面我們看一下具體的存儲(chǔ)規(guī)則。

在 Ext2 文件系統(tǒng)中所支持的 Block 大小有 1K、2K、4K 三種。在格式化時(shí) Block 的大小就固定了,且每個(gè) Block 都有編號,方便 Inode 的記錄。每個(gè) Block 內(nèi)最多只能夠放置一個(gè)文件的數(shù)據(jù),如果文件大于 Block 的大小,則一個(gè)文件會(huì)占用多個(gè) Block;如果文件小于 Block,則該 Block 的剩余容量就不能夠再被使用了,即磁盤空間會(huì)浪費(fèi)。下面看看 Inode 和 Block 的對應(yīng)關(guān)系。

Inode 要記錄的數(shù)據(jù)非常多,但大小僅為固定的 128 字節(jié),同時(shí)記錄一個(gè) Block 號碼就需要 4 字節(jié),假設(shè)一個(gè)文件有 400MB 且每個(gè) Block 為 4K 時(shí),那么至少也要十萬筆 Block 號碼的記錄。Inode 不可能有這么多的記錄信息,因此 Ext2 將 Inode 記錄 Block 號碼的區(qū)域定義為 12 個(gè)直接、一個(gè)間接、一個(gè)雙間接與一個(gè)三間接記錄區(qū)。Inode 存儲(chǔ)結(jié)構(gòu)如下圖所示:

最左邊為 Inode 本身(128 bytes),里面有 12 個(gè)直接指向 Block 號碼的對照,這 12 筆記錄能夠直接取得 Block 號碼。至于所謂的間接就是再拿一個(gè) Block 來當(dāng)作記錄 Block 號碼的記錄區(qū),如果文件太大時(shí),就會(huì)使用間接的 Block 來記錄編號。如上圖當(dāng)中間接只是拿一個(gè) Block 來記錄額外的號碼而已。 同理,如果文件持續(xù)長大,那么就會(huì)利用所謂的雙間接,第一個(gè) Block 僅再指出下一個(gè)記錄編號的 Block 在哪里,實(shí)際記錄的在第二個(gè) Block 當(dāng)中。依此類推,三間接就是利用第三層 Block 來記錄編號。

引入 Cache 層的目的是為了提高 Linux 操作系統(tǒng)對磁盤訪問的性能。Cache 層在內(nèi)存中緩存了磁盤上的部分?jǐn)?shù)據(jù)。當(dāng)數(shù)據(jù)的請求到達(dá)時(shí),如果在 Cache 中存在該數(shù)據(jù)且是最新的,則直接將數(shù)據(jù)傳遞給用戶程序,免除了對底層磁盤的操作,提高了性能。Cache 層也正是磁盤 IOPS 為什么能突破 200 的主要原因之一。

在 Linux 的實(shí)現(xiàn)中,文件 Cache 分為兩個(gè)層面,一是 Page Cache,另一個(gè) Buffer Cache,每一個(gè) Page Cache 包含若干 Buffer Cache。Page Cache 主要用來作為文件系統(tǒng)上的文件數(shù)據(jù)的緩存來用,尤其是針對當(dāng)進(jìn)程對文件有 read/write 操作的時(shí)候。Buffer Cache 則主要是設(shè)計(jì)用來在系統(tǒng)對塊設(shè)備進(jìn)行讀寫的時(shí)候,對塊進(jìn)行數(shù)據(jù)緩存的系統(tǒng)來使用。

磁盤 Cache 有兩大功能:預(yù)讀和回寫。預(yù)讀其實(shí)就是利用了局部性原理,具體過程是:對于每個(gè)文件的第一個(gè)讀請求,系統(tǒng)讀入所請求的頁面并讀入緊隨其后的少數(shù)幾個(gè)頁面(通常是三個(gè)頁面),這時(shí)的預(yù)讀稱為同步預(yù)讀。對于第二次讀請求,如果所讀頁面不在 Cache 中,即不在前次預(yù)讀的頁中,則表明文件訪問不是順序訪問,系統(tǒng)繼續(xù)采用同步預(yù)讀;如果所讀頁面在 Cache 中,則表明前次預(yù)讀命中,操作系統(tǒng)把預(yù)讀頁的大小擴(kuò)大一倍,此時(shí)預(yù)讀過程是異步的,應(yīng)用程序可以不等預(yù)讀完成即可返回,只要后臺慢慢讀頁面即可,這時(shí)的預(yù)讀稱為異步預(yù)讀。任何接下來的讀請求都會(huì)處于兩種情況之一:第一種情況是所請求的頁面處于預(yù)讀的頁面中,這時(shí)繼續(xù)進(jìn)行異步預(yù)讀;第二種情況是所請求的頁面處于預(yù)讀頁面之外,這時(shí)系統(tǒng)就要進(jìn)行同步預(yù)讀。

回寫是通過暫時(shí)將數(shù)據(jù)存在 Cache 里,然后統(tǒng)一異步寫到磁盤中。通過這種異步的數(shù)據(jù) I/O 模式解決了程序中的計(jì)算速度和數(shù)據(jù)存儲(chǔ)速度不匹配的鴻溝,減少了訪問底層存儲(chǔ)介質(zhì)的次數(shù),使存儲(chǔ)系統(tǒng)的性能大大提高。Linux 2.6.32 內(nèi)核之前,采用 pdflush 機(jī)制來將臟頁真正寫到磁盤中,什么時(shí)候開始回寫呢?下面兩種情況下,臟頁會(huì)被寫回到磁盤:

  1. 在空閑內(nèi)存低于一個(gè)特定的閾值時(shí),內(nèi)核必須將臟頁寫回磁盤,以便釋放內(nèi)存。

  2. 當(dāng)臟頁在內(nèi)存中駐留超過一定的閾值時(shí),內(nèi)核必須將超時(shí)的臟頁寫會(huì)磁盤,以確保臟頁不會(huì)無限期地駐留在內(nèi)存中。

回寫開始后,pdflush 會(huì)持續(xù)寫數(shù)據(jù),直到滿足以下兩個(gè)條件:

  1. 已經(jīng)有指定的最小數(shù)目的頁被寫回到磁盤。

  2. 空閑內(nèi)存頁已經(jīng)回升,超過了閾值。

Linux 2.6.32 內(nèi)核之后,放棄了原有的 pdflush 機(jī)制,改成了 bdi_writeback 機(jī)制。bdi_writeback 機(jī)制主要解決了原有 fdflush 機(jī)制存在的一個(gè)問題:在多磁盤的系統(tǒng)中,pdflush 管理了所有磁盤的 Cache,從而導(dǎo)致一定程度的 I/O 瓶頸。bdi_writeback 機(jī)制為每個(gè)磁盤都創(chuàng)建了一個(gè)線程,專門負(fù)責(zé)這個(gè)磁盤的 Page Cache 的刷新工作,從而實(shí)現(xiàn)了每個(gè)磁盤的數(shù)據(jù)刷新在線程級的分離,提高了 I/O 性能。

回寫機(jī)制存在的問題是回寫不及時(shí)引發(fā)數(shù)據(jù)丟失(可由 sync|fsync 解決),回寫期間讀 I/O 性能很差。

通用塊層

通用塊層的主要工作是:接收上層發(fā)出的磁盤請求,并最終發(fā)出 I/O 請求。該層隱藏了底層硬件塊設(shè)備的特性,為塊設(shè)備提供了一個(gè)通用的抽象視圖。

對于 VFS 和具體的文件系統(tǒng)來說,塊(Block)是基本的數(shù)據(jù)傳輸單元,當(dāng)內(nèi)核訪問文件的數(shù)據(jù)時(shí),它首先從磁盤上讀取一個(gè)塊。但是對于磁盤來說,扇區(qū)是最小的可尋址單元,塊設(shè)備無法對比它還小的單元進(jìn)行尋址和操作。由于扇區(qū)是磁盤的最小可尋址單元,所以塊不能比扇區(qū)還小,只能整數(shù)倍于扇區(qū)大小,即一個(gè)塊對應(yīng)磁盤上的一個(gè)或多個(gè)扇區(qū)。一般來說,塊大小是 2 的整數(shù)倍,而且由于 Page Cache 層的最小單元是頁(Page),所以塊大小不能超過一頁的長度。

大多情況下,數(shù)據(jù)的傳輸通過 DMA 方式。舊的磁盤控制器,僅僅支持簡單的 DMA 操作:每次數(shù)據(jù)傳輸,只能傳輸磁盤上相鄰的扇區(qū),即數(shù)據(jù)在內(nèi)存中也是連續(xù)的。這是因?yàn)槿绻麄鬏敺沁B續(xù)的扇區(qū),會(huì)導(dǎo)致磁盤花費(fèi)更多的時(shí)間在尋址操作上。而現(xiàn)在的磁盤控制器支持“分散 / 聚合”DMA 操作,這種模式下,數(shù)據(jù)傳輸可以在多個(gè)非連續(xù)的內(nèi)存區(qū)域中進(jìn)行。為了利用“分散 / 聚合”DMA 操作,塊設(shè)備驅(qū)動(dòng)必須能處理被稱為段(segments)的數(shù)據(jù)單元。一個(gè)段就是一個(gè)內(nèi)存頁面或一個(gè)頁面的部分,它包含磁盤上相鄰扇區(qū)的數(shù)據(jù)。

通用塊層是粘合所有上層和底層的部分,一個(gè)頁的磁盤數(shù)據(jù)布局如下圖所示:

I/O 調(diào)度層

I/O 調(diào)度層的功能是管理塊設(shè)備的請求隊(duì)列。即接收通用塊層發(fā)出的 I/O 請求,緩存請求并試圖合并相鄰的請求。并根據(jù)設(shè)置好的調(diào)度算法,回調(diào)驅(qū)動(dòng)層提供的請求處理函數(shù),以處理具體的 I/O 請求。

如果簡單地以內(nèi)核產(chǎn)生請求的次序直接將請求發(fā)給塊設(shè)備的話,那么塊設(shè)備性能肯定讓人難以接受,因?yàn)榇疟P尋址是整個(gè)計(jì)算機(jī)中最慢的操作之一。為了優(yōu)化尋址操作,內(nèi)核不會(huì)一旦接收到 I/O 請求后,就按照請求的次序發(fā)起塊 I/O 請求。為此 Linux 實(shí)現(xiàn)了幾種 I/O 調(diào)度算法,算法基本思想就是通過合并和排序 I/O 請求隊(duì)列中的請求,以此大大降低所需的磁盤尋道時(shí)間,從而提高整體 I/O 性能。

常見的 I/O 調(diào)度算法包括 Noop 調(diào)度算法(No Operation)、CFQ(完全公正排隊(duì) I/O 調(diào)度算法)、DeadLine(截止時(shí)間調(diào)度算法)、AS 預(yù)測調(diào)度算法等。

  • Noop 算法: 最簡單的 I/O 調(diào)度算法。該算法僅適當(dāng)合并用戶請求,并不排序請求。新的請求通常被插在調(diào)度隊(duì)列的開頭或末尾,下一個(gè)要處理的請求總是隊(duì)列中的第一個(gè)請求。這種算法是為不需要尋道的塊設(shè)備設(shè)計(jì)的,如 SSD。因?yàn)槠渌齻€(gè)算法的優(yōu)化是基于縮短尋道時(shí)間的,而 SSD 硬盤沒有所謂的尋道時(shí)間且 I/O 響應(yīng)時(shí)間非常短。

  • CFQ 算法: 算法的主要目標(biāo)是在觸發(fā) I/O 請求的所有進(jìn)程中確保磁盤 I/O 帶寬的公平分配。算法使用許多個(gè)排序隊(duì)列,存放了不同進(jìn)程發(fā)出的請求。通過散列將同一個(gè)進(jìn)程發(fā)出的請求插入同一個(gè)隊(duì)列中。采用輪詢方式掃描隊(duì)列,從第一個(gè)非空隊(duì)列開始,依次調(diào)度不同隊(duì)列中特定個(gè)數(shù)(公平)的請求,然后將這些請求移動(dòng)到調(diào)度隊(duì)列的末尾。

  • Deadline 算法: 算法引入了兩個(gè)排隊(duì)隊(duì)列分別包含讀請求和寫請求,兩個(gè)最后期限隊(duì)列包含相同的讀和寫請求。本質(zhì)就是一個(gè)超時(shí)定時(shí)器,當(dāng)請求被傳給電梯算法時(shí)開始計(jì)時(shí)。一旦最后期限隊(duì)列中的超時(shí)時(shí)間已到,就想請求移至調(diào)度隊(duì)列末尾。Deadline 算法避免了電梯調(diào)度策略(為了減少尋道時(shí)間,會(huì)優(yōu)先處理與上一個(gè)請求相近的請求)帶來的對某個(gè)請求忽略很長一段時(shí)間的可能。

  • AS 算法:AS 算法本質(zhì)上依據(jù)局部性原理,預(yù)測進(jìn)程發(fā)出的讀請求與剛被調(diào)度的請求在磁盤上可能是“近鄰”。算法統(tǒng)計(jì)每個(gè)進(jìn)程 I/O 操作信息,當(dāng)剛剛調(diào)度了由某個(gè)進(jìn)程的一個(gè)讀請求之后,算法馬上檢查排序隊(duì)列中的下一個(gè)請求是否來自同一個(gè)進(jìn)程。如果是,立即調(diào)度下一個(gè)請求。否則,查看關(guān)于該進(jìn)程的統(tǒng)計(jì)信息,如果確定進(jìn)程 p 可能很快發(fā)出另一個(gè)讀請求,那么就延遲一小段時(shí)間。

前文中計(jì)算出的 IOPS 是理論上的隨機(jī)讀寫的最大 IOPS,在隨機(jī)讀寫中,每次 I/O 操作的尋址和旋轉(zhuǎn)延時(shí)都不能忽略不計(jì),有了這兩個(gè)時(shí)間的存在也就限制了 IOPS 的大小。現(xiàn)在如果我們考慮在讀取一個(gè)很大的存儲(chǔ)連續(xù)分布在磁盤的文件,因?yàn)槲募拇鎯?chǔ)的分布是連續(xù)的,磁頭在完成一個(gè)讀 I/O 操作之后,不需要重新尋址,也不需要旋轉(zhuǎn)延時(shí),在這種情況下我們能到一個(gè)很大的 IOPS 值。這時(shí)由于不再考慮尋址和旋轉(zhuǎn)延時(shí),則性能瓶頸僅是數(shù)據(jù)傳輸時(shí)延,假設(shè)數(shù)據(jù)傳輸時(shí)延為 0.4ms,那么 IOPS=1000 / 0.4 = 2500 IOPS。

在許多的開源框架如 Kafka、HBase 中,都通過追加寫的方式來盡可能的將隨機(jī) I/O 轉(zhuǎn)換為順序 I/O,以此來降低尋址時(shí)間和旋轉(zhuǎn)延時(shí),從而最大限度的提高 IOPS。

塊設(shè)備驅(qū)動(dòng)層

驅(qū)動(dòng)層中的驅(qū)動(dòng)程序?qū)?yīng)具體的物理塊設(shè)備。它從上層中取出 I/O 請求,并根據(jù)該 I/O 請求中指定的信息,通過向具體塊設(shè)備的設(shè)備控制器發(fā)送命令的方式,來操縱設(shè)備傳輸數(shù)據(jù)。這里不再贅述。

基于磁盤 I/O 特性設(shè)計(jì)的技巧

在上一節(jié)中我們了解了 Linux 系統(tǒng)中請求到達(dá)磁盤的一次完整過程,期間 Linux 通過 Cache 以及排序合并 I/O 請求來提高系統(tǒng)的性能。其本質(zhì)就是由于磁盤隨機(jī)讀寫慢、順序讀寫快。本節(jié)針對常見開源系統(tǒng)闡述一些基于磁盤 I/O 特性的設(shè)計(jì)技巧。

采用追加寫

在進(jìn)行系統(tǒng)設(shè)計(jì)時(shí),良好的讀性能和寫性能往往不可兼得。在許多常見的開源系統(tǒng)中都是優(yōu)先在保證寫性能的前提下來優(yōu)化讀性能。那么如何設(shè)計(jì)能讓一個(gè)系統(tǒng)擁有良好的寫性能呢?一個(gè)好的辦法就是采用追加寫,每次將數(shù)據(jù)添加到文件。由于完全是順序的,所以可以具有非常好的寫操作性能。但是這種方式也存在一些缺點(diǎn):從文件中讀一些數(shù)據(jù)時(shí)將會(huì)需要更多的時(shí)間:需要倒序掃描,直到找到所需要的內(nèi)容。當(dāng)然在一些簡單的場景下也能夠保證讀操作的性能:

數(shù)據(jù)是被整體訪問,比如 HDFS

HDFS 建立在一次寫多次讀的模型之上。在 HDFS 中就是采用了追加寫并且設(shè)計(jì)為高數(shù)據(jù)吞吐量;高吞吐量必然以高延遲為代價(jià),所以 HDFS 并不適用于對數(shù)據(jù)訪問要求低延遲的場景;由于采用是的追加寫,也并不適用于任意修改文件的場景。HDFS 設(shè)計(jì)為流式訪問大文件,使用大數(shù)據(jù)塊并且采用流式數(shù)據(jù)訪問來保證數(shù)據(jù)被整體訪問,同時(shí)最小化硬盤的尋址開銷,只需要一次尋址即可,這時(shí)尋址時(shí)間相比于傳輸時(shí)延可忽略,從而也擁有良好的讀性能。HDFS 不適合存儲(chǔ)小文件,原因之一是由于 NameNode 內(nèi)存不足問題,還有就是因?yàn)樵L問大量小文件需要執(zhí)行大量的尋址操作,并且需要不斷的從一個(gè) datanode 跳到另一個(gè) datanode,這樣會(huì)大大降低數(shù)據(jù)訪問性能。

知道文件明確的偏移量,比如 Kafka

在 Kafka 中,采用消息追加的方式來寫入每個(gè)消息,每個(gè)消息讀寫時(shí)都會(huì)利用 Page Cache 的預(yù)讀和后寫特性,同時(shí) partition 中都使用順序讀寫,以此來提高 I/O 性能。雖然 Kafka 能夠根據(jù)偏移量查找到具體的某個(gè)消息,但是查找過程是順序查找,因此如果數(shù)據(jù)很大的話,查找效率就很低。

所以 Kafka 中采用了分段和索引的方式來解決查找效率問題。Kafka 把一個(gè) patition 大文件又分成了多個(gè)小文件段,每個(gè)小文件段以偏移量命名,通過多個(gè)小文件段,不僅可以使用二分搜索法很快定位消息,同時(shí)也容易定期清除或刪除已經(jīng)消費(fèi)完的文件,減少磁盤占用。

為了進(jìn)一步提高查找效率,Kafka 為每個(gè)分段后的數(shù)據(jù)建立了索引文件,并通過索引文件稀疏存儲(chǔ)來降低元數(shù)據(jù)占用大小。一個(gè)段中數(shù)據(jù)對應(yīng)結(jié)構(gòu)如下圖所示:

在面對更復(fù)雜的讀場景(比如按 key)時(shí),如何來保證讀操作的性能呢?簡單的方式是像 Kafka 那樣,將文件數(shù)據(jù)有序保存,使用二分查找來優(yōu)化效率;或者通過建索引的方式來進(jìn)行優(yōu)化;也可以采用 hash 的方式將數(shù)據(jù)分割為不同的桶。以上的方法都能增加讀操作的性能,但是由于在數(shù)據(jù)上強(qiáng)加了數(shù)據(jù)結(jié)構(gòu),又會(huì)降低寫操作的性能。比如如果采用索引的方式來優(yōu)化讀操作,那么在更新索引時(shí)就需要更新 B-tree 中的特定部分,這時(shí)候的寫操作就是隨機(jī)寫。那么有沒有一種辦法在保證寫性能不損失的同時(shí)也提供較好的讀性能呢?一個(gè)好的選擇就是使用 LSM-tree。LSM-tree 與 B-tree 相比,LSM-tree 犧牲了部分讀操作,以此大幅提高寫性能。

日志結(jié)構(gòu)的合并樹 LSM(The Log-Structured Merge-Tree)是 HBase,LevelDB 等 NoSQL 數(shù)據(jù)庫的存儲(chǔ)引擎。Log-Structured 的思想是將整個(gè)磁盤看做一個(gè)日志,在日志中存放永久性數(shù)據(jù)及其索引,每次都添加到日志的末尾。并且通過將很多小文件的存取轉(zhuǎn)換為連續(xù)的大批量傳輸,使得對于文件系統(tǒng)的大多數(shù)存取都是順序的,從而提高磁盤 I/O。

LSM-tree 就是這樣一種采用追加寫、數(shù)據(jù)有序以及將隨機(jī) I/O 轉(zhuǎn)換為順序 I/O 的延遲更新,批量寫入硬盤的數(shù)據(jù)結(jié)構(gòu)。LSM-tree 將數(shù)據(jù)的修改增量先保存在內(nèi)存中,達(dá)到指定的大小限制后再將這些修改操作批量寫入磁盤。因此比較舊的文件不會(huì)被更新,重復(fù)的紀(jì)錄只會(huì)通過創(chuàng)建新的紀(jì)錄來覆蓋,這也就產(chǎn)生了一些冗余的數(shù)據(jù)。所以系統(tǒng)會(huì)周期性的合并一些數(shù)據(jù),移除重復(fù)的更新或者刪除紀(jì)錄,同時(shí)也會(huì)刪除上述的冗余。

在進(jìn)行讀操作時(shí),如果內(nèi)存中沒有找到相應(yīng)的 key,那么就是倒序從一個(gè)個(gè)磁盤文件中查找。如果文件越來越多那么讀性能就會(huì)越來越低,目前的解決方案是采用頁緩存來減少查詢次數(shù),周期合并文件也有助于提高讀性能。在文件越來越多時(shí),可通過布隆過濾器來避免大量的讀文件操作。

LSM-tree 犧牲了部分讀性能,以此來換取寫入的最大化性能,特別適用于讀需求低,會(huì)產(chǎn)生大量插入操作的應(yīng)用環(huán)境。

文件合并和元數(shù)據(jù)優(yōu)化

目前的大多數(shù)文件系統(tǒng),如 XFS/Ext4、GFS、HDFS,在元數(shù)據(jù)管理、緩存管理等實(shí)現(xiàn)策略上都側(cè)重大文件。上述基于磁盤 I/O 特性設(shè)計(jì)的系統(tǒng)都有一個(gè)共性特點(diǎn)就是都運(yùn)行在這些文件系統(tǒng)之上。這些文件系統(tǒng)在面臨海量時(shí)在性能和存儲(chǔ)效率方面都大幅降低,本節(jié)來探討下海量小文件下的系統(tǒng)設(shè)計(jì)。

常見文件系統(tǒng)在海量小文件應(yīng)用下性能表現(xiàn)不佳的根本原因是磁盤最適合順序的大文件 I/O 讀寫模式,而非常不適合隨機(jī)的小文件 I/O 讀寫模式。主要原因體現(xiàn)在元數(shù)據(jù)管理低效和數(shù)據(jù)布局低效:

  • 元數(shù)據(jù)管理低效: 由于小文件數(shù)據(jù)內(nèi)容較少,因此元數(shù)據(jù)的訪問性能對小文件訪問性能影響巨大。Ext2 文件系統(tǒng)中 Inode 和 Data Block 分別保存在不同的物理位置上,一次讀操作需要至少經(jīng)過兩次的獨(dú)立訪問。在海量小文件應(yīng)用下,Inode 的頻繁訪問,使得原本的并發(fā)訪問轉(zhuǎn)變?yōu)榱撕A康碾S機(jī)訪問,大大降低了性能。另外,大量的小文件會(huì)快速耗盡 Inode 資源,導(dǎo)致磁盤盡管有大量 Data Block 剩余也無法存儲(chǔ)文件,會(huì)浪費(fèi)磁盤空間。

  • 數(shù)據(jù)布局低效:Ext2 在 Inode 中使用多級指針來索引數(shù)據(jù)塊。對于大文件,數(shù)據(jù)塊的分配會(huì)盡量連續(xù),這樣會(huì)具有比較好的空間局部性。但是對于小文件,數(shù)據(jù)塊可能零散分布在磁盤上的不同位置,并且會(huì)造成大量的磁盤碎片,不僅造成訪問性能下降,還大量浪費(fèi)了磁盤空間。數(shù)據(jù)塊一般為 1KB、2KB 或 4KB,對于小于 4KB 的小文件,Inode 與數(shù)據(jù)的分開存儲(chǔ)破壞了空間局部性,同時(shí)也造成了大量的隨機(jī) I/O。

對于海量小文件應(yīng)用,常見的 I/O 流程復(fù)雜也是造成磁盤性能不佳的原因。對于小文件,磁盤的讀寫所占用的時(shí)間較少,而用于文件的 open 操作占用了絕大部分系統(tǒng)時(shí)間,導(dǎo)致磁盤有效服務(wù)時(shí)間非常低,磁盤性能低下。針對于問題的根源,優(yōu)化的思路大體上分為:

  1. 針對數(shù)據(jù)布局低效,采用小文件合并策略,將小文件合并為大文件。

  2. 針對元數(shù)據(jù)管理低效,優(yōu)化元數(shù)據(jù)的存儲(chǔ)和管理。針對這兩種優(yōu)化方式,業(yè)內(nèi)也出現(xiàn)了許多優(yōu)秀的開源軟件。

小文件合并

小文件合并為大文件后,首先減少了大量元數(shù)據(jù),提高了元數(shù)據(jù)的檢索和查詢效率,降低了文件讀寫的 I/O 操作延時(shí)。其次將可能連續(xù)訪問的小文件一同合并存儲(chǔ),增加了文件之間的局部性,將原本小文件間的隨機(jī)訪問變?yōu)榱隧樞蛟L問,大大提高了性能。同時(shí),合并存儲(chǔ)能夠有效的減少小文件存儲(chǔ)時(shí)所產(chǎn)生的磁盤碎片問題,提高了磁盤的利用率。最后,合并之后小文件的訪問流程也有了很大的變化,由原來許多的 open 操作轉(zhuǎn)變?yōu)榱?seek 操作,定位到大文件具體的位置即可。如何尋址這個(gè)大文件中的小文件呢?其實(shí)就是利用一個(gè)旁路數(shù)據(jù)庫來記錄每個(gè)小文件在這個(gè)大文件中的偏移量和長度等信息。其實(shí)小文件合并的策略本質(zhì)上就是通過分層的思想來存儲(chǔ)元數(shù)據(jù)。中控節(jié)點(diǎn)存儲(chǔ)一級元數(shù)據(jù),也就是大文件與底層塊的對應(yīng)關(guān)系;數(shù)據(jù)節(jié)點(diǎn)存放二級元數(shù)據(jù),也就是最終的用戶文件在這些一級大塊中的存儲(chǔ)位置對應(yīng)關(guān)系,經(jīng)過兩級尋址來讀寫數(shù)據(jù)。

淘寶的TFS就采用了小文件合并存儲(chǔ)的策略。TFS中默認(rèn)Block大小為64M,每個(gè)塊中會(huì)存儲(chǔ)許多不同的小文件,但是這個(gè)塊只占用一個(gè)Inode。假設(shè)一個(gè)Block為64M,數(shù)量級為1PB。那么NameServer上會(huì)有 1 1024 1024 * 1024 / 64 = 16.7M個(gè)Block。

假設(shè)每個(gè)Block的元數(shù)據(jù)大小為0.1K,則占用內(nèi)存不到2G。在TFS中,文件名中包含了Block ID和File ID,通過Block ID定位到具體的DataServer上,然后DataServer會(huì)根據(jù)本地記錄的信息來得到File ID所在Block的偏移量,從而讀取到正確的文件內(nèi)容。TFS一次讀過程如下圖所示:

元數(shù)據(jù)管理優(yōu)化

一般來說元數(shù)據(jù)信息包括名稱、文件大小、設(shè)備標(biāo)識符、用戶標(biāo)識符、用戶組標(biāo)識符等等,在小文件系統(tǒng)中可以對元數(shù)據(jù)信息進(jìn)行精簡,僅保存足夠的信息即可。元數(shù)據(jù)精簡可以減少元數(shù)據(jù)通信延時(shí),同時(shí)相同容量的 Cache 能存儲(chǔ)更多的元數(shù)據(jù),從而提高元數(shù)據(jù)使用效率。另外可以在文件名中就包含元數(shù)據(jù)信息,從而減少一個(gè)元數(shù)據(jù)的查詢操作。最后針對特別小的一些文件,可以采取元數(shù)據(jù)和數(shù)據(jù)并存的策略,將數(shù)據(jù)直接存儲(chǔ)在元數(shù)據(jù)之中,通過減少一次尋址操作從而大大提高性能。

TFS 中文件命名就隱含了位置信息等部分元數(shù)據(jù),從而減少了一個(gè)元數(shù)據(jù)的查詢操作。在 Rerserfs 中,對于小于 1KB 的小文件,Rerserfs 可以將數(shù)據(jù)直接存儲(chǔ)在 Inode 中。

總結(jié)

本文從磁盤性能指標(biāo)出發(fā),探究了操作系統(tǒng)與磁盤的交互以及對磁盤讀寫的優(yōu)化,最后列舉了一些常用開源系統(tǒng)中基于磁盤 I/O 特性的設(shè)計(jì)特點(diǎn)。期望通過展現(xiàn)磁盤 I/O 的特性,為存儲(chǔ)系統(tǒng)設(shè)計(jì)和解決一些系統(tǒng)性能問題提供一種新思路。

作者介紹

喻梟,2016 年加入美團(tuán)點(diǎn)評,就職于美團(tuán)點(diǎn)評酒店旅游事業(yè)群境內(nèi)度假研發(fā)組。專注 Java 后臺開發(fā),對并發(fā)編程大數(shù)據(jù)有濃厚興趣。

本文轉(zhuǎn)載自美團(tuán)點(diǎn)評技術(shù)團(tuán)隊(duì),InfoQ 已獲得轉(zhuǎn)載授權(quán)。美團(tuán)點(diǎn)評技術(shù)團(tuán)隊(duì)微信 ID:meituantech

參考文章

  1. IBM developerWorks,AIX 下磁盤 I/O 性能分析 (https://www.ibm.com/developerworks/cn/aix/library/1203_weixy_aixio/),2012。

  2. CSDN 博客頻道,磁盤性能評價(jià)指標(biāo)—IOPS 和吞吐量 (http://blog.csdn.net/hanchengxi/article/details/19089589),2014。

  3. IBM developerWorks,read 系統(tǒng)調(diào)用剖析 (https://www.ibm.com/developerworks/cn/linux/l-cn-read/),2008。

  4. IBM developerWorks,從文件 I/O 看 Linux 的虛擬文件系統(tǒng) (https://www.ibm.com/developerworks/cn/linux/l-cn-vfs/),2007。

  5. CSDN 博客頻道,Linux 文件系統(tǒng)預(yù)讀 (http://blog.csdn.net/kai_ding/article/details/17322787),2013。

  6. Linux Kernel Exploration,Linux 通用塊設(shè)備層 (http://www./files/Linux.Generic.Block.Layer.pdf)。

  7. CSDN 博客頻道,Linux 塊設(shè)備的 IO 調(diào)度算法和回寫機(jī)制 (http://blog.csdn.net/hustyangju/article/details/40507647),2014。

  8. Apache,Kafka

  9. Taobao,Taobao File System。

2017年,有哪些值得關(guān)注的運(yùn)維技術(shù)熱點(diǎn)?智能化運(yùn)維、Serverless、DevOps ......CNUTCon全球運(yùn)維技術(shù)大會(huì)將于9月上海舉辦,12位大牛聯(lián)合出品,揭秘最前沿運(yùn)維技術(shù),推薦學(xué)習(xí)!



點(diǎn)擊“閱讀原文”,先睹為快!

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产精品一区二区香蕉视频| 人妻偷人精品一区二区三区不卡| 亚洲av一区二区三区精品| 国产欧美日产中文一区| 草草视频精品在线观看| 99精品国产一区二区青青| 开心激情网 激情五月天| 五月婷日韩中文字幕四虎| 精品推荐久久久国产av| 亚洲精品国产主播一区| 国产在线观看不卡一区二区| 国产麻豆一区二区三区在| 成人综合网视频在线观看| 粗暴蹂躏中文一区二区三区| 国产欧美日韩精品自拍| 国产精品一区二区不卡中文 | 婷婷开心五月亚洲综合| 激情亚洲内射一区二区三区| 人妻久久一区二区三区精品99| 日本女人亚洲国产性高潮视频| 久草热视频这里只有精品| 亚洲日本加勒比在线播放 | 亚洲精品有码中文字幕在线观看| 91亚洲精品亚洲国产| 日本欧美视频在线观看免费| 不卡一区二区在线视频| 成年女人下边潮喷毛片免费| 中文字幕熟女人妻视频| 亚洲男人的天堂久久a| 久久成人国产欧美精品一区二区| 日韩一区二区三区在线欧洲| 日韩精品视频高清在线观看| 欧洲自拍偷拍一区二区| 中日韩美一级特黄大片| 欧美日韩精品综合一区| 久久精品国产亚洲av久按摩 | 91日韩在线观看你懂的| 午夜成年人黄片免费观看| 久草国产精品一区二区| 国产精品熟女在线视频| 午夜色午夜视频之日本|