面試問答| 為什麼kafka在各大mq效能評測中還能夠擊敗眾多對手?

面試問答| 為什麼kafka在各大mq效能評測中還能夠擊敗眾多對手?

一、背景

前段時間,師兄一直在四處奔波面試中,和我分享了很多面試題和經驗,我都一一拿小本本記下來(超認真!!)

其實在今天的很多面試裡,都會要求能夠熟練運用ApacheKafka等至少一種訊息佇列,ApacheKafka也是我們面試裡的常客。

但在大多數人的印象中,寫磁碟都是比較慢的,可是,為什麼ApacheKafka在各大MQ效能的評測中,還能夠擊敗眾多對手,取得不錯的成績呢?

接下來就讓我們透過師兄遇到的關於Kafka問題的暴擊三連問,走進他受傷的心。。。。

二、帶著疑問思考

「Kafka為什麼快?」

「Kafka和其他訊息佇列的區別?」

「Kafka這麼快,它是如何保證不丟失訊息?」

三、我的回答思路

其實,在大資料開發崗的面試中,都避免不了面試官問你Kafka,畢竟Kafka和KafkaStreams耍起來確實很香啊!

那我們就先從Kafka的基礎知識瞭解起吧,還不太瞭解的小夥伴可以先看看這幾篇部落格,先構建一個技術棧的知識框架,這樣在面試時就能做到無懈可擊啦!

四、關於Kafka為什麼這麼快

Kafka的訊息是儲存或快取在磁碟上的,一般認為在磁碟上讀寫資料是會降低效能的,因為定址會比較消耗時間,但

是實際上,Kafka的特性之一就是高吞吐率。Kafka之所以能這麼快,無非是:「順序寫磁碟、大量使用記憶體頁、零複製技術的使用」。。。

下面我就從資料寫入和讀取兩方面分析,為大家分析下為什麼Kafka速度這麼快。

資料寫入Kafka會把收到的訊息都寫入到硬碟中,它絕對不會丟失資料。為了最佳化寫入速度Kafka採用了兩個技術,順序寫入和MemoryMappedFile。

4。1順序寫入

Kafka會把收到的訊息都寫入到硬碟中,它絕對不會丟失資料。為了最佳化寫入速度Kafka採用了兩個技術,順序寫入和MMFile(MemoryMappedFile)。

磁碟讀寫的快慢取決於你怎麼使用它,也就是順序讀寫或者隨機讀寫。在順序讀寫的情況下,磁碟的順序讀寫速度和記憶體持平。因為硬碟是機械結構,每次讀寫都會定址->寫入,其中定址是一個“機械動作”,它是最耗時的。「所以硬碟最討厭隨機I/O,最喜歡順序I/O」。為了提高讀寫硬碟的速度,Kafka就是使用順序I/O。

而且Linux對於磁碟的讀寫最佳化也比較多,包括read-ahead和write-behind,磁碟快取等。

如果在記憶體做這些操作的時候,一個是Java物件的記憶體開銷很大,另一個是隨著堆記憶體資料的增多,Java的GC時間會變得很長。

使用磁碟操作有以下幾個好處:

磁碟順序讀寫速度超過記憶體隨機讀寫。

JVM的GC效率低,記憶體佔用大。使用磁碟可以避免這一問題。

系統冷啟動後,磁碟快取依然可用。

下圖就展示了Kafka是如何寫入資料的,每一個Partition其實都是一個檔案,收到訊息後Kafka會把資料插入到檔案末尾(虛框部分):

面試問答| 為什麼kafka在各大mq效能評測中還能夠擊敗眾多對手?

這種方法有一個缺陷——沒有辦法刪除資料,所以Kafka是不會刪除資料的,它會把所有的資料都保留下來,每個消費者(Consumer)對每個Topic都有一個Offset用來表示讀取到了第幾條資料。

面試問答| 為什麼kafka在各大mq效能評測中還能夠擊敗眾多對手?

一般情況下Offset由客戶端SDK負責儲存,會儲存到Zookeeper裡面。關於存在硬碟中的訊息,Kafka也有它的解決方法,可以基於時間和Partition檔案的大小,正常Kafka是預設七天的儲存,也可以透過命令來修改,以userstopic為例。

修改kafka7天預設儲存週期kafka-topics。sh——zookeeper6——alter——topicusers——configretention。ms=100000

