“磁盤”這個詞,對于程序員來說并不陌生,我們知道它是一種存儲介質,主要用來存儲數據的,可以說常用的中間件基本上都離不開它,比如我們常用的MySQL數據庫、kafka消息引擎,甚至redis緩存都離不開磁盤。
我們在優化某個業務邏輯的時候,經常需要用到緩存,盡量讓熱數據都從緩存里讀取,因為我們知道磁盤是緩慢的,特別在高并發的場景下,我們要保證極少的請求走磁盤IO。不知道你有沒有思考過以下問題:
機械硬盤為什么慢?機械硬盤有多慢?kafka也是寫磁盤的,它卻挺快的,為什么?SSD為什么比普通的機械磁盤要快?既然SSD這么快,那為什么不拋棄傳統的機械磁盤?帶著這些疑問,我們一起來看看磁盤相關的知識。
從機械硬盤開始這是一塊普通機械硬盤的內部結構,它的組成并不多,我們重點關注磁盤、磁頭臂、磁頭就行。
先說磁盤,它的樣子就像光盤,我們的數據就是存在它里面的,我們其實一般稱它為盤片,盤片的表面涂有磁性的記錄材料,注意這里不是只有一面可以存數據,盤片的兩面都可以存,同時對于一塊磁盤來說,通常它是由多個盤片組成的,因此的它組成應該是這樣的。
圖中一共有4個盤片,一共8個盤面,其中每個盤面都被畫成了一圈一圈的同心圓。
這樣的一圈一圈的橙色圓我們把它叫做磁道,當然磁道也是由一個個弧段組成的。
像如圖的綠色弧段部分我們叫做扇區,扇區是磁盤組成的最小單位,一般一個扇區可以存儲512個字節。
多個盤片相同的磁道,可以組成一個虛擬的圓柱,這個虛擬的圓柱面,我們叫做柱面。
對于一個盤面來說,它的存儲容量=單個扇區的大小 * 磁道的扇區數 * 磁道數。
對于一個盤片來說,它有兩個面所以一個盤片的容量=2 * 盤面容量。
對于一個磁盤來說,磁盤的容量=盤片的數量 * 單盤片的容量。
說完了盤片,我們來說說磁頭,我們的數據是在盤片上的,盤片上的數據并不能直接傳輸到總線上,因此需要一個媒介,這個媒介就是磁頭,磁頭可以把某個扇區的數據傳輸到總線上。
說完了磁頭,接下來就是磁臂了,磁頭解決了數據讀寫的問題,但是沒有解決讀寫哪個扇區的問題,這時候就需要磁臂,磁臂可以在一定范圍內擺動,來找到目標扇區。
當然磁臂的擺動范圍有限,比如磁臂無論怎么擺動也無法擺動到扇區B處,這時就該轉動盤片了,你應該聽過磁盤的轉速,比如7200轉/分,這個其實就是轉軸來帶動盤片的轉動的速度,因此最終通過盤片的轉動+磁臂的擺動,就可以定位到我們的目標扇區。
機械硬盤有多快搞懂了機械硬盤的物理結構之后,我們接下來看看它有多快。我們知道定位到一條數據需要盤片的轉動,還需要磁臂的擺動,這些都是物理的,當我們的磁頭定位到具體的扇區之后,讀寫數據的速度是很快的,因此影響機械硬盤讀寫速度的主要原因就是這兩個物理運動,這兩個物理運動對應兩個專業名詞叫做 平均延時 和 平均尋道時間。
我們先說平均延時,我們上面說到當一個目標扇區不在磁臂的擺動范圍之內時,就要轉動盤片,以7200轉/min的磁盤為例,它每秒可以轉動120圈,轉一圈就是1/120s=8.33ms左右,那我們定位目標區域的平均耗時是多少呢?以一個磁道為例,它是圓形的,目標節點可以分布在圓上的任何位置。
比如當我們要找A的時候,可能只需要轉一點點,找B的時候,可能要轉半圈,找C的時候可能要轉將近一圈。所以根據算術平均法可以大致判斷平均找到一個目標節點需要轉動半圈,這個半圈的時間就是8.33/2=4.17ms,也就是平均延時。
我們再來看看平均尋道時間,通過盤面的轉動我們大致找到了目標區域,但是還沒精確定位到,這時候需要磁臂的擺動去定位我們具體的目標扇區,這個擺動的耗時一般是4-10ms。
因此磁盤隨機IO大概的耗時就是4.17+4=8.17 ~ 4.17+10=14.14ms,這個數字代表了什么?我們取個折中,假設隨機IO的耗時是10ms,那么1s可以做100次隨機IO,看到100這個數字,你是不是明白了什么~,這個是真的小,這也是為什么我們對于QPS較高的接口,都要加個緩存層,因為磁盤扛不住啊。
這里科普下,我們上面說的1s內存能做100次隨機IO,這個100我們叫做IOPS,即每秒的輸入輸出量(或讀寫次數),是衡量磁盤性能的關鍵指標
我們可以通過iostat,來查看當前機器磁盤的指標:
iostatKB/ttpsMB/sus sy id 1m 5m 15m23.4490.20128 802.40 1.97 1.90
其中tps就是我們當前磁盤的每秒傳輸次數,當這個數值很大時需要注意。
當然以上都是隨機IO,順序IO就大大不一樣了,順序IO的速度堪比內存的離散讀寫,總之很快,像大名鼎鼎的kafka就是磁盤順序IO,所以至少在磁盤讀寫這塊它的性能還不錯。順序IO之所以快,首先盤片不需要每次轉動了,其次我們的磁臂也不需要大幅度的擺動去尋道了,因此節省了大量的物理耗時,速度和隨機IO之間應該是數量級的差異。
更加快速的固態硬盤先說個數字,我們日常用的機械硬盤的數據傳輸率差不多在200MB/s左右,而固態硬盤的傳輸率差不多在768MB/s,可以發現固態硬盤比普通機械硬盤快了不少,然而這只是在接口是SATA3.0的情況下,我們的固態硬盤還支持PCI Express接口,在這個接口下,固態硬盤的讀寫能力還會上一個等級,可以達到1GB/s以上,當然這些只是科普知識,作為程序員的我們不用太在意接口相關的知識。
我們還是先從原理開始講起,固態硬盤的運作方式和機械磁盤一點都不一樣,通過上圖的內部結構可以看出,固態硬盤并沒有盤片、磁臂等機械部件。
那么它是如何存儲數據的呢?答案是電容,電容是非常小的電子元件,我們只需要給電容充上電,那么就可以表示比特位1,給電容放電就可以表示比特位0,采用這樣方式存儲數據的固體硬盤,我們一般稱之為使用了SLC的顆粒,全稱是 Single-Level Cell,也就是一個存儲單元中只有一位數據,那么一塊固態硬盤能存儲多少數據就完全取決于能放多少電容,因此后來有的工程師發明了更高級的用法,即讓一個電容里能放下2個比特位、3個比特位、甚至4個比特位。
那么問題來了一個電容如何表示這個多的比特位?答案是電壓,給電容充電的玩意叫做電壓計,以能放兩位比特位的電容為例,它可以表示00、01、10、11 4個數字,放電狀態是00,其余我們只需要充上不同的電壓即可。
當然想要表示的數字越多,就得充很多不同的電壓,因此速度就會相對慢些。
短命的固態硬盤搞懂了固態硬盤的內部結構之后,我們來看看固態硬盤的讀寫原理,看看為什么固態硬盤的壽命不高。
相比機械硬盤存儲數據的盤片,固態硬盤的叫做裸片,裸片之間也是疊放在一起的,以一個裸片為例,它的結構大致如下:
還是劃分的概念,首先一張裸片上可以放多個平面,一般一個平面上的存儲容量大概是GB級別,然后一個平面上可以分成很多塊,一般一個塊的存儲大小,通常幾百KB到幾MB大小,一個塊里面再就是分很多的頁,一個頁的大小通常是4KB,我們著重關注下塊和頁,這和我們接下來要說的固態硬盤的壽命息息相關。
我們知道對于機械硬盤來說,我們寫入數據的時候,不會在乎要寫入的扇區是否已經有數據,直接覆蓋就行了,但是固態硬盤就不一樣了,如果某塊要寫的區域已經有數據了,必須要先擦除,然后才能寫入。
這個擦除很關鍵,因為它就是影響固定硬盤壽命的直接原因,擦的越多壽命就會越來越短,它就像你用一個橡皮擦在一張紙上擦拭一樣,擦的多了,紙張也就通了,紙張通了,紙就不能用了。
那么一塊固態硬盤可以擦除多少次呢?以單比特電容的模式來說,它大概可以擦除10w次,其他的多比特位的更少,可能只有幾千次。因此如果的你業務數據需要經常更新,不太建議使用固態硬盤。
關于擦除數據,還有一點很重要,那就是它是以塊為單位的,通過上圖我們知道數據存儲的最小單位其實是頁,頁屬于塊,一個塊上有很多的頁,如果一個塊上的某些頁的數據被標記刪除了,此時也不能直接擦除這些單獨的頁,因此這些頁就無法被復用,除非整個塊上的頁都被標記刪除了。
如圖,頁A、頁B、頁C雖然都是被標記刪除的數據,但是因為它所屬的塊還有其它有效數據,所以當有新數據要寫入的時候,它只能寫入白色區域未使用的頁,并不能利用這些紅色區域。但是這樣的話,就會出現這樣一個問題:隨著時間的推移,紅色區域會越來越多,也就是碎片越來越多,這樣勢必會造成浪費。
浪費可恥,因此需要一套緊湊機制,這時候會把相關塊的有效數據移動到一個新的塊中,讓不同塊的有效數據更加緊湊的分布,那么對于被移動出數據的塊來說,它上面的頁要么沒數據,要么是標記刪除的數據,可以直接對這個塊進行擦除,從而達到回收利用的目的。
回到題目通過對機械硬盤和固態硬盤的了解,我們再來看看一開始的問題:
機械硬盤為什么慢?機械磁盤慢的原因主要是因為定位一條數據需要盤片的轉動 + 磁臂的擺動,這些都是物理的,所以會慢機械硬盤有多慢?,通過上面的計算,我們可以大概得出對于一個7200轉的機械硬盤來說,它的iops大概在100左右,每次io的耗時在10ms左右kafka也是寫磁盤的,它卻挺快的,為什么? 因為kafka是順序io,就算對于機械硬盤來說,順序io也是很快的,因為它不會像離散io那樣,需要過多的尋道。SSD為什么比普通的機械硬盤要快?這里主要因為SSD不需要像機械硬盤那樣的物理運動來尋道。既然SSD這么快,那為什么不拋棄傳統的機械硬盤?首先從價格上來講,固態硬盤價格稍貴,其次固態硬盤的壽命沒有機械硬盤的高。