關於kafka的性能方面的一些總結,內容大部分來自網上,加上一些自己的經驗(加粗),主要是為了方便自己查閱 ...
Kafka消息的存儲
Kafka的設計基於一種非常簡單的指導思想:不是要在記憶體中保存儘可能多的數據,在需要時將這些數據刷新(flush)到文件系統,而是要做完全相反的事情。所有數據都要立即寫入文件系統中持久化的日誌中,但不進行刷新數據的任何調用。實際中這樣做意味著,數據被傳輸到OS內核的頁面緩存中了,OS隨後會將這些數據刷新到磁碟。
大家普遍為“磁碟很慢”,因而人們都對持久化(persistent structure)結構能夠提供說得過去的性能抱有懷疑態度。實際上,同人們的期望值相比,磁碟可以說是既很慢又很快,這取決決於磁碟的使用方式。設計的很好的磁碟結構可以和網路一樣快。在一個由6個7200rpm的SATA硬碟組成的RAID-5磁碟陣列上,線性寫入(linear write)的速度大約是600MB/秒,但隨機寫入卻只有100k/秒,其中的差距接近6000倍。
Kafka並沒有在記憶體中創建緩衝區,然後再向磁碟write的方法,而是直接使用了PageCache。
OS在文件系統的讀寫上已經做了太多的優化,PageCache就是其中最重要的一種方法.
- 直接使用PageCache有如下幾個好處:
- 減少記憶體開銷: Java對象的記憶體開銷(overhead)非常大,往往是對象中存儲的數據所占記憶體的兩倍以上。
- 避免GC問題:Java中的記憶體垃圾回收會隨著堆內數據不斷增長而變得越來越不明確,回收所花費的代價也會越來越大。
- 簡單可靠:OS會調用所有的空閑記憶體作為PageCache,併在其上做了大量的優化:預讀,後寫,flush管理等,這些都不用應用層操心,而是由OS自動完成。
由於這些因素,使用文件系統並依賴於PageCache頁面緩存要優於自己在記憶體中維護一個緩存或者什麼其他別的結構。
讀寫空中接力
當寫操作發生時,它只是將數據寫入Page Cache中,並將該頁置上dirty標誌。
當讀操作發生時,它會首先在Page Cache中查找內容,如果有就直接返回了,沒有的話就會從磁碟讀取文件再寫回Page Cache。
可見,只要生產者與消費者的速度相差不大,消費者會直接讀取之前生產者寫入Page Cache的數據,大家在記憶體里完成接力,根本沒有磁碟訪問。而比起在記憶體中維護一份消息數據的傳統做法,這既不會重覆浪費一倍的記憶體,Page Cache又不需要GC(可以放心使用大把記憶體了),而且即使Kafka重啟了,Page Cache還依然在。
相關內核參數
不能及時flush的話,OS crash(不是應用crash) 可能引起數據丟失;
內核線程pdflush負責將有dirty標記的頁面,發送給IO調度層。內核會為每個磁碟起一條pdflush線程,每5秒(/proc/sys/vm/dirty_writeback_centisecs)喚醒一次,根據下麵三個參數來決定行為:
/proc/sys/vm/dirty_expire_centiseconds
:如果page dirty的時間超過了30秒(單位是10ms),就會被刷到磁碟,所以crash時最多丟30秒左右的數據。/proc/sys/vm/dirty_background_ratio
:如果dirty page的總大小已經超過了10%的可用記憶體(cat /proc/meminfo里 MemFree+ Cached - Mapped),則會在後臺啟動pdflush 線程寫盤,但不影響當前的write(2)操作。增減這個值是最主要的flush策略里調優手段。/proc/sys/vm/dirty_ratio
:如果wrte(2)的速度太快,比pdflush還快,dirty page 迅速漲到 10%的總記憶體(cat /proc/meminfo里的MemTotal),則此時所有應用的寫操作都會被block,各自在自己的時間片里去執行flush,因為操作系統認為現在已經來不及寫盤了,如果crash會丟太多數據,要讓大家都冷靜點。這個代價有點大,要儘量避免。在Redis2.8以前,Rewrite AOF就經常導致這個大面積阻塞,現在已經改為Redis每32Mb先主動flush()一下了。
原理分析結論
- Kafka使用文件系統來交換消息,性能是否比使用記憶體來交換消息的系統要低很多?
- 在Apache Kafka里,消息的讀寫都發生在記憶體中(Pagecache),真正寫盤的就是那條pdflush內核線程,根本不在Kafka的主流程中,讀操作大多數會命中Pagecache,同時由於預讀機制存在,所以性能非常好,從原理上有保證的。
- 每個分區一個文件,那麼多個分區會有多個文件同時讀寫,是否會極大的降低性能?
- 首先,由於Kafka讀寫流程是發生在PageCache中,後臺的flush不在主流程中觸發,所以正常情況下理論上是沒有影響的,除非PageCache占用記憶體過大,或是釋放導致讀寫消耗Kafka進程的CPU時間。
- 再次,文件都是順序讀寫,OS層面有預讀和後寫機制,即使一臺伺服器上有多個Partition文件,經過合併和排序後都能獲得很好的性能,不會出現文件多了變成隨機讀寫的情況,但是當達到相當多的數量之後,也會存在一定的影響。
- 當PageCache過大,大量觸發磁碟I/O的時候,超過了/proc/sys/vm/dirty_ratio,Flush會占用各個應用自己的CPU時間,會對主流程產生影響,讓主流程變慢。
- 使用SSD盤並不能顯著地改善 Kafka 的性能,主要有兩個原因:
- Kafka寫磁碟是非同步的,不是同步的。就是說,除了啟動、停止之外,Kafka的任何操作都不會去等待磁碟同步(sync)完成;而磁碟同步(syncs)總是在後臺完成的。這就是為什麼Kafka消息至少複製到三個副本是至關重要的,因為一旦單個副本崩潰,這個副本就會丟失數據無法同步寫到磁碟。
- 每一個Kafka Partition被存儲為一個串列的WAL(Write Ahead Log)日誌文件。因此,除了極少數的數據查詢,Kafka中的磁碟讀寫都是串列的。現代的操作系統已經對串列讀寫做了大量的優化工作。
- 如何對Kafka Broker上持久化的數據進行加密
- 目前,Kafka不提供任何機制對Broker上持久化的數據進行加密。用戶可以自己對寫入到Kafka的數據進行加密,即是,生產者(Producers)在寫Kafka之前加密數據,消費者(Consumers)能解密收到的消息。這就要求生產者(Producers)把加密協議(protocols)和密鑰(keys)分享給消費者(Consumers)。
- 另外一種選擇,就是使用軟體提供的文件系統級別的加密,例如Cloudera Navigator Encrypt。Cloudera Navigator Encrypt是Cloudera企業版(Cloudera Enterprise)的一部分,在應用程式和文件系統之間提供了一個透明的加密層。
- Kafka是否支持跨數據中心的可用性
- Kafka跨數據中心可用性的推薦解決方案是使用MirrorMaker。在你的每一個數據中心都搭建一個Kafka集群,在Kafka集群之間使用MirrorMaker來完成近實時的數據複製。
- 使用MirrorMaker的架構模式是為每一個”邏輯”的topic在每一個數據中心創建一個topic:例如,在邏輯上你有一個”clicks”的topic,那麼你實際上有”DC1.clicks”和“DC2.clicks”兩個topic(DC1和DC2指得是你的數據中心)。DC1向DC1.clicks中寫數據,DC2向DC2.clicks中寫數據。MirrorMaker將複製所有的DC1 topics到DC2,並且複製所有的DC2 topics到DC1。現在每個DC上的應用程式都能夠訪問寫入到兩個DC的事件。這個應用程式能夠合併信息和處理相應的衝突。
- 另一種更複雜的模式是在每一個DC都搭建本地和聚合Kafka集群。這個模式已經被Linkedin使用,Linkedin Kafka運維團隊已經在 這篇Blog 中有詳細的描述(參見“Tiers and Aggregation”)。