前言
為了提升我們的軟件性能,我們有多種方法,如合理的數(shù)據(jù)結(jié)構(gòu)、優(yōu)秀的算法,還有非常重要的一點就是:依據(jù)軟件所依附的硬件自身特性,設(shè)計能最大限度發(fā)揮硬件性能的軟件。根據(jù)計算機內(nèi)存快但無法持久化,硬盤可以持久化但是慢的特性,我們設(shè)計了「緩存」這一策略來提升性能;根據(jù)硬盤隨機讀寫慢但順序讀寫快這一特性,Kafka 用自己獨特的方式來實現(xiàn)高吞吐量的隊列。如今,由于 SSD 出色的性能以及逐漸降低的價格,有逐步代替 HDD(傳統(tǒng)硬盤)的趨勢,而我們之前的大部分程序是基于 HDD 開發(fā)的,并不能最好的發(fā)揮 SSD 的性能,因此,我們有必要了解 SSD 的特性,以及如何寫更適合于 SSD 的程序。
SSD 介紹
SSD 是用固態(tài)電子存儲芯片陣列而制成的硬盤,由控制單元和存儲單元(FLASH芯片、DRAM芯片)組成。SSD 的每個數(shù)據(jù)位保存在由浮柵晶體管制成的閃存單元里。SSD整個都是由電子組件制成的,沒有像硬盤那樣的移動或者機械的部分。SSD 的內(nèi)部架構(gòu)如下:
1. 使用壽命
SSD 的數(shù)據(jù)保存在浮柵晶體管中,浮柵晶體管使用電壓來實現(xiàn)每個位的讀寫和擦除。寫晶體管有兩個方法:NOR 閃存和 NAND 閃存。目前大多數(shù)制造商都采用 NAND 閃存,NAND 閃存模塊的一個重要特征是,他們的閃存單元是損耗性的,因此它們有一個壽命。衡量壽命的單位是 PE周期(program/erase cycles)。
2. 性能
下圖是 SSD 與其他存儲介質(zhì)的性能對比:
其中 SLC、MLC、TLC 可以理解為不同類型的 SSD,下面會有介紹。
2. 存儲結(jié)構(gòu)
SSD 存儲結(jié)構(gòu)分為單元、頁、塊三層,下面依次介紹:
-
單元(cell):單元是 SSD 存儲的最小單位,由于采用的閃存類型不同,各類閃存中的單元能存儲的數(shù)據(jù)也不同,目前有三種閃存單元類型:
- 單層單元(SLC),這種的晶體管只能存儲 1 個比特但壽命很長。
- 多層單元(MLC),這種的晶體管可以存儲 2 個比特,但是會導(dǎo)致增 加延遲時間和相對于SLC減少壽命。
- 三層單元(TLC),這種的晶體管可以保存 3 個比特,但是會有更高的延遲時間和更短的壽命。
頁(page):頁由許多單元組成,是我們讀寫 SSD 的最小單位,大多數(shù)硬盤的頁大小是 2KB、4KB、8 KB 或 16 KB。當(dāng)我們讀 SSD 的數(shù)據(jù)時,最少讀取一頁(就算我們只需要 1 字節(jié)的數(shù)據(jù));當(dāng)我們寫數(shù)據(jù)時,就算只需要寫 1 字節(jié),也會寫一頁,因此存在寫入放大的問題。頁不能被重復(fù)寫。
塊(block):塊由許多頁組成,是數(shù)據(jù)擦除的最小單元,大多數(shù)SSD每個塊有128或256個頁。這即表示一個塊的大小可能在 256 KB 到 4 MB 之間。之前我們說過,頁不能被重復(fù)寫,要修改頁的數(shù)據(jù),必須先將頁所在的塊擦除,然后再重新寫入新值(塊中其他葉需要緩存然后再寫入)。
3. 數(shù)據(jù)更新
由于 SSD 不支持數(shù)據(jù)覆蓋寫入,因此對于數(shù)據(jù)的更新,只能將老的數(shù)據(jù)標(biāo)記為過期,在另外一個空閑的地方寫入更新后的值。具體操作如下圖:
由上圖我們可知,更新一個已有數(shù)據(jù)的流程如下:
- 將老的數(shù)據(jù)所在的頁(PPN=0)標(biāo)記為過期
- 在空閑頁(PPN=3)寫入新值 x'
- 當(dāng)垃圾回收程序檢測到該塊(Block 1000)中存在過期數(shù)據(jù)(PPN=0),準(zhǔn)備將其回收。而數(shù)據(jù)的擦除是以塊為單位的,因此,需要先將 Block 1000 中有效的數(shù)據(jù)拷貝到另外一個空閑塊(Block 2000),然后再將 Block 1000 擦除。
4. 損耗均衡
由于 SSD 存儲單元的擦除次數(shù)是有限的,而擦除的最小單位是塊,所以需要有一個機制來平衡各塊的擦除次數(shù),從而使整個 SSD 的各部分損耗更加均衡,這一機制稱為損耗均衡。通過該技術(shù),存儲數(shù)據(jù)會在不同的塊之間移動,以避免對同一塊的頻繁擦除。
5. 內(nèi)部并行
因為物理限制的存在,異步 NAND 閃存 I/O 總線無法提供32-40 MB/s以上的帶寬。由于一塊 SSD 是由多個存儲芯片組成的,因此我們可以通過并行讀寫多個存儲芯片的方式來提升 SSD I/O 的性能。
SSD 內(nèi)部將不同芯片中的多個塊稱為一個簇(clustered block),一次數(shù)據(jù)寫入可以并行的寫入到簇中的不同塊中。如下圖中,一次數(shù)據(jù)寫入可以拆封成多個并行寫入的任務(wù),寫入到黃色虛線框的簇中。
6. 垃圾回收
上面我們已經(jīng)講到,SSD 中的數(shù)據(jù)是不能被重復(fù)寫的,必須先擦除然后再寫,而擦除的速度非常慢(通常為毫秒級),SSD控制芯片會執(zhí)行垃圾回收操作,即回收使用過的塊,確保后續(xù)寫操作能夠快速分配到可用的塊。
設(shè)計 SSD 友好的程序
針對上述介紹的 SSD 特性,在開發(fā)程序時我們可以做一些針對性的設(shè)計,以提高 SSD 的性能,還有延長 SSD 的使用壽命。
1. 對齊寫入
SSD 數(shù)據(jù)寫入的最小單元是頁,因此,如果我們寫入的數(shù)據(jù)小于頁大小,會有兩個危害:
- 空間浪費:如果想再次利用頁中的空閑部分,必須擦除所處的整個塊
- 讀取效率低:一次讀取讀到的有效數(shù)據(jù)少,讀相同數(shù)據(jù)需要更多次讀操作
對齊寫入指一次數(shù)據(jù)寫入是頁大小的整數(shù)倍,如果數(shù)據(jù)不夠足,可以先緩沖在內(nèi)存中,例如 Twitter 的 fatcache 就是湊夠了1MB 才會寫 SSD。
2. 相關(guān)的數(shù)據(jù)一起寫
相關(guān)的數(shù)據(jù)一起寫指的是對于經(jīng)常被一起訪問的數(shù)據(jù),寫的時候盡量一次同時寫入,這樣做有兩個層面的好處:
- 由于讀寫的最小單位是頁,相關(guān)的數(shù)據(jù)寫在一頁上,數(shù)據(jù)讀的時候效率高
- 由于 SSD 存在內(nèi)部并行的特性,一次將大量(簇大小的整數(shù)倍)相關(guān)的數(shù)據(jù)同時寫入,內(nèi)部會優(yōu)化并行寫入到各個存儲芯片,讀的時候也能并行讀取,可以提升讀的性能
3. 將冷熱數(shù)據(jù)分開寫
將冷熱數(shù)據(jù)分開寫主要是防止由于冷熱數(shù)據(jù)范圍頻率不同而導(dǎo)致的寫入放大,如果將冷熱數(shù)據(jù)寫一起,將會有兩個層面的缺點:
- 如果冷熱數(shù)據(jù)在同一個頁上:數(shù)據(jù)塊小于16KB或不對齊16KB時,更新熱數(shù)據(jù)不得不讀改寫冷數(shù)據(jù)。
- 如果冷熱數(shù)據(jù)在同一個塊上:垃圾回收熱數(shù)據(jù)時,不得不搬移并重寫冷數(shù)據(jù)。
4. 緩存熱數(shù)據(jù)
我們知道 SSD 對于數(shù)據(jù)更新,只能通過擦除-寫入的方式,對于非常高頻變化的數(shù)據(jù),要對數(shù)據(jù)所在塊進行頻繁的擦除-寫入,對整體性能會有較大影響。所以建議對熱數(shù)據(jù)進行程序的緩存,而不是寫入磁盤。
5. 讀寫分離
混合的小讀寫交叉的工作負載會妨礙內(nèi)部緩存和預(yù)讀取機制正常工作,并會導(dǎo)致吞吐量下降。最好的辦法是避免同時發(fā)生的讀操作和寫操作,并將其以一個接一個的大數(shù)據(jù)塊的形式實現(xiàn),數(shù)據(jù)塊大小推薦和簇大小相同。舉個例子,如果要更新1000個文件,你可以遍歷文件逐一讀寫,但會很慢。如果一次讀取1000個文件然后一次寫回1000個文件將會好很多。
6. 避免長時間大數(shù)據(jù)寫入
SSD 內(nèi)部存在異步垃圾回收進程,一般情況下不會影響正常的 I/O,但是如果長時間大數(shù)據(jù)寫入,可能存在垃圾回收速度跟不上寫入速度的情況,導(dǎo)致寫入請求需要等待垃圾回收進程擦除塊,影響性能。
7. 避免就地(in-place)更新優(yōu)化
由于HDD在查找數(shù)據(jù)時又尋道時間,為了避免尋道產(chǎn)生的延遲,應(yīng)用程序常常被優(yōu)化成就地更新。但這個優(yōu)化對于 SSD 并不適用,因為 SSD 沒有尋道時間,且不支持覆蓋寫,更新數(shù)據(jù)必須先擦除,再寫入,所以就地更新反而會造成性能下降,下圖是 SSD 與 HDD 就地更新的性能對比:
8. 對于大的(大于簇的大小)讀寫操作,單線程比多線程好
造成這個現(xiàn)象的原因就是我們上面說到過的 SSD 存在內(nèi)部并行的機制。
- 對于大的寫入,由于內(nèi)部并行機制的存在,單線程也能達到多線程的性能,并且,一個大的單線程寫入比并發(fā)寫入延遲時間更短。因此,在可用的時候,使用單線程大寫入是最好的。
- 對于大的讀取,單線程讀不僅可以依靠內(nèi)部并行機制達到多線程讀同樣的性能,而且可以更好的使用預(yù)讀取機制,因此比多線程并發(fā)讀更優(yōu)。
9. 對于小數(shù)據(jù)的讀寫,多線程比單線程好
如果數(shù)據(jù)太小且沒辦法進行合并后讀寫,就無法使用其內(nèi)部并行機制,因此多線程更優(yōu)。
總結(jié)
本文分析了一些 SSD 的特性,以及針對這些特性,我們應(yīng)該如何設(shè)計更適合于 SSD 的程序。SSD 是提升磁盤性能的利器,目前看也大有替代傳統(tǒng) HDD 的趨勢,因此建議廣大工程師都對其有所了解。當(dāng)然,SSD 目前也在快速的發(fā)展之中,可能有些特性會隨著發(fā)展而改變,希望大家能及時同步最新知識。