深入浅出kafka原理-2-Kafka为何那么快(高效)
目錄
前言:Kafka為何那么快(高效)
1.文件系統
2.降低時間復雜度
3.零拷貝
4.下一節預告
前言:Kafka為何那么快(高效)
- 利用磁盤順序寫的優勢
- 預讀取后寫入
- 盡量避免使用 in-memory cache
- 將消息打包降低大量小型IO操作的影響
- 零拷貝(基于mmap的索引和日志讀寫用到的TransportLayer)
1.文件系統
????????Kafka 對消息的存儲和緩存嚴重依賴于文件系統。人們對于“磁盤速度慢”具有普遍印象,事實上,磁盤的速度比人們預期的要慢的多,也快得多,這取決于人們使用磁盤的方式。
使用6個7200rpm、SATA接口、RAID-5的磁盤陣列在JBOD配置下的順序寫入的性能約為600MB/秒,但隨機寫入的性能僅約為100k/秒,相差6000倍以上。
線性的讀取和寫入是磁盤使用模式中最有規律的,并且由操作系統進行了大量的優化。
-
read-ahead 是以大的 data block 為單位預先讀取數據
-
write-behind 是將多個小型的邏輯寫合并成一次大型的物理磁盤寫入
????????關于該問題的進一步討論可以參考 ACM Queue article,他們發現實際上順序磁盤訪問在某些情況下比隨機內存訪問還要快!
????????為了彌補這種性能差異,現代操作系統主動將所有空閑內存用作 disk caching(磁盤高速緩存),所有對磁盤的讀寫操作都會通過這個統一的 cache( in-process cache)。
????????即使進程維護了 in-process cache,該數據也可能會被復制到操作系統的 pagecache 中,事實上所有內容都被存儲了兩份。
此外,Kafka 建立在 JVM 之上,任何了解 Java 內存使用的人都知道兩點:
對象的內存開銷非常高,通常是所存儲的數據的兩倍(甚至更多)。
隨著堆中數據的增加,Java 的垃圾回收變得越來越復雜和緩慢。
????????kafka選擇了一個非常簡單的設計:相比于維護盡可能多的 in-memory cache,并且在空間不足的時候匆忙將數據 flush 到文件系統,我們把這個過程倒過來。所有數據一開始就被寫入到文件系統的持久化日志中,而不用在 cache 空間不足的時候 flush 到磁盤。實際上,這表明數據被轉移到了內核的 pagecache 中。
Pagecache頁面緩存
-
Page cache(頁面緩存)
Page cache 也叫頁緩沖或文件緩沖,是由好幾個磁盤塊構成,大小通常為4k,在64位系統上為8k,構成的幾個磁盤塊在物理磁盤上不一定連續,文件的組織單位為一頁, 也就是一個page cache大小,文件讀取是由外存上不連續的幾個磁盤塊,到buffer cache,然后組成page cache,然后供給應用程序。
-
Buffer cache(塊緩存)
Buffer cache 也叫塊緩沖,是對物理磁盤上的一個磁盤塊進行的緩沖,其大小為通常為1k,磁盤塊也是磁盤的組織單位。設立buffer cache的目的是為在程序多次訪問同一磁盤塊時,減少訪問時間。
-
Page cache(頁面緩存)與Buffer cache(塊緩存)的區別
磁盤的操作有邏輯級(文件系統)和物理級(磁盤塊),這兩種Cache就是分別緩存邏輯和物理級數據的。
我們通過文件系統操作文件,那么文件將被緩存到Page Cache,如果需要刷新文件的時候,Page Cache將交給Buffer Cache去完成,因為Buffer Cache就是緩存磁盤塊的。
簡單說來,page cache用來緩存文件數據,buffer cache用來緩存磁盤數據。在有文件系統的情況下,對文件操作,那么數據會緩存到page cache,如果直接采用dd等工具對磁盤進行讀寫,那么數據會緩存到buffer cache。
Buffer(Buffer Cache)以塊形式緩沖了塊設備的操作,定時或手動的同步到硬盤,它是為了緩沖寫操作然后一次性將很多改動寫入硬盤,避免頻繁寫硬盤,提高寫入效率。
Cache(Page Cache)以頁面形式緩存了文件系統的文件,給需要使用的程序讀取,它是為了給讀操作提供緩沖,避免頻繁讀硬盤,提高讀取效率。
2.降低時間復雜度
????????消息系統使用的持久化數據結構通常是和 BTree 相關聯的消費者隊列或者其他用于存儲消息源數據的通用隨機訪問數據結構。BTree 的操作復雜度是 O(log N),通常我們認為 O(log N) 基本等同于常數時間,但這條在磁盤操作中不成立。
????????存儲系統將非常快的cache操作和非常慢的物理磁盤操作混合在一起,當數據隨著 fixed cache 增加時,可以看到樹的性能通常是非線性的——比如數據翻倍時性能下降不只兩倍。
????????kafka選擇把持久化隊列建立在簡單的讀取和向文件后追加兩種操作之上,這和日志解決方案相同。這種架構的優點在于所有的操作復雜度都是O(1),而且讀操作不會阻塞寫操作,讀操作之間也不會互相影響。
????????在不產生任何性能損失的情況下能夠訪問幾乎無限的硬盤空間,Kafka 可以讓消息保留相對較長的一段時間(比如一周),而不是試圖在被消費后立即刪除。
????????降低大量小型IO操作的影響
????????小型的 I/O 操作發生在客戶端和服務端之間以及服務端自身的持久化操作中。
????????為了避免這種情況,kafka的協議是建立在一個 “消息塊” 的抽象基礎上,合理將消息分組。將多個消息打包成一組,而不是每次發送一條消息,從而使整組消息分擔網絡中往返的開銷。
????????這個簡單的優化對速度有著數量級的提升。批處理允許更大的網絡數據包,更大的順序讀寫磁盤操作,連續的內存塊等等,所有這些都使 KafKa 將隨機流消息順序寫入到磁盤, 再由 consumers 進行消費。
3.零拷貝
????????字節拷貝是低效率的操作,在消息量少的時候沒啥問題,但是在高負載的情況下,影響就不容忽視。為了避免這種情況,kafka使用 producer ,broker 和 consumer 都共享的標準化的二進制消息格式,這樣數據塊不用修改就能在他們之間傳遞。
保持這種通用格式可以對一些很重要的操作進行優化: 持久化日志塊的網絡傳輸。現代的unix 操作系統提供了一個高度優化的編碼方式,用于將數據從 pagecache 轉移到 socket 網絡連接中;在 Linux 中系統調用 sendfile 做到這一點。
-
傳統IO?(4次上下文切換4次拷貝)
假如將磁盤上的文件讀取出來,然后通過網絡協議發送給客戶端。
一般需要兩個系統調用,但是一共4次上下文切換,4次拷貝
read(file, tmp_buf, len); write(socket, tmp_buf, len);
-
要想提高文件傳輸的性能,就需要減少「用戶態與內核態的上下文切換」和「內存拷貝」的次數。
-
mmap(4次上下文切換3次拷貝)
mmap()系統調用函數會直接把內核緩沖區里的數據「映射」到用戶空間,這樣,操作系統內核與用戶空間就不需要再進行任何的數據拷貝操作,它替換了read()系統調用函數。
buf = mmap(file, len); write(sockfd, buf, len);
-
sendfile(2次上下文切換3次拷貝)
Linux 內核版本 2.1 中,提供了一個專門發送文件的系統調用函數 sendfile()
首先,它可以替代前面的 read()和 write()這兩個系統調用,這樣就可以減少一次系統調用,也就減少了 2 次上下文切換的開銷。
其次,該系統調用,可以直接把內核緩沖區里的數據拷貝到 socket 緩沖區里,不再拷貝到用戶態,這樣就只有 2 次上下文切換,和 3 次數據拷貝。
#include <sys/socket.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);它的前兩個參數分別是目的端和源端的文件描述符,后面兩個參數是源端的偏移量和復制數據的長度,返回值是實際復制數據的長度。
-
零拷貝(2次上下文切換2次拷貝)
Linux 內核 2.4 版本開始起,對于支持網卡支持 SG-DMA 技術的情況下, sendfile() 系統調用的過程發生了點變化,具體過程如下:
-
-
第一步,通過 DMA 將磁盤上的數據拷貝到內核緩沖區里;
-
第二步,緩沖區描述符和數據長度傳到 socket 緩沖區,這樣網卡的 SG-DMA 控制器就可以直接將內核緩存中的數據拷貝到網卡的緩沖區里,此過程不需要將數據從操作系統內核緩沖區拷貝到 socket 緩沖區中,這樣就減少了一次數據拷貝;
-
4.下一節預告
- kafka高效文件存儲設計特點
推薦閱讀
- 深入淺出kafka原理-1-初識只作乍見之歡
- 深入淺出kafka原理-2-Kafka為何那么快(高效)
- 深入淺出kafka原理-3-高效文件存儲設計特點
- 深入淺出kafka原理-4-kafka網絡機制原理
總結
以上是生活随笔為你收集整理的深入浅出kafka原理-2-Kafka为何那么快(高效)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 土堆Pytorch学习笔记(三)
- 下一篇: Joint Event and Temp