磁盤的可靠性其實很低,這種數(shù)據(jù)可靠性技術(shù)可以解決
在我們?nèi)粘5恼J識中,磁盤是非??煽康?。作者03年自己組裝的電腦的磁盤現(xiàn)在還在用著,想想都快二十年了。很多人可能會想,消費級的磁盤都這么靠譜,那企業(yè)級的豈不是更加厲害。其實并非如此,其實磁盤是很容易出現(xiàn)故障的。
作者有幸參觀過一個大型的數(shù)據(jù)中心,發(fā)現(xiàn)成堆的故障磁盤。據(jù)說,該數(shù)據(jù)中心每天有幾十塊磁盤出現(xiàn)故障。谷歌在設(shè)計其分布式文件系統(tǒng)GFS的時候,也是將磁盤作為非常不可靠部件進行考慮的,因此在其架構(gòu)中通過多副本保證其可靠性。
想想其中的原因,大概是使用頻度的差異造成的錯覺吧。我們平時使用的磁盤平均下來每天應(yīng)該不會超過一小時,而且大部分時間都沒有數(shù)據(jù)讀寫。而數(shù)據(jù)中心磁盤則是7*24小時運行,且負載很重。試想,如果讓你7*24小時不歇著,估計...
好了,廢話少說。既然磁盤這么不靠譜,我們今天就給大家介紹一下存儲領(lǐng)域保證磁盤可靠性的技術(shù)。為了方便大家理解和日后學習,我們就以Linux內(nèi)核為例進行介紹,其實技術(shù)是相通的,大家可以遷移過去。在Linux操作系統(tǒng)下提升磁盤可靠性的技術(shù)就是RAID技術(shù)。具體實現(xiàn)有兩種,一種是通過MD實現(xiàn)的多磁盤設(shè)備,另外一種是LVM。今天我們主要介紹一下通過MD實現(xiàn)的RAID技術(shù)。
一、RAID的原理及基本操作
人類認識事物的客觀規(guī)律是具體的比抽象的容易理解。因此,為了讓大家更加容易理解本文介紹的內(nèi)容,我們先從具體的內(nèi)容開始。
1. RAID的基本原理
RAID的全稱為廉價冗余磁盤陣列(Redundant Array of Inexpensive Disks),從字面可以看出其基本原理就是通過廉價的磁盤組成一組磁盤,從而提高磁盤的整體可靠性。在Linux操作系統(tǒng)層面,其實就是將物理磁盤通過軟件抽象為邏輯磁盤。以RAID1(兩塊磁盤存儲相同的數(shù)據(jù),在出現(xiàn)一塊磁盤故障的情況下,數(shù)據(jù)不丟失)為例,通過Linux內(nèi)核中的軟件創(chuàng)建一個虛擬的塊設(shè)備,而該塊設(shè)備中記錄了底層對應(yīng)的物理設(shè)備及相關(guān)參數(shù)。
圖1 RAID示意圖
因此,從用戶層面來看就是一塊普通的磁盤設(shè)備,而在底層卻是2個獨立的物理硬盤。當用戶向邏輯磁盤寫數(shù)據(jù)的時候,其中的軟件會通過參數(shù)進行計算,并將數(shù)據(jù)重新定向到底層的物理設(shè)備。通過這種方法可以保證即使出現(xiàn)某個物理磁盤損壞,用戶的數(shù)據(jù)仍然完好無損。
2. Linux下RAID管理
在Linux操作系統(tǒng)下可以通過mdadm工具非常方便的創(chuàng)建RAID。我們以RAID1為例演示一下如何創(chuàng)建??赡芎芏嗤瑢W沒有多塊物理磁盤,其實沒有關(guān)系,我們可以通過虛擬機創(chuàng)建虛擬磁盤或者loop設(shè)備模擬的方式創(chuàng)建RAID。
為了方便大家練習,本文就通過loop設(shè)備模擬磁盤來創(chuàng)建RAID。首先需要創(chuàng)建2個loop設(shè)備。具體執(zhí)行如下命令:
圖2 創(chuàng)建loop設(shè)備
成功運行上述命令后,在/dev目錄下就多出2個設(shè)備,分別是loop0和loop1。我們可以將這兩個設(shè)備當做磁盤來使用。下面我們就可以創(chuàng)建RAID了,非常簡單,通過一條命令就可以了。
- mdadm --create /dev/md1 --level=1 --raid-devices=2 /de
在上面命令中/dev/md1表示創(chuàng)建的新設(shè)備的名稱,level=1表示是RAID1,后面分別是物理設(shè)備的數(shù)量和具體的物理設(shè)備路徑。
除了創(chuàng)建RAID之外,mdadm還支持很多功能,比如獲取RAID的詳細信息(mdadm --detail /dev/md1)。
圖3 RAID的詳細信息
該命令的功能很多,我們就不一一介紹了。這里只是拋磚引玉,具體內(nèi)容大家自行學習就行。后面我們重點從原理和實現(xiàn)層面介紹一下Linux的RAID技術(shù)。
3. RAID軟件架構(gòu)
Linux的RAID實現(xiàn)在用戶態(tài)和內(nèi)核態(tài)都有涉及。其中用戶態(tài)主要進行RAID的管理,而內(nèi)核態(tài)一方面配合用戶態(tài)進行RAID管理,另外一方面則實現(xiàn)對IO的處理,這部分才是RAID最為核心的內(nèi)容。
圖4 軟件架構(gòu)
對于基于SCSI物理磁盤的RAID來說,Linux環(huán)境下整個軟件架構(gòu)如圖4所示。其中虛線以上的為用戶態(tài)的軟件模塊,虛線以下的為內(nèi)核態(tài)的軟件模塊。這里比較核心的是RAID公共層,在這里主要創(chuàng)建md設(shè)備,該設(shè)備是一個邏輯設(shè)備,也是用戶可以看到的RAID設(shè)備。其下則是具體的RAID模塊,用于實現(xiàn)不同的RAID級別(算法)。
再往下就是通用SCSI驅(qū)動層了,也就是圖中的SCSI磁盤驅(qū)動這一層的內(nèi)容。該層其實是SCSI系統(tǒng)的上層驅(qū)動(SCSI子系統(tǒng)分為上中下三層)。RAID模塊通過調(diào)用該層的數(shù)據(jù)訪問接口就可以實現(xiàn)物理磁盤數(shù)據(jù)讀寫了。
這里需要說明的是,這里的物理磁盤并不一定是本地磁盤。由于基于SAN或者其它協(xié)議的磁盤可以通過光纖或者SAS線連接到主機,并呈現(xiàn)為物理硬盤。這種物理硬盤與本地物理硬盤沒有任何差異。
二、RAID的代碼淺析
針對Linux內(nèi)核的具體實現(xiàn),我們簡單介紹一下其中的代碼。關(guān)于代碼部分我們以RAID1為例介紹兩部分的內(nèi)容,一部分是關(guān)于創(chuàng)建RAID的邏輯;另一部分是請求處理邏輯。理解了上述內(nèi)容,也就理解了關(guān)于RAID代碼邏輯的大部分內(nèi)容。
1. 關(guān)于RAID的超級塊
接觸過Linux文件系統(tǒng)的同學應(yīng)該對超級塊不會陌生。在RAID中也有超級塊(superblock),并且作用與文件系統(tǒng)類似。RAID超級塊的作用類似,可以將超級塊理解稱為RAID的地圖。RAID軟件對底層物理磁盤的一切操作都以該超級塊為依據(jù)。
Linux的RAID有多個版本,包括0.9、1.0、1.1和1.2四個版本,且版本之間并不能保證兼容性。對于1.2版本的RAID,其超級塊位于開始4KB偏移的位置。我們可以通過dd或者其它工具將該數(shù)據(jù)導(dǎo)出到文件中,并通過二進制工具查看。
圖5 RAID的超級塊
如圖5是作者導(dǎo)出的上面創(chuàng)建的RAID1的超級塊信息及數(shù)據(jù)結(jié)構(gòu)(mdp_superblock_1)對比圖。如果看不清楚也沒關(guān)系,大家可以自行獲取上述進行,并對比。
RAID的超級塊內(nèi)容非常多,在本文不可能一一介紹。這里大家只要知道這里面包含創(chuàng)建時間、RAID級別、設(shè)備大小及同步信息等內(nèi)容即可。后續(xù)我們可能會專門介紹超級塊中每個成員的具體作用。
2. 創(chuàng)建RAID核心流程
創(chuàng)建RAID的流程是由用戶態(tài)觸發(fā),內(nèi)核態(tài)具體完成的。RAID的創(chuàng)建核心分為3個步驟,具體如下:
- 用戶態(tài)工具mdadm根據(jù)參數(shù)構(gòu)建超級塊,并寫入物理設(shè)備
- 用戶態(tài)工具觸發(fā)創(chuàng)建md設(shè)備(RAID設(shè)備)
- 用戶態(tài)工具觸發(fā)內(nèi)核,是RAID處于運行狀態(tài)
其中第一步我們不再解釋,原理很簡單,大家自行閱讀代碼即可。關(guān)于第二步,用戶態(tài)工具(mdadm)通過向/sys/module/md_mod/parameters/new_array中寫入一個名為md*的字符串來觸發(fā)內(nèi)核創(chuàng)建md設(shè)備。
圖6 RAID創(chuàng)建流程
這里的核心是分配一個關(guān)于md設(shè)備的數(shù)據(jù)結(jié)構(gòu)(mddev),并且調(diào)用通用塊層的接口創(chuàng)建一個通用塊設(shè)備并添加(add_disk)到系統(tǒng)。成功之后,我們就可以在/dev目錄下看到我們想創(chuàng)建的md設(shè)備了,設(shè)備名稱就是mdadm傳入的參數(shù)。這里面需要重點關(guān)注的是,在分配md設(shè)備的數(shù)據(jù)結(jié)構(gòu)的時候會關(guān)聯(lián)一個名為md_make_request的函數(shù),該函數(shù)就是RAID的IO處理函數(shù)。
此時已經(jīng)可以看到設(shè)備,但是還不能使用,因為RAID設(shè)備還沒有與底層的物理設(shè)備關(guān)聯(lián)起來。因此,后續(xù)mdadm工具會通過系統(tǒng)調(diào)用觸發(fā)內(nèi)核啟動RAID,具體流程如圖7所示。
圖7 RAID啟動流程
此時,內(nèi)核會根據(jù)超級塊信息執(zhí)行若干動作,并且更改其中某些狀態(tài)標記。成功之后RAID設(shè)備就可以使用了。關(guān)于細節(jié)我們這里不做介紹,介紹了大家也記不住,有興趣的同學可以自行閱讀代碼。
3. 讀寫請求核心流程
前文我們知道創(chuàng)建RAID其實是創(chuàng)建了一個通用塊設(shè)備,并注冊了請求處理函數(shù)(md_make_request)。當在用戶態(tài)通過文件系統(tǒng)接口訪問該塊設(shè)備(RAID)時,虛擬文件系統(tǒng)會調(diào)用該函數(shù)(請參考本號之前關(guān)于SCSI磁盤的文章)。因此,關(guān)于RAID的讀寫流程,我們就從該函數(shù)開始介紹,下面先看一下整體流程。
圖8 讀寫流程
從圖8可以看出整個流程還是比較簡單的。在公共層會根據(jù)參數(shù)調(diào)用個性層的接口,對于RAID1來說就是調(diào)用raid1_make_request函數(shù)。該函數(shù)中會根據(jù)請求類型出現(xiàn)不同的分支,上圖是寫數(shù)據(jù)的流程。
RAID1本身比較簡單(請求分別放入底層物理磁盤),IO經(jīng)過簡單處理后會放入一個隊列中。然后喚醒守護線程刷寫隊列。