所以,為了避免磁碟被撐滿的情況,Kakfa提供了兩種策略來刪除資料:

「基於時間」(預設七天)

「基於Partition檔案大小」

4。2MemoryMappedFiles

這個和JavaNIO中的記憶體對映基本相同,在大學的計算機原理裡我們學過(劃重點),mmf(MemoryMappedFiles)直接利用作業系統的Page來實現檔案到物理記憶體的對映,完成之後對物理記憶體的操作會直接同步到硬碟。mmf透過記憶體對映的方式大大提高了IO速率,省去了使用者空間到核心空間的複製。它的缺點顯而易見——不可靠,當發生宕機而資料未同步到硬碟時,資料會丟失,Kafka提供了produce。type引數來控制是否主動的進行重新整理,如果Kafka寫入到mmf後立即flush再返回給生產者則為同步模式,反之為非同步模式。

Kafka提供了一個引數producer。type來控制是不是主動Flush:

如果Kafka寫入到mmf之後就立即Flush,然後再返回Producer叫同步(Sync)。

如果Kafka寫入mmf之後立即返回Producer不呼叫Flush叫非同步(Async)。

資料讀取Kafka在讀取磁碟時做了哪些最佳化?

4。3基於Sendfile實現零複製(ZeroCopy)

作為一個訊息系統,不可避免的便是訊息的複製,常規的操作,一條訊息,需要從建立者的socket到應用,再到作業系統核心,然後才能落盤。同樣,一條訊息傳送給消費者也要從磁碟到核心到應用再到接收者的socket,中間經過了多次不是很有必要的複製。

傳統Read/Write方式進行網路檔案傳輸,在傳輸過程中,檔案資料實際上是經過了四次Copy操作,其具體流程細節如下:

呼叫Read函式,檔案資料被Copy到核心緩衝區。

Read函式返回,檔案資料從核心緩衝區Copy到使用者緩衝區

Write函式呼叫,將檔案資料從使用者緩衝區Copy到核心與Socket相關的緩衝區。

資料從Socket緩衝區Copy到相關協議引擎。

硬碟—>核心buf—>使用者buf—>Socket相關緩衝區—>協議引擎

而Sendfile系統呼叫則提供了一種減少以上多次Copy,提升檔案傳輸效能的方法。在核心版本2。1中,引入了Sendfile系統呼叫,以簡化網路上和兩個本地檔案之間的資料傳輸。Sendfile的引入不僅減少了資料複製,還減少了上下文切換。相較傳統Read/Write方式,2。1版本核心引進的Sendfile已經減少了核心緩衝區到User緩衝區,再由User緩衝區到Socket相關緩衝區的檔案Copy。而在核心版本2。4之後,檔案描述符結果被改變,Sendfile實現了更簡單的方式,再次減少了一次Copy操作。

Kafka把所有的訊息都存放在一個一個的檔案中,當消費者需要資料的時候Kafka直接把檔案傳送給消費者,配合mmap作為檔案讀寫方式,直接把它傳給Sendfile。

4。4批次傳送

Kafka允許進行批次傳送訊息,producter傳送訊息的時候,可以將訊息快取在本地,等到了固定條件傳送到Kafka。

等訊息條數到固定條數。

一段時間傳送一次。

4。5資料壓縮

Kafka還支援對訊息集合進行壓縮,Producer可以透過GZIP或Snappy格式對訊息集合進行壓縮。壓縮的好處就是減少傳輸的資料量,減輕對網路傳輸的壓力。

Producer壓縮之後,在Consumer需進行解壓,雖然增加了CPU的工作,但在對大資料處理上,瓶頸在網路上而不是CPU,所以這個成本很值得。

注意:「批次傳送」和「資料壓縮」一起使用,單條做資料壓縮的話,效果不明顯

4。6總結

以上,便是ApacheKafka雖然使用了硬碟儲存,但是仍然可以速度很快的原因。它把所有的訊息都變成一個批次的檔案,並且進行合理的批次壓縮,減少網路IO損耗,透過mmap提高I/O速度。寫入資料的時候由於單個Partion是末尾新增,所以速度最優;讀取資料的時候配合Sendfile直接暴力輸出。

五、關於Kafka和其他訊息佇列的區別

Kafka的設計目標是高吞吐量,那它與其它訊息佇列的區別就顯而易見了:

