從磁盤原理理解文件讀寫優化

目錄

 

案例背景

磁盤結構

盤面

磁道

柱面

啓停區或着陸區(LandingZone)

扇區

讀寫過程

 磁盤碎片的產生(1) 

希捷硬盤讀寫性能測試報告

 文件碎片

文件碎片定義

產生 原因

文件碎片和連續文件讀取性能對比(100M相同內容的文件)

解決方案

避免隨機讀 寫

隨機讀寫危害

隨機讀寫解決方案

異步刷盤和同步刷盤

同步刷盤

異步刷盤

小文件性能影響及解決方案

小文件性能影響

小文件解決方案

併發寫提高數據入盤性能

廣義併發寫

狹義併發寫

讀寫分離

讀寫分離的必要性

讀寫分離的解決方案

業界優秀案例

K afka的log文件系統

topic中partition存儲分佈

採用優化策略

數據過期刪除及log大小固定策略

小文件合併增大吞吐量


案例背景

目前影響文件讀寫性能的點在於文件碎片,大量小文件,文件刷盤方式,隨即讀寫等。基於上述幾類瓶頸點給於解決方案。以便解決業務線現場大量的語音文件接入。

磁盤結構

硬盤在邏輯上被劃分爲磁道、柱面以及扇區.下圖所示:

 

數據的讀/寫按柱面進行,而不按盤面進行。也就是說,一個磁道寫滿數據後,就在同一柱面的下一個盤面來寫,一個柱面寫滿後,才移到下一個扇區開始寫數據。讀數據也按照這種方式進行,這樣就提高了硬盤的讀/寫效率。

盤面

硬盤的盤片一般用鋁合金材料做基片,高速硬盤也可能用玻璃做基片。硬盤的每一個盤片都有兩個盤面(Side),即上、下盤面,一般每個盤面都會利用,都可以存儲數據,成爲有效盤片,也有極個別的硬盤盤面數爲單數。每一個這樣的有效盤面都有一個盤面號,按順序從上至下從“0”開始依次編號。在硬盤系統中,盤面號又叫磁頭號,因爲每一個有效盤面都有一個對應的讀寫磁頭。硬盤的盤片組在2~14片不等,通常有2~3個盤片,故盤面號(磁頭號)爲0~3或 0~5。

磁道

    磁盤在格式化時被劃分成許多同心圓,這些同心圓軌跡叫做磁道(Track)。磁道從外向內從0開始順序編號。硬盤的每一個盤面有300~1 024個磁道,新式大容量硬盤每面的磁道數更多。信息以脈衝串的形式記錄在這些軌跡中,這些同心圓不是連續記錄數據,而是被劃分成一段段的圓弧,這些圓弧的角速度一樣。由於徑向長度不一樣,所以,線速度也不一樣,外圈的線速度較內圈的線速度大,即同樣的轉速下,外圈在同樣時間段裏,劃過的圓弧長度要比內圈 劃過的圓弧長度大。每段圓弧叫做一個扇區,扇區從“1”開始編號,每個扇區中的數據作爲一個單元同時讀出或寫入。一個標準的3.5寸硬盤盤面通常有幾百到幾千條磁道。磁道是“看”不見的,只是盤面上以特殊形式磁化了的一些磁化區,在磁盤格式化時就已規劃完畢。

柱面

所有盤面上的同一磁道構成一個圓柱,通常稱做柱面(Cylinder),每個圓柱上的磁頭由上而下從“0”開始編號。數據的讀/寫按柱面進行,即磁 頭讀/寫數據時首先在同一柱面內從“0”磁頭開始進行操作,依次向下在同一柱面的不同盤面即磁頭上進行操作,只在同一柱面所有的磁頭全部讀/寫完畢後磁頭 才轉移到下一柱面(同心圓的再往裏的柱面),因爲選取磁頭只需通過電子切換即可,而選取柱面則必須通過機械切換。電子切換相當快,比在機械上磁頭向鄰近磁道移動快得多,所以,數據的讀/寫按柱面進行,而不按盤面進行也就是說,一個磁道寫滿數據後,就在同一柱面的下一個盤面來寫,一個柱面寫滿後,才移到下一個扇區開始寫數據。讀數據也按照這種方式進行,這樣就提高了硬盤的讀/寫效率。一塊硬盤驅動器的圓柱數(或每個盤面的磁道數)既取決於每條磁道的寬窄(同樣,也與磁頭的大小有關),也取決於定位機構所決定的磁道間步距的大小。

啓停區或着陸區(LandingZone)

磁頭靠近主軸接觸的表面,即線速度最小的地方,是一個特殊的區域,它不存放任何數據

扇區

操作系統以扇區(Sector)形式將信息存儲在硬盤上,每個扇區包括512個字節的數據和一些其他信息。一個扇區有兩個主要部分:存儲數據地點的標識符和存儲數據的數據段。

讀寫過程

