來源:cnblogs.com/starluke/p/12558952.html
Kafka是大資料領域無處不在的訊息中間件,目前廣泛使用在企業內部的實時資料管道,并幫助企業構建自己的流計算應用程式,
Kafka雖然是基于磁盤做的資料存盤,但卻具有高性能、高吞吐、低延時的特點,其吞吐量動輒幾萬、幾十上百萬,
但是很多使用過Kafka的人,經常會被問到這樣一個問題,Kafka為什么速度快,吞吐量大;大部分被問的人都是一下子就懵了,或者是只知道一些簡單的點,本文就簡單的介紹一下Kafka為什么吞吐量大,速度快,
另外,最近面試整理了 Java 最新、最全的面試題:
https://www.javastack.cn/mst/
一、順序讀寫
眾所周知Kafka是將訊息記錄持久化到本地磁盤中的,一般人會認為磁盤讀寫性能差,可能會對Kafka性能如何保證提出質疑,實際上不管是記憶體還是磁盤,快或慢關鍵在于尋址的方式,磁盤分為順序讀寫與隨機讀寫,記憶體也一樣分為順序讀寫與隨機讀寫,基于磁盤的隨機讀寫確實很慢,但磁盤的順序讀寫性能卻很高,一般而言要高出磁盤隨機讀寫三個數量級,一些情況下磁盤順序讀寫性能甚至要高于記憶體隨機讀寫,
這里給出著名學術期刊 ACM Queue 上的性能對比圖:

磁盤的順序讀寫是磁盤使用模式中最有規律的,并且作業系統也對這種模式做了大量優化,Kafka就是使用了磁盤順序讀寫來提升的性能,Kafka的message是不斷追加到本地磁盤檔案末尾的,而不是隨機的寫入,這使得Kafka寫入吞吐量得到了顯著提升 ,

上圖就展示了Kafka是如何寫入資料的, 每一個Partition其實都是一個檔案 ,收到訊息后Kafka會把資料插入到檔案末尾(虛框部分),
這種方法有一個缺陷—— 沒有辦法洗掉資料 ,所以Kafka是不會洗掉資料的,它會把所有的資料都保留下來,每個消費者(Consumer)對每個Topic都有一個offset用來表示 讀取到了第幾條資料 ,

兩個消費者,Consumer1有兩個offset分別對應Partition0、Partition1(假設每一個Topic一個Partition);Consumer2有一個offset對應Partition2,這個offset是由客戶端SDK負責保存的,Kafka的Broker完全無視這個東西的存在;一般情況下SDK會把它保存到zookeeper里面,(所以需要給Consumer提供zookeeper的地址),
如果不洗掉硬碟肯定會被撐滿,所以Kakfa提供了兩種策略來洗掉資料,一是基于時間,二是基于partition檔案大小,具體配置可以參看它的配置檔案,
二、Page Cache
為了優化讀寫性能,Kafka利用了作業系統本身的Page Cache,就是利用作業系統自身的記憶體而不是JVM空間記憶體,這樣做的好處有:
- 避免Object消耗:如果是使用 Java 堆,Java物件的記憶體消耗比較大,通常是所存盤資料的兩倍甚至更多,
- 避免GC問題:隨著JVM中資料不斷增多,垃圾回收將會變得復雜與緩慢,使用系統快取就不會存在GC問題
相比于使用JVM或in-memory cache等資料結構,利用作業系統的Page Cache更加簡單可靠,
首先,作業系統層面的快取利用率會更高,因為存盤的都是緊湊的位元組結構而不是獨立的物件,
其次,作業系統本身也對于Page Cache做了大量優化,提供了 write-behind、read-ahead以及flush等多種機制,
再者,即使服務行程重啟,系統快取依然不會消失,避免了in-process cache重建快取的程序,
通過作業系統的Page Cache,Kafka的讀寫操作基本上是基于記憶體的,讀寫速度得到了極大的提升,
三、零拷貝
linux作業系統 “零拷貝” 機制使用了sendfile方法, 允許作業系統將資料從Page Cache 直接發送到網路,只需要最后一步的copy操作將資料復制到 NIC 緩沖區, 這樣避免重新復制資料 ,示意圖如下:

通過這種 “零拷貝” 的機制,Page Cache 結合 sendfile 方法,Kafka消費端的性能也大幅提升,這也是為什么有時候消費端在不斷消費資料時,我們并沒有看到磁盤io比較高,此刻正是作業系統快取在提供資料,
當Kafka客戶端從服務器讀取資料時,如果不使用零拷貝技術,那么大致需要經歷這樣的一個程序:
- 作業系統將資料從磁盤上讀入到內核空間的讀緩沖區中,
- 應用程式(也就是Kafka)從內核空間的讀緩沖區將資料拷貝到用戶空間的緩沖區中,
- 應用程式將資料從用戶空間的緩沖區再寫回到內核空間的socket緩沖區中,
- 作業系統將socket緩沖區中的資料拷貝到NIC緩沖區中,然后通過網路發送給客戶端,

從圖中可以看到,資料在內核空間和用戶空間之間穿梭了兩次,那么能否避免這個多余的程序呢?當然可以,Kafka使用了零拷貝技術,也就是直接將資料從內核空間的讀緩沖區直接拷貝到內核空間的socket緩沖區,然后再寫入到NIC緩沖區,避免了在內核空間和用戶空間之間穿梭,

可見,這里的零拷貝并非指一次拷貝都沒有,而是避免了在內核空間和用戶空間之間的拷貝,如果真是一次拷貝都沒有,那么資料發給客戶端就沒了不是?不過,光是省下了這一步就可以帶來性能上的極大提升,
四、磁區分段+索引
Kafka的message是按topic分類存盤的,topic中的資料又是按照一個一個的partition即磁區存盤到不同broker節點,每個partition對應了作業系統上的一個檔案夾,partition實際上又是按照segment分段存盤的,這也非常符合分布式系統磁區分桶的設計思想,
通過這種磁區分段的設計,Kafka的message訊息實際上是分布式存盤在一個一個小的segment中的,每次檔案操作也是直接操作的segment,為了進一步的查詢優化,Kafka又默認為分段后的資料檔案建立了索引檔案,就是檔案系統上的.index檔案,這種磁區分段+索引的設計,不僅提升了資料讀取的效率,同時也提高了資料操作的并行度,
五、批量讀寫
Kafka資料讀寫也是批量的而不是單條的,
除了利用底層的技術外,Kafka還在應用程式層面提供了一些手段來提升性能,最明顯的就是使用批次,在向Kafka寫入資料時,可以啟用批次寫入,這樣可以避免在網路上頻繁傳輸單個訊息帶來的延遲和帶寬開銷,假設網路帶寬為10MB/S,一次性傳輸10MB的訊息比傳輸1KB的訊息10000萬次顯然要快得多,
六、批量壓縮
在很多情況下,系統的瓶頸不是CPU或磁盤,而是網路IO,對于需要在廣域網上的資料中心之間發送訊息的資料流水線尤其如此,進行資料壓碩訓消耗少量的CPU資源,不過對于kafka而言,網路IO更應該需要考慮,
- 如果每個訊息都壓縮,但是壓縮率相對很低,所以Kafka使用了批量壓縮,即將多個訊息一起壓縮而不是單個訊息壓縮
- Kafka允許使用遞回的訊息集合,批量的訊息可以通過壓縮的形式傳輸并且在日志中也可以保持壓縮格式,直到被消費者解壓縮
- Kafka支持多種壓縮協議,包括Gzip和Snappy壓縮協議
Kafka速度的秘訣在于,它把所有的訊息都變成一個批量的檔案,并且進行合理的批量壓縮,減少網路IO損耗,通過mmap提高I/O速度,寫入資料的時候由于單個Partion是末尾添加所以速度最優;讀取資料的時候配合sendfile直接暴力輸出,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
2.勁爆!Java 協程要來了,,,
3.Spring Boot 2.x 教程,太全了!
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501664.html
標籤:Java
