爲了追求極致的性能,Kafka掌控這11項要領

不少同窗私信問我Kafka在性能優化方面作了哪些舉措,對於相關問題的答案其實我早就寫過了,就是沒有系統的整理一篇,最近思考着花點時間來整理一下,下次再有同窗問我相關的問題我就能夠瀟灑的甩個連接了。這個問題也是Kafka面試的時候的常見問題,面試官問你這個問題也不算刁難你。在網上也有不少相關的文章開講解這個問題,好比以前各大公衆號轉載的《爲何Kafka這麼快?》,這些文章我看了,寫的不錯,問題在於只是羅列了部分的要領,沒有所有的詳述出來。本文所羅列的要領會比大家網上搜尋到的都多,若是你在看完本篇文章以後,在面試的時候遇到相關問題,相信你必定能讓面試官眼前一亮。面試

PS: 本文章的全部要領在《深刻理解Kafka》一書中都有詳細的描述,若是對相關知識點生疏的話,能夠再去翻看一下。緩存

批量處理

傳統消息中間件的消息發送和消費總體上是針對單條的。對於生產者而言,它先發一條消息,而後broker返回ACK表示已接收,這裏產生2次rpc;對於消費者而言,它先請求接受消息,而後broker返回消息,最後發送ACK表示已消費,這裏產生了3次rpc(有些消息中間件會優化一下,broker返回的時候返回多條消息)。而Kafka採用了批量處理:生產者聚合了一批消息,而後再作2次rpc將消息存入broker,這本來是須要不少次的rpc才能完成的操做。假設須要發送1000條消息,每條消息大小1KB,那麼傳統的消息中間件須要2000次rpc,而Kafka可能會把這1000條消息包裝成1個1MB的消息,採用2次rpc就完成了任務。這一改進舉措一度被認爲是一種「做弊」的行爲,然而在微批次理念盛行的今日,其它消息中間件也開始紛紛效仿。安全

客戶端優化

這裏接着批量處理的概念繼續來講,新版生產者客戶端摒棄了以往的單線程,而採用了雙線程:主線程和Sender線程。主線程負責將消息置入客戶端緩存,Sender線程負責從緩存中發送消息,而這個緩存會聚合多個消息爲一個批次。有些消息中間件會把消息直接扔到broker。性能優化

日誌格式

Kafka從0.8版本開始日誌格式歷經了三次變革:v0、v一、v2。在以前發過的一篇文章《一文看懂Kafka消息格式的演變》中詳細介紹了Kafka日誌格式,Kafka的日誌格式愈來愈利於批量消息的處理,有興趣的同窗能夠閱讀一下這篇文章以做了解。 微信

在這裏插入圖片描述

日誌編碼

若是瞭解了Kafka具體的日誌格式(能夠參考上圖),那麼你應該瞭解日誌(Record,或者稱之爲消息)自己除了基本的key和value以外,還有一些其它的字段,本來這些附加字段按照固定的大小佔用必定的篇幅(參考上圖左),而Kafka最新的版本中採用了變成字段Varints和ZigZag編碼,有效地下降了這些附加字段的佔用大小。日誌(消息)儘量變小了,那麼網絡傳輸的效率也會變高,日誌存盤的效率也會提高,從而整理的性能也會有所提高。網絡

消息壓縮

Kafka支持多種消息壓縮方式(gzip、snappy、lz4)。對消息進行壓縮能夠極大地減小網絡傳輸 量、下降網絡 I/O,從而提升總體的性能。消息壓縮是一種使用時間換空間的優化方式,若是對 時延有必定的要求,則不推薦對消息進行壓縮。app

創建索引,方便快速定位查詢

每一個日誌分段文件對應了兩個索引文件,主要用來提升查找消息的效率,這也是提高性能的一種方式。(具體的內容在書中的第5章有詳細的講解,公衆號裏好像忘記發表了,找了一圈沒找到)分佈式

分區

不少人會忽略掉這個因素,其實分區也是提高性能的一種很是有效的方式,這種方式所帶來的效果會比前面所說的日誌編碼、消息壓縮等更加的明顯。分區在其餘分佈式組件中也有大量涉及,至於爲何分區可以提高性能這種基本知識在這裏就不在贅述了。不過須要注意,一昧地增長分區並不能一直帶來性能的提高,有興趣的同窗能夠看一下這篇《Kafka主題中的分區數越多吞吐量就越高?》。性能

一致性

絕大多數的資料在講述Kafka性能優化的舉措之時是不會說起一致性的東西的。咱們所瞭解的通用的一致性協議如Paxos、Raft、Gossip等,而Kafka另闢蹊徑採用相似PacificA的作法不是「拍大腿」拍出來的,採用這種模型會提高整理的效率。具體的細節後面會整理一篇,相似《在Kafka中使用Raft替換PacificA的可行性分析及優缺點》。優化

順序寫盤

