不管 kafka 做爲 MQ 也好,做爲存儲層也罷,無非就是兩個功能(好簡單的樣子),一是 Producer 生產的數據存到 broker,二是 Consumer 從 broker 讀取數據。那 Kafka 的快也就體如今讀寫兩個方面了,下面咱們就聊聊 Kafka 快的緣由。html
咱們都知道 Kafka 是一個 Pub-Sub 的消息系統,不管是發佈仍是訂閱,都要指定 Topic。java
Topic 只是一個邏輯的概念。每一個 Topic 都包含一個或多個 Partition,不一樣 Partition 可位於不一樣節點。linux
一方面,因爲不一樣 Partition 可位於不一樣機器,所以能夠充分利用集羣優點,實現機器間的並行處理。另外一方面,因爲 Partition 在物理上對應一個文件夾,即便多個 Partition 位於同一個節點,也可經過配置讓同一節點上的不一樣 Partition 置於不一樣的磁盤上,從而實現磁盤間的並行處理,充分發揮多磁盤的優點。web
能並行處理,速度確定會有提高,多個工人確定比一個工人乾的快。面試
能夠並行寫入不一樣的磁盤?那磁盤讀寫的速度能夠控制嗎?算法
那就先簡單扯扯磁盤/IO 的那些事apache
硬盤性能的制約因素是什麼?如何根據磁盤I/O特性來進行系統設計?緩存
硬盤內部主要部件爲磁盤盤片、傳動手臂、讀寫磁頭和主軸馬達。實際數據都是寫在盤片上,讀寫主要是經過傳動手臂上的讀寫磁頭來完成。實際運行時,主軸讓磁盤盤片轉動,而後傳動手臂可伸展讓讀取頭在盤片上進行讀寫操做。磁盤物理結構以下圖所示:安全
因爲單一盤片容量有限,通常硬盤都有兩張以上的盤片,每一個盤片有兩面,均可記錄信息,因此一張盤片對應着兩個磁頭。盤片被分爲許多扇形的區域,每一個區域叫一個扇區。盤片表面上以盤片中心爲圓心,不一樣半徑的同心圓稱爲磁道,不一樣盤片相同半徑的磁道所組成的圓柱稱爲柱面。磁道與柱面都是表示不一樣半徑的圓,在許多場合,磁道和柱面能夠互換使用。磁盤盤片垂直視角以下圖所示:網絡
影響磁盤的關鍵因素是磁盤服務時間,即磁盤完成一個I/O請求所花費的時間,它由尋道時間、旋轉延遲和數據傳輸時間三部分構成。
機械硬盤的連續讀寫性能很好,但隨機讀寫性能不好,這主要是由於磁頭移動到正確的磁道上須要時間,隨機讀寫時,磁頭須要不停的移動,時間都浪費在了磁頭尋址上,因此性能不高。衡量磁盤的重要主要指標是IOPS和吞吐量。
在許多的開源框架如 Kafka、HBase 中,都經過追加寫的方式來儘量的將隨機 I/O 轉換爲順序 I/O,以此來下降尋址時間和旋轉延時,從而最大限度的提升 IOPS。
感興趣的同窗能夠看看 磁盤I/O那些事
磁盤讀寫的快慢取決於你怎麼使用它,也就是順序讀寫或者隨機讀寫。
Kafka 中每一個分區是一個有序的,不可變的消息序列,新的消息不斷追加到 partition 的末尾,這個就是順序寫。
好久好久之前就有人作過基準測試:《每秒寫入2百萬(在三臺廉價機器上)》http://ifeve.com/benchmarking-apache-kafka-2-million-writes-second-three-cheap-machines/
因爲磁盤有限,不可能保存全部數據,實際上做爲消息系統 Kafka 也不必保存全部數據,須要刪除舊的數據。又因爲順序寫入的緣由,因此 Kafka 採用各類刪除策略刪除數據的時候,並不是經過使用「讀 - 寫」模式去修改文件,而是將 Partition 分爲多個 Segment,每一個 Segment 對應一個物理文件,經過刪除整個文件的方式去刪除 Partition 內的數據。這種方式清除舊數據的方式,也避免了對文件的隨機寫操做。
引入 Cache 層的目的是爲了提升 Linux 操做系統對磁盤訪問的性能。Cache 層在內存中緩存了磁盤上的部分數據。當數據的請求到達時,若是在 Cache 中存在該數據且是最新的,則直接將數據傳遞給用戶程序,免除了對底層磁盤的操做,提升了性能。Cache 層也正是磁盤 IOPS 爲何能突破 200 的主要緣由之一。
在 Linux 的實現中,文件 Cache 分爲兩個層面,一是 Page Cache,另外一個 Buffer Cache,每個 Page Cache 包含若干 Buffer Cache。Page Cache 主要用來做爲文件系統上的文件數據的緩存來用,尤爲是針對當進程對文件有 read/write 操做的時候。Buffer Cache 則主要是設計用來在系統對塊設備進行讀寫的時候,對塊進行數據緩存的系統來使用。
使用 Page Cache 的好處:
I/O Scheduler 會將連續的小塊寫組裝成大塊的物理寫從而提升性能
I/O Scheduler 會嘗試將一些寫操做從新按順序排好,從而減小磁盤頭的移動時間
充分利用全部空閒內存(非 JVM 內存)。若是使用應用層 Cache(即 JVM 堆內存),會增長 GC 負擔
讀操做可直接在 Page Cache 內進行。若是消費和生產速度至關,甚至不須要經過物理磁盤(直接經過 Page Cache)交換數據
若是進程重啓,JVM 內的 Cache 會失效,但 Page Cache 仍然可用
Broker 收到數據後,寫磁盤時只是將數據寫入 Page Cache,並不保證數據必定徹底寫入磁盤。從這一點看,可能會形成機器宕機時,Page Cache 內的數據未寫入磁盤從而形成數據丟失。可是這種丟失只發生在機器斷電等形成操做系統不工做的場景,而這種場景徹底能夠由 Kafka 層面的 Replication 機制去解決。若是爲了保證這種狀況下數據不丟失而強制將 Page Cache 中的數據 Flush 到磁盤,反而會下降性能。也正因如此,Kafka 雖然提供了 flush.messages
和 flush.ms
兩個參數將 Page Cache 中的數據強制 Flush 到磁盤,可是 Kafka 並不建議使用。
Kafka 中存在大量的網絡數據持久化到磁盤(Producer 到 Broker)和磁盤文件經過網絡發送(Broker 到 Consumer)的過程。這一過程的性能直接影響 Kafka 的總體吞吐量。
操做系統的核心是內核,獨立於普通的應用程序,能夠訪問受保護的內存空間,也有訪問底層硬件設備的權限。
爲了不用戶進程直接操做內核,保證內核安全,操做系統將虛擬內存劃分爲兩部分,一部分是內核空間(Kernel-space),一部分是用戶空間(User-space)。
傳統的 Linux 系統中,標準的 I/O 接口(例如read,write)都是基於數據拷貝操做的,即 I/O 操做會致使數據在內核地址空間的緩衝區和用戶地址空間的緩衝區之間進行拷貝,因此標準 I/O 也被稱做緩存 I/O。這樣作的好處是,若是所請求的數據已經存放在內核的高速緩衝存儲器中,那麼就能夠減小實際的 I/O 操做,但壞處就是數據拷貝的過程,會致使 CPU 開銷。
傳統模式下,數據從網絡傳輸到文件須要 4 次數據拷貝、4 次上下文切換和兩次系統調用。
data = socket.read()// 讀取網絡數據
File file = new File()
file.write(data)// 持久化到磁盤
file.flush()
這一過程實際上發生了四次數據拷貝:
DMA(Direct Memory Access):直接存儲器訪問。DMA 是一種無需 CPU 的參與,讓外設和系統內存之間進行雙向數據傳輸的硬件機制。使用 DMA 可使系統 CPU 從實際的 I/O 數據傳輸過程當中擺脫出來,從而大大提升系統的吞吐率。
同時,還伴隨着四次上下文切換,以下圖所示
數據落盤一般都是非實時的,kafka 生產者數據持久化也是如此。Kafka 的數據並非實時的寫入硬盤,它充分利用了現代操做系統分頁存儲來利用內存提升 I/O 效率,就是上一節提到的 Page Cache。
對於 kafka 來講,Producer 生產的數據存到 broker,這個過程讀取到 socket buffer 的網絡數據,其實能夠直接在內核空間完成落盤。並無必要將 socket buffer 的網絡數據,讀取到應用進程緩衝區;在這裏應用進程緩衝區其實就是 broker,broker 收到生產者的數據,就是爲了持久化。
在此特殊場景
下:接收來自 socket buffer 的網絡數據,應用進程不須要中間處理、直接進行持久化時。可使用 mmap 內存文件映射。
Memory Mapped Files:簡稱 mmap,也有叫 MMFile 的,使用 mmap 的目的是將內核中讀緩衝區(read buffer)的地址與用戶空間的緩衝區(user buffer)進行映射。從而實現內核緩衝區與應用程序內存的共享,省去了將數據從內核讀緩衝區(read buffer)拷貝到用戶緩衝區(user buffer)的過程。它的工做原理是直接利用操做系統的 Page 來實現文件到物理內存的直接映射。完成映射以後你對物理內存的操做會被同步到硬盤上。
使用這種方式能夠獲取很大的 I/O 提高,省去了用戶空間到內核空間複製的開銷。
mmap 也有一個很明顯的缺陷——不可靠,寫到 mmap 中的數據並無被真正的寫到硬盤,操做系統會在程序主動調用 flush 的時候才把數據真正的寫到硬盤。Kafka 提供了一個參數——producer.type
來控制是否是主動flush;若是 Kafka 寫入到 mmap 以後就當即 flush 而後再返回 Producer 叫同步(sync);寫入 mmap 以後當即返回 Producer 不調用 flush 就叫異步(async),默認是 sync。
零拷貝(Zero-copy)技術指在計算機執行操做時,CPU 不須要先將數據從一個內存區域複製到另外一個內存區域,從而能夠減小上下文切換以及 CPU 的拷貝時間。
它的做用是在數據報從網絡設備到用戶程序空間傳遞的過程當中,減小數據拷貝次數,減小系統調用,實現 CPU 的零參與,完全消除 CPU 在這方面的負載。
- 直接I/O:數據直接跨過內核,在用戶地址空間與I/O設備之間傳遞,內核只是進行必要的虛擬存儲配置等輔助工做;
- 避免內核和用戶空間之間的數據拷貝:當應用程序不須要對數據進行訪問時,則能夠避免將數據從內核空間拷貝到用戶空間
- mmap
- sendfile
- splice && tee
- sockmap
- copy on write:寫時拷貝技術,數據不須要提早拷貝,而是當須要修改的時候再進行部分拷貝。
傳統方式實現:先讀取磁盤、再用 socket 發送,實際也是進過四次 copy
buffer = File.read
Socket.send(buffer)
這一過程能夠類比上邊的生產消息:
Linux 2.4+ 內核經過 sendfile 系統調用,提供了零拷貝。數據經過 DMA 拷貝到內核態 Buffer 後,直接經過 DMA 拷貝到 NIC Buffer,無需 CPU 拷貝。這也是零拷貝這一說法的來源。除了減小數據拷貝外,由於整個讀文件 - 網絡發送由一個 sendfile 調用完成,整個過程只有兩次上下文切換,所以大大提升了性能。
Kafka 在這裏採用的方案是經過 NIO 的 transferTo/transferFrom
調用操做系統的 sendfile 實現零拷貝。總共發生 2 次內核數據拷貝、2 次上下文切換和一次系統調用,消除了 CPU 數據拷貝
在不少狀況下,系統的瓶頸不是 CPU 或磁盤,而是網絡IO。
所以,除了操做系統提供的低級批處理以外,Kafka 的客戶端和 broker 還會在經過網絡發送數據以前,在一個批處理中累積多條記錄 (包括讀和寫)。記錄的批處理分攤了網絡往返的開銷,使用了更大的數據包從而提升了帶寬利用率。
Producer 可將數據壓縮後發送給 broker,從而減小網絡傳輸代價,目前支持的壓縮算法有:Snappy、Gzip、LZ4。數據壓縮通常都是和批處理配套使用來做爲優化手段的。