即一次訪盤請求(讀/寫)完成過程由三個動作組成:
       1)尋道(時間):磁頭移動定位到指定磁道 ,經驗值在3~15ms
       2)旋轉延遲(時間):等待指定扇區從磁頭下旋轉經過 60*1000/7200/2 = 4.17ms(7200rpm硬盤
       3)數據傳輸(時間):數據在磁盤與內存之間的實際傳輸(200M/S,希捷4TB硬盤

因此在磁盤上讀取扇區數據(一塊數據)所需時間:

      Ti/o=tseek +tla + n *twm

其中:

tseek 爲尋道時間

tla爲旋轉時間

twm 爲傳輸時間

 磁盤碎片的產生(1) 

俗話說一圖勝千言,先用一張ACSII碼圖來解釋爲什麼會產生磁盤碎片。

                                        

這裏所說的方法二就像是我們的windows系統的存儲方式,每個文件都是緊挨着的,但如果其中某個文件要更改的話,那麼就意味着接下來的數據將會被放在磁盤其他的空餘的地方。
如果這個文件被刪除了,那麼就會在系統中留下空格,久而久之,我們的文件系統就會變得支離破碎,碎片就是這麼產生的。

Linux文件碎片基本很少,Linux的ext2,ext3,ext4文件系統——ext4是Ubuntu和目前大多發行版所採用的文件系統——會以一種更加智能的方式來放置文件。Linux的文件系統會將文件分散在整個磁盤,在文件之間留有大量的自由空間,而不是像Windows那樣將文件一個接一個的放置。當一個文件被編輯了並且變大了,一般都會有足夠的自由空間來保存文件。如果碎片真的產生了,文件系統就會嘗試在日常使用中將文件移動來減少碎片,所以不需要專門的碎片整理程序。

希捷硬盤讀寫性能測試報告

硬盤規格:希捷新酷魚2TB硬盤(3碟版)的HD Tune讀取

                             

上述出現原因從磁盤結構上看,角速度一致,線速度由於半徑越來越小,導致讀寫速度下降。

 文件碎片

文件碎片定義

文件碎片是因爲文件被分散保存到整個磁盤的不同地方,而不是連續地保存在磁盤連續的中形成的

產生 原因

(1)在文件操作過程中,Windows系統可能會調用虛擬內存來同步管理程序,這樣就會導致各個程序對硬盤頻繁讀寫,從而產生文件碎片。

(2)還有一種情況就是當中間的一個扇區內容被刪除後,新寫入一個較小的文件,這樣在這個文件兩邊就會出現一些空間,這時候再寫入一個文件,兩段空間的任意一部分都不能容納該文件,這時候就需要將文件分割成兩個部分,碎片再次產生了。

(3)最常見的就是下載電影之類的大文件,這期間大家一般都會處理一下其它事情,而下載下來的電影文件被迫分割成若干個碎片存儲於硬盤中。因此下載是產生碎片的一個重要源頭。還有就是經常刪除、添加文件,這時候如果文件空間不夠大,就會產生大量的文件碎片,隨着文件的刪改頻繁,這種情況會日益嚴重。

文件碎片和連續文件讀取性能對比(100M相同內容的文件)

                                    

從上圖看出,在採用nio讀取文件的時候,buffer的大小設置爲512kb,讀取性能最高,文件碎片讀取爲80ms,連續文件讀取60ms,得知,文件碎片嚴重影響讀寫性能。

解決方案

在我們目前開發的文件存儲系統,由於各種原因,很難避免文件碎片的產生。基於迅雷下載思想的啓發,我們在從小文件讀取數據寫入大文件的時候,可以事先分配好大文件的尺寸,佔據固定大小的連續的磁盤分區。這樣在採用修改的方式,把每個字節修改成指定的數據,會得到連續完整的文件,避免磁盤碎片的產生。

分配尺寸方式:RandomAccessFile raf = new RandomAccessFile(file, “rw”);

              Raf.setLength(1024*1024*1024);

這樣可以分配1G大小的文件,經測試1ms左右的時間可以完成上述分配。

避免隨機 

隨機讀寫危害

由磁盤結構一節可知,數據寫入和讀取時間由尋址,磁頭旋轉,數據傳輸組成,如果隨即讀寫的話,每一次數據讀寫時間=尋址(12ms)+磁頭旋轉(3ms)+數據傳輸(1ms,小文件),如果是順序讀寫時間=數據傳輸。

隨機讀寫解決方案

磁盤分區循環寫入數據,先將一塊磁盤分成3個分區,1分區寫完,寫2分區,2分區寫完,寫3分區,3分區寫完,將1分區快速格式化,然後將數據寫入1分區。

                                             

異步刷盤和同步刷盤

同步刷盤

採用零拷貝的方式,採用nio的強制刷盤策略,等待文件真正落盤後,纔會繼續代碼流程,小文件性能大概在2~3M/s左右。

異步刷盤

javaIo的普通刷盤方式,就是一種異步刷盤,當調用文件IO的flush的方式時,其實是向內核發送一條刷盤指令,直接返回。小文件性能在13M/S左右。

小文件性能影響及解決方案

小文件性能影響

文件讀寫時間=尋址+旋轉延遲+數據傳輸,小文件讀寫的性能瓶頸在尋址上。

小文件200kb:尋址時間=12ms

              旋轉延遲=3ms

              數據傳輸=1ms

上述讀寫性能在0.2M * (1/0.016)=12.5M/S,IOPS=62次/s

大文件 20GB:  尋址時間=12ms

              旋轉延遲=3ms

              數據傳輸=20*1024/200=102400ms

上述讀寫性能在 20*1024M *(1/102415) = 200M/s,基本可以發揮文件讀寫極致,瓶頸在數據傳輸上,這是我們想要的效果。

小文件解決方案

將小文件合併成大文件進行存儲,這樣讀取性能就會大大提高,目前kafka的log存儲和讀取原理採用的就是此種方式。

                                      

數據塊定義:起始字符,版本標識,chunkSize,subChunkId, subChunkSize,Data,結束標記。

存儲:存儲小文件的時候,首先抽取一個可存入大文件路徑,   封裝小文件的元數據和數據信息,追加到大文件的尾部。返回一個加密後的ID,這個id包含小文件的位置和基本信息。

讀取:讀取根據id進行讀取,也可遍歷讀取。

刪除:在元數據中加標記,不真正刪除文件,避免文件碎片,   後期統一格式化處理。

併發寫提高數據入性能

廣義併發寫

一塊磁盤啓用多個線程進行寫數據,根據硬盤知識可知,一塊硬盤有很多磁頭,但是隻有一個有效磁頭在運作,多個線程會造成磁頭爭奪,導致寫入性能下降。

狹義併發寫

 一臺linux機器可以多塊硬盤,比如4塊,爲了避免爭奪磁頭,每塊硬盤最多隻能由一個線程操作,這樣就可以同時寫入4個數據分別落到不同的硬盤中,實現方式架構如下圖:

                                                    

Queue:數據硬盤目錄,四塊硬盤,四個目錄,阻塞隊列

線程池:根據硬盤數量分配線程池線程數量,四塊硬盤,四個線程。

流程:當數據準備入庫,獲取線程,線程從隊列中請求入庫硬盤,根據請求地址入庫。

讀寫分離

讀寫分離的必要性

 根據硬盤知識可知,一塊硬盤有很多磁頭,但是隻有一個有效磁頭在運作,如果同時有數據進行讀和寫操作,就會造成磁頭爭奪,導致性能下降。

讀寫分離的解決方案

目前同一進程讀寫分離可以實現,不同進程的讀寫分離無法實現。

業界優秀案例

K afka的log文件系統

topic中partition存儲分佈

Kafka集羣只有一個broker,xxx/message-folder爲數據文件存儲根目錄,在Kafka broker中server.properties文件配置(參數log.dirs=xxx/message-folder),例如創建2個topic名 稱分別爲report_push、launch_info, partitions數量都爲partitions=4

存儲路徑和目錄規則爲:

xxx/message-folder

  |--report_push-0
  |--report_push-1
  |--report_push-2
  |--report_push-3
  |--launch_info-0
  |--launch_info-1
  |--launch_info-2
  |--launch_info-3

 

在Kafka文件存儲中,同一個topic下有多個不同partition,每個partition爲一個目錄,partiton命名規則爲topic名稱+有序序號,第一個partiton序號從0開始,序號最大值爲partitions數量減1。

採用優化策略

kafka是一個高吞吐量分佈式消息系統,並且提供了持久化。其高性能的有兩個重要特點:1、併發,2、連續讀寫性能遠高於隨機讀寫。

優化着重於併發策略,下面舉兩個案例說明:

  1. 一個topic分成多個patition,磁盤總數是一塊,這樣多個分區會往同一塊磁盤寫入數據,造成廣義併發寫,磁頭爭奪,性能急劇下降。

  2. 一個topic分成多個patition,磁盤總數是多塊,這樣每一個分區會寫一塊硬盤,造成狹義併發寫,性能得到保障。

數據過期刪除及log大小固定策略

Kafka並未提供刪除數據的api,可以設置過期時間,kafka根據過期時間自動刪除。

Kafka的log大小固定,所以一塊盤中的log數據基本上都是一樣大,根據文件寫入規則,基本上不會產生文件碎片。

小文件合併增大吞吐

衆所周知,kafka 是一個消息中間件,存儲採用固定大小的log系統,當我們將小文件數據或者是一條條日誌存儲到kafka中,都會被寫到指定的log文件,這樣小文件就會被組合成大文件。

每個log entry格式爲"4個字節的數字N表示消息的長度" + "N個字節的消息內容";每個日誌都有一個offset來唯一的標記一條消息,offset的值爲8個字節的數字,表示此消息在此partition中所處的起始位置..每個partition在物理存儲層面,有多個log file組成(稱爲segment).segment file的命名爲"最小offset".kafka.例如"00000000000.kafka";其中"最小offset"表示此segment中起始消息的offset.

獲取消息時,需要指定offset和最大chunk尺寸,offset用來表示消息的起始位置,chunk size用來表示最大獲取消息的總長度(間接的表示消息的條數).根據offset,可以找到此消息所在segment文件,然後根據segment的最小offset取差值,得到它在file中的相對位置,直接讀取輸出即可。

                                   

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章