操做系統能夠針對線性讀寫作深層次的優化,好比預讀(read-ahead,提早將一個比較大的磁盤塊讀入內存) 和後寫(write-behind,將不少小的邏輯寫操做合併起來組成一個大的物理寫操做)技術。Kafka 在設計時採用了文件追加的方式來寫入消息,即只能在日誌文件的尾部追加新的消 息,而且也不容許修改已寫入的消息,這種方式屬於典型的順序寫盤的操做,因此就算 Kafka 使用磁盤做爲存儲介質,它所能承載的吞吐量也不容小覷。

頁緩存

爲何Kafka性能這麼高?當遇到這個問題的時候不少人都會想到上面的順序寫盤這一點。其實在順序斜盤前面還有頁緩存(PageCache)這一層的優化。

頁緩存是操做系統實現的一種主要的磁盤緩存,以此用來減小對磁盤 I/O 的操做。具體 來講,就是把磁盤中的數據緩存到內存中,把對磁盤的訪問變爲對內存的訪問。爲了彌補性 能上的差別,現代操做系統愈來愈「激進地」將內存做爲磁盤緩存,甚至會很是樂意將全部 可用的內存用做磁盤緩存,這樣當內存回收時也幾乎沒有性能損失,全部對於磁盤的讀寫也 將經由統一的緩存。

當一個進程準備讀取磁盤上的文件內容時,操做系統會先查看待讀取的數據所在的頁 (page)是否在頁緩存(pagecache)中,若是存在(命中)則直接返回數據,從而避免了對物 理磁盤的 I/O 操做;若是沒有命中,則操做系統會向磁盤發起讀取請求並將讀取的數據頁存入 頁緩存,以後再將數據返回給進程。一樣,若是一個進程須要將數據寫入磁盤,那麼操做系統也會檢測數據對應的頁是否在頁緩存中,若是不存在,則會先在頁緩存中添加相應的頁,最後將數據寫入對應的頁。被修改事後的頁也就變成了髒頁,操做系統會在合適的時間把髒頁中的 數據寫入磁盤,以保持數據的一致性。

對一個進程而言,它會在進程內部緩存處理所需的數據,然而這些數據有可能還緩存在操 做系統的頁緩存中,所以同一份數據有可能被緩存了兩次。而且,除非使用 Direct I/O 的方式, 不然頁緩存很難被禁止。此外,用過 Java 的人通常都知道兩點事實:對象的內存開銷很是大, 一般會是真實數據大小的幾倍甚至更多,空間使用率低下;Java 的垃圾回收會隨着堆內數據的 增多而變得愈來愈慢。基於這些因素,使用文件系統並依賴於頁緩存的作法明顯要優於維護一 個進程內緩存或其餘結構,至少咱們能夠省去了一份進程內部的緩存消耗,同時還能夠經過結構緊湊的字節碼來替代使用對象的方式以節省更多的空間。如此,咱們能夠在 32GB 的機器上使用 28GB 至 30GB 的內存而不用擔憂 GC 所帶來的性能問題。此外,即便 Kafka 服務重啓, 頁緩存仍是會保持有效,然而進程內的緩存卻須要重建。這樣也極大地簡化了代碼邏輯,由於 維護頁緩存和文件之間的一致性交由操做系統來負責,這樣會比進程內維護更加安全有效。

Kafka 中大量使用了頁緩存,這是 Kafka 實現高吞吐的重要因素之一。雖然消息都是先被寫入頁緩存,而後由操做系統負責具體的刷盤任務的。

零拷貝

我在好久以前就以前就發過一篇《什麼是Zero Copy》,若是對Zero Copy不瞭解的同窗能夠翻閱一下。Kafka使用了Zero Copy技術提高了消費的效率。前面所說的Kafka將消息先寫入頁緩存,若是消費者在讀取消息的時候若是在頁緩存中能夠命中,那麼能夠直接從頁緩存中讀取,這樣又節省了一次從磁盤到頁緩存的copy開銷。另外對於讀寫的概念能夠進一步瞭解一下什麼是寫放大和讀放大。

一個磁盤IO流程能夠參考下圖:

在這裏插入圖片描述
具體解析參考《 Linux IO磁盤篇整理小記

寫在最後

本文羅列了一些Kafka的在性能優化方面的要領。本文中的全部內容都在《深刻理解Kafka》一書中有講解,只是散落在各處而已,按照既定的順序編排,力求從易入難。若是在書中再採用篇幅去羅列相似主題的話,會出現知識講解的冗餘,故沒有在書中再次整理贅述,不過這些內容會在公衆號裏發表出來,前面已經按照其它維度整理過好幾篇了。若是須要新的維度內容,能夠在公衆號裏留言,訴求很大的話我會對此整理一篇的,這篇文章就是這麼來的。


歡迎支持筆者小冊:《圖解Kafka之實戰指南》和《圖解Kafka之核心原理


歡迎支持筆者新做:《深刻理解Kafka:核心設計與實踐原理》和《RabbitMQ實戰指南》,同時歡迎關注筆者的微信公衆號:朱小廝的博客。

相關文章
相關標籤/搜索