掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網(wǎng)交流
作者:不假 2020-08-13 09:19:10
開源
Kafka 無論 Kafka 作為 MQ 也好,還是作為存儲層也罷,無非就是兩個功能,一是 Producer 生產(chǎn)的數(shù)據(jù)存到 Broker,二是 Consumer 從 Broker 讀取數(shù)據(jù)。

創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設、高性價比信豐網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式信豐網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設找我們,業(yè)務覆蓋信豐地區(qū)。費用合理售后完善,十載實體公司更值得信賴。
無論 Kafka 作為 MQ 也好,還是作為存儲層也罷,無非就是兩個功能,一是 Producer 生產(chǎn)的數(shù)據(jù)存到 Broker,二是 Consumer 從 Broker 讀取數(shù)據(jù)。
片來自 Pexels
那 Kafka 的快也就體現(xiàn)在讀寫兩個方面了,下面我們就聊聊 Kafka 快的原因。
利用 Partition 實現(xiàn)并行處理
我們都知道 Kafka 是一個 Pub-Sub 的消息系統(tǒng),無論是發(fā)布還是訂閱,都要指定 Topic。
Topic 只是一個邏輯的概念。每個 Topic 都包含一個或多個 Partition,不同 Partition 可位于不同節(jié)點。
一方面,由于不同 Partition 可位于不同機器,因此可以充分利用集群優(yōu)勢,實現(xiàn)機器間的并行處理。
另一方面,由于 Partition 在物理上對應一個文件夾,即使多個 Partition 位于同一個節(jié)點,也可通過配置讓同一節(jié)點上的不同 Partition 置于不同的磁盤上,從而實現(xiàn)磁盤間的并行處理,充分發(fā)揮多磁盤的優(yōu)勢。
能并行處理,速度肯定會有提升,多個工人肯定比一個工人干的快??梢圆⑿袑懭氩煌拇疟P?那磁盤讀寫的速度可以控制嗎?那就先簡單扯扯磁盤 I/O 的那些事。
硬盤性能的制約因素是什么?如何根據(jù)磁盤 I/O 特性來進行系統(tǒng)設計?
硬盤內部主要部件為磁盤盤片、傳動手臂、讀寫磁頭和主軸馬達。實際數(shù)據(jù)都是寫在盤片上,讀寫主要是通過傳動手臂上的讀寫磁頭來完成。
實際運行時,主軸讓磁盤盤片轉動,然后傳動手臂可伸展讓讀取頭在盤片上進行讀寫操作。
磁盤物理結構如下圖所示:
由于單一盤片容量有限,一般硬盤都有兩張以上的盤片,每個盤片有兩面,都可記錄信息,所以一張盤片對應著兩個磁頭。
盤片被分為許多扇形的區(qū)域,每個區(qū)域叫一個扇區(qū)。盤片表面上以盤片中心為圓心,不同半徑的同心圓稱為磁道,不同盤片相同半徑的磁道所組成的圓柱稱為柱面。
磁道與柱面都是表示不同半徑的圓,在許多場合,磁道和柱面可以互換使用。
磁盤盤片垂直視角如下圖所示:
影響磁盤的關鍵因素是磁盤服務時間,即磁盤完成一個 I/O 請求所花費的時間,它由尋道時間、旋轉延遲和數(shù)據(jù)傳輸時間三部分構成。
機械硬盤的連續(xù)讀寫性能很好,但隨機讀寫性能很差,這主要是因為磁頭移動到正確的磁道上需要時間,隨機讀寫時,磁頭需要不停的移動,時間都浪費在了磁頭尋址上,所以性能不高。衡量磁盤的重要主要指標是 IOPS 和吞吐量。
在許多的開源框架如 Kafka、HBase 中,都通過追加寫的方式來盡可能的將隨機 I/O 轉換為順序 I/O,以此來降低尋址時間和旋轉延時,從而最大限度的提高 IOPS。
感興趣的同學可以看看磁盤 I/O 那些事[1],磁盤讀寫的快慢取決于你怎么使用它,也就是順序讀寫或者隨機讀寫。
順序寫磁盤
Kafka 中每個分區(qū)是一個有序的,不可變的消息序列,新的消息不斷追加到 Partition 的末尾,這個就是順序寫。
很久很久以前就有人做過基準測試:《每秒寫入 2 百萬(在三臺廉價機器上)》
- http://ifeve.com/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines/
由于磁盤有限,不可能保存所有數(shù)據(jù),實際上作為消息系統(tǒng) Kafka 也沒必要保存所有數(shù)據(jù),需要刪除舊的數(shù)據(jù)。
又由于順序寫入的原因,所以 Kafka 采用各種刪除策略刪除數(shù)據(jù)的時候,并非通過使用“讀 - 寫”模式去修改文件,而是將 Partition 分為多個 Segment。
每個 Segment 對應一個物理文件,通過刪除整個文件的方式去刪除 Partition 內的數(shù)據(jù)。
這種方式清除舊數(shù)據(jù)的方式,也避免了對文件的隨機寫操作。
充分利用 Page Cache
引入 Cache 層的目的是為了提高 Linux 操作系統(tǒng)對磁盤訪問的性能。Cache 層在內存中緩存了磁盤上的部分數(shù)據(jù)。
當數(shù)據(jù)的請求到達時,如果在 Cache 中存在該數(shù)據(jù)且是最新的,則直接將數(shù)據(jù)傳遞給用戶程序,免除了對底層磁盤的操作,提高了性能。Cache 層也正是磁盤 IOPS 為什么能突破 200 的主要原因之一。
在 Linux 的實現(xiàn)中,文件 Cache 分為兩個層面,一是 Page Cache,另一個 Buffer Cache,每一個 Page Cache 包含若干 Buffer Cache。
Page Cache 主要用來作為文件系統(tǒng)上的文件數(shù)據(jù)的緩存來用,尤其是針對當進程對文件有 read/write 操作的時候。
Buffer Cache 則主要是設計用來在系統(tǒng)對塊設備進行讀寫的時候,對塊進行數(shù)據(jù)緩存的系統(tǒng)來使用。
使用 Page Cache 的好處:
Broker 收到數(shù)據(jù)后,寫磁盤時只是將數(shù)據(jù)寫入 Page Cache,并不保證數(shù)據(jù)一定完全寫入磁盤。從這一點看,可能會造成機器宕機時,Page Cache 內的數(shù)據(jù)未寫入磁盤從而造成數(shù)據(jù)丟失。
但是這種丟失只發(fā)生在機器斷電等造成操作系統(tǒng)不工作的場景,而這種場景完全可以由 Kafka 層面的 Replication 機制去解決。
如果為了保證這種情況下數(shù)據(jù)不丟失而強制將 Page Cache 中的數(shù)據(jù) Flush 到磁盤,反而會降低性能。
也正因如此,Kafka 雖然提供了 flush.messages 和 flush.ms 兩個參數(shù)將 Page Cache 中的數(shù)據(jù)強制 Flush 到磁盤,但是 Kafka 并不建議使用。
零拷貝技術
Kafka 中存在大量的網(wǎng)絡數(shù)據(jù)持久化到磁盤(Producer 到 Broker)和磁盤文件通過網(wǎng)絡發(fā)送(Broker 到 Consumer)的過程。這一過程的性能直接影響 Kafka 的整體吞吐量。
操作系統(tǒng)的核心是內核,獨立于普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的權限。
為了避免用戶進程直接操作內核,保證內核安全,操作系統(tǒng)將虛擬內存劃分為兩部分,一部分是內核空間(Kernel-space),一部分是用戶空間(User-space)。
傳統(tǒng)的 Linux 系統(tǒng)中,標準的 I/O 接口(例如 read,write)都是基于數(shù)據(jù)拷貝操作的,即 I/O 操作會導致數(shù)據(jù)在內核地址空間的緩沖區(qū)和用戶地址空間的緩沖區(qū)之間進行拷貝,所以標準 I/O 也被稱作緩存 I/O。
這樣做的好處是,如果所請求的數(shù)據(jù)已經(jīng)存放在內核的高速緩沖存儲器中,那么就可以減少實際的 I/O 操作,但壞處就是數(shù)據(jù)拷貝的過程,會導致 CPU 開銷。
我們把 Kafka 的生產(chǎn)和消費簡化成如下兩個過程來看[2]:
①網(wǎng)絡數(shù)據(jù)持久化到磁盤 (Producer 到 Broker)
傳統(tǒng)模式下,數(shù)據(jù)從網(wǎng)絡傳輸?shù)轿募枰?4 次數(shù)據(jù)拷貝、4 次上下文切換和兩次系統(tǒng)調用。
- data = socket.read()// 讀取網(wǎng)絡數(shù)據(jù)
- File file = new File()
- file.write(data)// 持久化到磁盤
- file.flush()
這一過程實際上發(fā)生了四次數(shù)據(jù)拷貝:
DMA(Direct Memory Access):直接存儲器訪問。DMA 是一種無需 CPU 的參與,讓外設和系統(tǒng)內存之間進行雙向數(shù)據(jù)傳輸?shù)挠布C制。
使用 DMA 可以使系統(tǒng) CPU 從實際的 I/O 數(shù)據(jù)傳輸過程中擺脫出來,從而大大提高系統(tǒng)的吞吐率。
同時,還伴隨著四次上下文切換,如下圖所示:
數(shù)據(jù)落盤通常都是非實時的,Kafka 生產(chǎn)者數(shù)據(jù)持久化也是如此。Kafka 的數(shù)據(jù)并不是實時的寫入硬盤,它充分利用了現(xiàn)代操作系統(tǒng)分頁存儲來利用內存提高 I/O 效率,就是上一節(jié)提到的 Page Cache。
對于 Kafka 來說,Producer 生產(chǎn)的數(shù)據(jù)存到 Broker,這個過程讀取到 socket buffer 的網(wǎng)絡數(shù)據(jù),其實可以直接在內核空間完成落盤。
并沒有必要將 socket buffer 的網(wǎng)絡數(shù)據(jù),讀取到應用進程緩沖區(qū);在這里應用進程緩沖區(qū)其實就是 Broker,Broker 收到生產(chǎn)者的數(shù)據(jù),就是為了持久化。
在此特殊場景下:接收來自 socket buffer 的網(wǎng)絡數(shù)據(jù),應用進程不需要中間處理、直接進行持久化時??梢允褂?mmap 內存文件映射。
Memory Mapped Files:簡稱 mmap,也有叫 MMFile 的,使用 mmap 的目的是將內核中讀緩沖區(qū)(read buffer)的地址與用戶空間的緩沖區(qū)(user buffer)進行映射。
從而實現(xiàn)內核緩沖區(qū)與應用程序內存的共享,省去了將數(shù)據(jù)從內核讀緩沖區(qū)(read buffer)拷貝到用戶緩沖區(qū)(user buffer)的過程。
它的工作原理是直接利用操作系統(tǒng)的 Page 來實現(xiàn)文件到物理內存的直接映射。完成映射之后你對物理內存的操作會被同步到硬盤上。
使用這種方式可以獲取很大的 I/O 提升,省去了用戶空間到內核空間復制的開銷。
mmap 也有一個很明顯的缺陷:不可靠,寫到 mmap 中的數(shù)據(jù)并沒有被真正的寫到硬盤,操作系統(tǒng)會在程序主動調用 Flush 的時候才把數(shù)據(jù)真正的寫到硬盤。
Kafka 提供了一個參數(shù) producer.type 來控制是不是主動 Flush;如果 Kafka 寫入到 mmap 之后就立即 Flush,然后再返回 Producer 叫同步(sync)。
寫入 mmap 之后立即返回 Producer 不調用 Flush 就叫異步(async),默認是 sync。
零拷貝(Zero-copy)技術指在計算機執(zhí)行操作時,CPU 不需要先將數(shù)據(jù)從一個內存區(qū)域復制到另一個內存區(qū)域,從而可以減少上下文切換以及 CPU 的拷貝時間。
它的作用是在數(shù)據(jù)報從網(wǎng)絡設備到用戶程序空間傳遞的過程中,減少數(shù)據(jù)拷貝次數(shù),減少系統(tǒng)調用,實現(xiàn) CPU 的零參與,徹底消除 CPU 在這方面的負載。
目前零拷貝技術主要有三種類型[3]:
②磁盤文件通過網(wǎng)絡發(fā)送(Broker 到 Consumer)
傳統(tǒng)方式實現(xiàn):先讀取磁盤、再用 Socket 發(fā)送,實際也是進過四次 Copy。
- buffer = File.read
- Socket.send(buffer)
這一過程可以類比上邊的生產(chǎn)消息:
Linux 2.4+ 內核通過 Sendfile 系統(tǒng)調用,提供了零拷貝。數(shù)據(jù)通過 DMA 拷貝到內核態(tài) Buffer 后,直接通過 DMA 拷貝到 NIC Buffer,無需 CPU 拷貝。這也是零拷貝這一說法的來源。
除了減少數(shù)據(jù)拷貝外,因為整個讀文件,網(wǎng)絡發(fā)送由一個 Sendfile 調用完成,整個過程只有兩次上下文切換,因此大大提高了性能。
Kafka 在這里采用的方案是通過 NIO 的 transferTo/transferFrom 調用操作系統(tǒng)的 Sendfile 實現(xiàn)零拷貝。
總共發(fā)生 2 次內核數(shù)據(jù)拷貝、2 次上下文切換和一次系統(tǒng)調用,消除了 CPU 數(shù)據(jù)拷貝。
批處理
在很多情況下,系統(tǒng)的瓶頸不是 CPU 或磁盤,而是網(wǎng)絡IO。
因此,除了操作系統(tǒng)提供的低級批處理之外,Kafka 的客戶端和 Broker 還會在通過網(wǎng)絡發(fā)送數(shù)據(jù)之前,在一個批處理中累積多條記錄 (包括讀和寫)。
記錄的批處理分攤了網(wǎng)絡往返的開銷,使用了更大的數(shù)據(jù)包從而提高了帶寬利用率。
數(shù)據(jù)壓縮
Producer 可將數(shù)據(jù)壓縮后發(fā)送給 Broker,從而減少網(wǎng)絡傳輸代價,目前支持的壓縮算法有:Snappy、Gzip、LZ4。數(shù)據(jù)壓縮一般都是和批處理配套使用來作為優(yōu)化手段的。
下次面試官問我 Kafka 為什么快,我就這么說:
參考資料:
https://tech.meituan.com/2017/05/19/about-desk-io.html
https://zhuanlan.zhihu.com/p/78335525
https://cllc.fun/2020/03/18/linux-zero-copy/
作者:不假
編輯:陶家龍
出處:轉載自公眾號 JavaKeeper(ID:JavaKeeper)
名稱欄目:終于知道Kafka為什么這么快了!
文章起源:http://uogjgqi.cn/article/cogposc.html

我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網(wǎng)交流