1. 程式人生 > >RocketMQ 原始碼分析 訊息儲存(預備知識一)

RocketMQ 原始碼分析 訊息儲存(預備知識一)

前言

看到RocketMQ的效能問題的時候,通常能看到page cache順序IO寫預讀等,要想設計出一個高效能的中介軟體,這部分的知識是絕對要掌握的。

順序IO讀寫為什麼速度更快

當需要從硬碟上讀取一個檔案時,首先會要求磁頭定位到這個檔案的起始扇區。這個定位過程包括兩個步驟: 磁頭定位到對應的磁軌; 主軸馬達帶動碟片轉動到正確的位置。

這個過程所花費的時間被稱為定址時間。也就是說定址時間實際上包含兩部分: 磁頭定位到磁軌的時間為尋道時間; 等待碟片轉動到正確位置的時間稱為旋轉等待時間。

硬碟定址的目的是為了找到將要讀取的檔案的起始扇區,並開始去取資料。這就可以解釋為什麼硬碟上讀取一個

100MB大小的檔案和讀取1000個100KB大小的檔案時間是完全不一樣的現象了:

通常來說一個100MB的檔案是儲存在硬碟上可以連續讀取的扇區上的,也就是說當硬碟需要讀取這個檔案時只需要進行一次定址。 (為什麼說是“通常”呢。因為前提是硬碟上至少要有一端連續空白的扇區,如果此時硬碟上碎片太多可能就找不到這樣的連續空白區域了);

而讀取1000個檔案時,由於這些檔案的起始儲存位不連續,所以每次都要進行定址操作。所花費的時間多很多。

這也就是解釋瞭如果是順序IO為什麼更快,因為對一個檔案的IO可以減少定址時間。

從上面的描述中可以明白RocketMQ中的兩個問題:

  1. 為什麼要將訊息都儲存在一個檔案中,滿1G再建立新的檔案。

    多檔案的起始儲存在磁碟中不連續,讀取訊息會花很多時間,通過對一個檔案追加寫提高IO

  2. 為什麼要講commitlog檔案設計成1G大小的。

    磁碟空間預分配。建立檔案時為檔案分配固定大小的空間,讓檔案儘可能的佔用連續的磁碟扇區,減少後續寫入的磁碟尋道(seek)開銷。

    這裡還有一個原因是:RocketMQ採用MappedByteBuffer這種記憶體對映的方式有幾個限制,其中之一是一次只能對映1.5~2G 的檔案至使用者態的虛擬記憶體,這也是為何RocketMQ預設設定單個CommitLog日誌資料檔案為1G的原因了

page cache

先舉個例子:一位學者一上班開始撰寫論文,邊修改邊寫,如果每次修改一個詞就寫進磁碟中,這個IO開銷太大了。所以這部分論文的資料都儲存在快取記憶體中。就是說這部分的資料不會馬上寫入到磁碟中。這部分快取稱為page cache。

這裡需要提一點cache分成page cache和Buffer cache兩者的區別:

在檔案層面上的資料會快取到page cache。比如你打開了一個檔案,檔案中的內容就會快取到page cache中。

直接對磁碟進行操作的資料會快取到buffer cache中,例如,檔案系統的元資料都會快取到buffer cache中。 比如ls 命令,顯示的一些資料是針對檔案系統,這部分的資料就會快取在buffer cache中。

回寫

還是上面的例子,因為這修改的這部分資料都是儲存在了page cach,如果系統出現故障了,存放在快取記憶體中的資料會隨之消失。

根據LRU演算法,那些經常被訪問的盤塊資料,可能會一直保留在快取記憶體中,長期不會被寫回磁碟。(注意,LRU鏈意味著鏈中任何一元素在被訪問之後,總是又被掛到鏈尾不會被寫回磁碟,只是一直未被訪問的元素,才有可能移到鏈首,而被寫回磁碟)

在Unix系統中,有些方法是通過呼叫SYNC。設定30s強制將快取中的資料寫回磁碟中。RocketMQ也有同步刷盤和非同步刷盤的機制。在後續給解釋。

預讀

當一個檔案被讀取時,在它臨近扇區所儲存的檔案資料也將在近期被讀取。所以硬碟會預先讀取後者到快取中,以便在不久的將來,這些資料被請求讀取時直接從快取中向外部裝置輸出檔案資料。在RocketMQ中就是將資料一些預讀到快取中,讓下次的操作資料時不需要經過定址的過程。提高cache的命中率,提高IO。