天天看點

Kafka高性能之道(下)順序讀寫提升磁盤IO性能PageCache加速消息讀寫零拷貝

順序讀寫提升磁盤IO性能

磁盤有個特性:順序讀寫性能遠好于随機讀寫。

在SSD上,順序讀寫的性能要比随機讀寫快幾倍,如果是機械硬碟,這個差距會達到幾十倍。

os每次從磁盤讀寫資料時,需先尋址,即找到資料在磁盤的實體位置,然後再讀寫資料。

若是機械硬碟,尋址需要較長時間,因為要移動磁頭。順序讀寫相比随機讀寫省去大量尋址時間,隻要尋址一次,就可連續讀寫下去,是以性能比随機讀寫好。

Kafka充分利用磁盤特性。存儲設計非常簡單,對每個分區,它把從Producer收到的消息,順序地寫入對應log檔案,一個檔案寫滿,就開啟新檔案順序寫。

消費時,也是從某個全局位置開始,即某個log檔案的某位置開始,順序讀出消息。

這簡單的設計,充分利用順序讀寫特性,極大提升Kafka在使用磁盤時的IO性能。

PageCache加速消息讀寫

PageCache是os在記憶體中給磁盤的檔案建立的緩存。

無論使用什麼進階語言,在調用系統API讀寫檔案時,并不會直接去讀寫磁盤的檔案,實際操作的都是PageCache,即檔案在記憶體中緩存的副本。

應用程式在寫入檔案時,作業系統會先把資料寫入到記憶體中的PageCache,再一批批寫到磁盤。

讀取檔案的時候,也是從PageCache中來讀取資料,這時候會出現兩種可能情況。

  1. PageCache中有資料,直接讀取,這樣就節省了從磁盤上讀取資料的時間;另一種情況是,PageCache中沒有資料,這時候作業系統會引發一個缺頁中斷,應用程式的讀取線程會被阻塞,作業系統把資料從檔案中複制到PageCache中,然後應用程式再從PageCache中繼續把資料讀出來,這時會真正讀一次磁盤上的檔案,這個讀的過程就會比較慢。

應用程式使用完某塊PageCache後,os并不會立刻清除該PageCache,而是盡可能地利用空閑的實體記憶體儲存這些PageCache,除非系統記憶體不夠用,作業系統才會清理部分PageCache。清理的政策一般是LRU或它的變種算法:優先保留最近一段時間最常使用的那些PageCache。

Kafka在讀寫消息檔案的時候,充分利用了PageCache的特性。一般來說,消息剛剛寫入到服務端就會被消費,按照LRU的“優先清除最近最少使用的頁”這種政策,讀取時候,對于這種剛剛寫入的PageCache,命中的幾率會非常高。

大部分情況下,消費讀消息都會命中PageCache,帶來的好處有:

  • 讀取的速度會非常快
  • 給寫入消息讓出磁盤的IO資源,間接也提升了寫入的性能

零拷貝

Kafka的服務端在消費過程中,還使用了一種“零拷貝”的os特性提升消費的性能。

在服務端,處理消費的大緻邏輯:

  • 首先,從檔案中找到消息資料,讀到記憶體
  • 然後,把消息通過網絡發給用戶端

資料實際上做了2或3次複制:

  1. 從檔案複制資料到PageCache,若命中PageCache,這一步可省
  2. 從PageCache複制到應用程式的記憶體空間,即我們可以操作的對象所在的記憶體
  3. 從應用程式的記憶體空間複制到Socket緩沖區,該過程就是我們調用網絡應用架構API發送資料的過程

Kafka使用零拷貝技術可把這複制次數減少一次,上面的2、3步驟兩次複制合并成一次複制。

直接從PageCache中把資料複制到Socket緩沖區:

  • 不僅減少一次資料複制
  • 由于不用把資料複制到使用者記憶體空間,DMA控制器可直接完成資料複制,無需CPU參與,速度更快。

該零拷貝對應的系統調用:

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);      

前兩個參數分别目的端、源端的檔案描述符

  • 後面兩個參數是源端的偏移量和複制資料的長度
  • 傳回值是實際複制資料的長度    

如果你遇到這種從檔案讀出資料後再通過網絡發送出去的場景,并且這過程中你不需對這些資料處理,那一定要使用零拷貝方法,有效提升性能。

繼續閱讀