1、Kafka操作的是序列檔案I/O(序列檔案的特徵是按順序寫,按順序讀),為保證順序,Kafka強制點對點的按順序傳遞訊息,這意味著,一個consumer在訊息流(或分割槽)中只有一個位置。

2、Kafka不儲存訊息的狀態,即訊息是否被“消費”。一般的訊息系統需要儲存訊息的狀態,並且還需要以隨機訪問的形式更新訊息的狀態。而Kafka的做法是儲存Consumer在Topic分割槽中的位置offset,在offset之前的訊息是已被“消費”的,在offset之後則為未“消費”的,並且offset是可以任意移動的,這樣就消除了大部分的隨機IO。

3、Kafka支援點對點的批次訊息傳遞。

4、Kafka的訊息儲存在OSpagecache(頁快取,pagecache的大小為一頁,通常為4K,在Linux讀寫檔案時,它用於快取檔案的邏輯內容,從而加快對磁碟上映像和資料的訪問)。

RabbitMQ:分散式,支援多種MQ協議,重量級ActiveMQ:與RabbitMQ類似ZeroMQ:以庫的形式提供,使用複雜,無持久化Redis:單機、純記憶體性好,持久化較差Kafka:分散式,訊息不是使用完就丟失【較長時間持久化】,吞吐量高【高效能】,輕量靈活

六、Kafka如何保證訊息佇列不丟失

我想,如果小夥伴直接遇到面試官問:Kafka為什麼這麼快,又能保證不丟失訊息?肯定是矇蔽的,但如果前面回答的僅僅有條,這個時候再回答訊息不丟失,就很容易啦!Σ(っ°Д°;)っ

6。1關於ACK機制

關於ACK機制,不瞭解的小夥伴,可以看這裡:Kafka架構深入,透過ACK機制保證訊息送達。Kafka採用的是至少一次(Atleastonce),訊息不會丟,但是可能會重複傳輸。

acks的預設值即為1,代表我們的訊息被leader副本接收之後就算被成功傳送。我們可以配置acks=all,代表則所有副本都要接收到該訊息之後該訊息才算真正成功被髮送。

6。2關於設定分割槽

為了保證leader副本能有follower副本能同步訊息,我們一般會為topic設定replication。factor>=3。這樣就可以保證每個分割槽(partition)至少有3個副本,以確保訊息佇列的安全性。

6。3關閉uncleanleader選舉

我們最開始也說了我們傳送的訊息會被髮送到leader副本,然後follower副本才能從leader副本中拉取訊息進行同步。多個follower副本之間的訊息同步情況不一樣,當我們配置了unclean。leader。election。enable=false的話,當leader副本發生故障時就不會從follower副本中和leader同步程度達不到要求的副本中選擇出leader,這樣降低了訊息丟失的可能性。

6。4關於傳送訊息

為了得到更好的效能,Kafka支援在生產者一側進行本地buffer,也就是累積到一定的條數才傳送,如果這裡設定不當是會丟訊息的。

生產者端設定:producer。type=async,sync,預設是sync。

當設定為async,會大幅提升效能,因為生產者會在本地緩衝訊息,並適時批次傳送。

如果對可靠性要求高,那麼這裡可以設定為sync同步傳送。

一般時候我們還需要設定:min。insync。replicas>1,訊息至少要被寫入到這麼多副本才算成功,也是提升資料永續性的一個引數,與acks配合使用。

但如果出現兩者相等,我們還需要設定replication。factor=min。insync。replicas+1,避免在一個副本掛掉,整個分割槽無法工作的情況!

6。5Consumer端丟失訊息

consumer端丟失訊息的情形比較簡單:

如果在訊息處理完成前就提交了offset,那麼就有可能造成資料的丟失。

由於Kafkaconsumer預設是自動提交位移的,所以在後臺提交位移前一定要保證訊息被正常處理了,因此不建議採用很重的處理邏輯,如果處理耗時很長,則建議把邏輯放到另一個執行緒中去做。

為了避免資料丟失,現給出幾點建議:設定enable。auto。commit=false

關閉自動提交位移,在訊息被完整處理之後再手動提交位移。

以上,便是本篇部落格的所有內容,希望師兄大廠面試順利!

我是敖丙,你知道的越多,你不知道的越多,我們下期見!

頂部