深入浅出kafka原理-2-Kafka为何那么快(高效)
目錄
前言:Kafka為何那么快(高效)
1.文件系統(tǒng)
2.降低時(shí)間復(fù)雜度
3.零拷貝
4.下一節(jié)預(yù)告
前言:Kafka為何那么快(高效)
- 利用磁盤順序?qū)懙膬?yōu)勢
- 預(yù)讀取后寫入
- 盡量避免使用 in-memory cache
- 將消息打包降低大量小型IO操作的影響
- 零拷貝(基于mmap的索引和日志讀寫用到的TransportLayer)
1.文件系統(tǒng)
????????Kafka 對消息的存儲和緩存嚴(yán)重依賴于文件系統(tǒng)。人們對于“磁盤速度慢”具有普遍印象,事實(shí)上,磁盤的速度比人們預(yù)期的要慢的多,也快得多,這取決于人們使用磁盤的方式。
使用6個(gè)7200rpm、SATA接口、RAID-5的磁盤陣列在JBOD配置下的順序?qū)懭氲男阅芗s為600MB/秒,但隨機(jī)寫入的性能僅約為100k/秒,相差6000倍以上。
線性的讀取和寫入是磁盤使用模式中最有規(guī)律的,并且由操作系統(tǒng)進(jìn)行了大量的優(yōu)化。
-  read-ahead 是以大的 data block 為單位預(yù)先讀取數(shù)據(jù) 
-  write-behind 是將多個(gè)小型的邏輯寫合并成一次大型的物理磁盤寫入 
????????關(guān)于該問題的進(jìn)一步討論可以參考 ACM Queue article,他們發(fā)現(xiàn)實(shí)際上順序磁盤訪問在某些情況下比隨機(jī)內(nèi)存訪問還要快!
????????為了彌補(bǔ)這種性能差異,現(xiàn)代操作系統(tǒng)主動(dòng)將所有空閑內(nèi)存用作 disk caching(磁盤高速緩存),所有對磁盤的讀寫操作都會通過這個(gè)統(tǒng)一的 cache( in-process cache)。
????????即使進(jìn)程維護(hù)了 in-process cache,該數(shù)據(jù)也可能會被復(fù)制到操作系統(tǒng)的 pagecache 中,事實(shí)上所有內(nèi)容都被存儲了兩份。
此外,Kafka 建立在 JVM 之上,任何了解 Java 內(nèi)存使用的人都知道兩點(diǎn):
對象的內(nèi)存開銷非常高,通常是所存儲的數(shù)據(jù)的兩倍(甚至更多)。
隨著堆中數(shù)據(jù)的增加,Java 的垃圾回收變得越來越復(fù)雜和緩慢。
????????kafka選擇了一個(gè)非常簡單的設(shè)計(jì):相比于維護(hù)盡可能多的 in-memory cache,并且在空間不足的時(shí)候匆忙將數(shù)據(jù) flush 到文件系統(tǒng),我們把這個(gè)過程倒過來。所有數(shù)據(jù)一開始就被寫入到文件系統(tǒng)的持久化日志中,而不用在 cache 空間不足的時(shí)候 flush 到磁盤。實(shí)際上,這表明數(shù)據(jù)被轉(zhuǎn)移到了內(nèi)核的 pagecache 中。
Pagecache頁面緩存
-  Page cache(頁面緩存) Page cache 也叫頁緩沖或文件緩沖,是由好幾個(gè)磁盤塊構(gòu)成,大小通常為4k,在64位系統(tǒng)上為8k,構(gòu)成的幾個(gè)磁盤塊在物理磁盤上不一定連續(xù),文件的組織單位為一頁, 也就是一個(gè)page cache大小,文件讀取是由外存上不連續(xù)的幾個(gè)磁盤塊,到buffer cache,然后組成page cache,然后供給應(yīng)用程序。 
-  Buffer cache(塊緩存) Buffer cache 也叫塊緩沖,是對物理磁盤上的一個(gè)磁盤塊進(jìn)行的緩沖,其大小為通常為1k,磁盤塊也是磁盤的組織單位。設(shè)立buffer cache的目的是為在程序多次訪問同一磁盤塊時(shí),減少訪問時(shí)間。 
-  Page cache(頁面緩存)與Buffer cache(塊緩存)的區(qū)別 磁盤的操作有邏輯級(文件系統(tǒng))和物理級(磁盤塊),這兩種Cache就是分別緩存邏輯和物理級數(shù)據(jù)的。 我們通過文件系統(tǒng)操作文件,那么文件將被緩存到Page Cache,如果需要刷新文件的時(shí)候,Page Cache將交給Buffer Cache去完成,因?yàn)锽uffer Cache就是緩存磁盤塊的。 簡單說來,page cache用來緩存文件數(shù)據(jù),buffer cache用來緩存磁盤數(shù)據(jù)。在有文件系統(tǒng)的情況下,對文件操作,那么數(shù)據(jù)會緩存到page cache,如果直接采用dd等工具對磁盤進(jìn)行讀寫,那么數(shù)據(jù)會緩存到buffer cache。 Buffer(Buffer Cache)以塊形式緩沖了塊設(shè)備的操作,定時(shí)或手動(dòng)的同步到硬盤,它是為了緩沖寫操作然后一次性將很多改動(dòng)寫入硬盤,避免頻繁寫硬盤,提高寫入效率。 Cache(Page Cache)以頁面形式緩存了文件系統(tǒng)的文件,給需要使用的程序讀取,它是為了給讀操作提供緩沖,避免頻繁讀硬盤,提高讀取效率。 
2.降低時(shí)間復(fù)雜度
????????消息系統(tǒng)使用的持久化數(shù)據(jù)結(jié)構(gòu)通常是和 BTree 相關(guān)聯(lián)的消費(fèi)者隊(duì)列或者其他用于存儲消息源數(shù)據(jù)的通用隨機(jī)訪問數(shù)據(jù)結(jié)構(gòu)。BTree 的操作復(fù)雜度是 O(log N),通常我們認(rèn)為 O(log N) 基本等同于常數(shù)時(shí)間,但這條在磁盤操作中不成立。
????????存儲系統(tǒng)將非常快的cache操作和非常慢的物理磁盤操作混合在一起,當(dāng)數(shù)據(jù)隨著 fixed cache 增加時(shí),可以看到樹的性能通常是非線性的——比如數(shù)據(jù)翻倍時(shí)性能下降不只兩倍。
????????kafka選擇把持久化隊(duì)列建立在簡單的讀取和向文件后追加兩種操作之上,這和日志解決方案相同。這種架構(gòu)的優(yōu)點(diǎn)在于所有的操作復(fù)雜度都是O(1),而且讀操作不會阻塞寫操作,讀操作之間也不會互相影響。
????????在不產(chǎn)生任何性能損失的情況下能夠訪問幾乎無限的硬盤空間,Kafka 可以讓消息保留相對較長的一段時(shí)間(比如一周),而不是試圖在被消費(fèi)后立即刪除。
????????降低大量小型IO操作的影響
????????小型的 I/O 操作發(fā)生在客戶端和服務(wù)端之間以及服務(wù)端自身的持久化操作中。
????????為了避免這種情況,kafka的協(xié)議是建立在一個(gè) “消息塊” 的抽象基礎(chǔ)上,合理將消息分組。將多個(gè)消息打包成一組,而不是每次發(fā)送一條消息,從而使整組消息分擔(dān)網(wǎng)絡(luò)中往返的開銷。
????????這個(gè)簡單的優(yōu)化對速度有著數(shù)量級的提升。批處理允許更大的網(wǎng)絡(luò)數(shù)據(jù)包,更大的順序讀寫磁盤操作,連續(xù)的內(nèi)存塊等等,所有這些都使 KafKa 將隨機(jī)流消息順序?qū)懭氲酱疟P, 再由 consumers 進(jìn)行消費(fèi)。
3.零拷貝
????????字節(jié)拷貝是低效率的操作,在消息量少的時(shí)候沒啥問題,但是在高負(fù)載的情況下,影響就不容忽視。為了避免這種情況,kafka使用 producer ,broker 和 consumer 都共享的標(biāo)準(zhǔn)化的二進(jìn)制消息格式,這樣數(shù)據(jù)塊不用修改就能在他們之間傳遞。
保持這種通用格式可以對一些很重要的操作進(jìn)行優(yōu)化: 持久化日志塊的網(wǎng)絡(luò)傳輸。現(xiàn)代的unix 操作系統(tǒng)提供了一個(gè)高度優(yōu)化的編碼方式,用于將數(shù)據(jù)從 pagecache 轉(zhuǎn)移到 socket 網(wǎng)絡(luò)連接中;在 Linux 中系統(tǒng)調(diào)用 sendfile 做到這一點(diǎn)。
-  傳統(tǒng)IO?(4次上下文切換4次拷貝) 假如將磁盤上的文件讀取出來,然后通過網(wǎng)絡(luò)協(xié)議發(fā)送給客戶端。 一般需要兩個(gè)系統(tǒng)調(diào)用,但是一共4次上下文切換,4次拷貝 read(file, tmp_buf, len); write(socket, tmp_buf, len);
-  要想提高文件傳輸?shù)男阅?#xff0c;就需要減少「用戶態(tài)與內(nèi)核態(tài)的上下文切換」和「內(nèi)存拷貝」的次數(shù)。 
-  mmap(4次上下文切換3次拷貝) mmap()系統(tǒng)調(diào)用函數(shù)會直接把內(nèi)核緩沖區(qū)里的數(shù)據(jù)「映射」到用戶空間,這樣,操作系統(tǒng)內(nèi)核與用戶空間就不需要再進(jìn)行任何的數(shù)據(jù)拷貝操作,它替換了read()系統(tǒng)調(diào)用函數(shù)。 buf = mmap(file, len); write(sockfd, buf, len);
-  sendfile(2次上下文切換3次拷貝) Linux 內(nèi)核版本 2.1 中,提供了一個(gè)專門發(fā)送文件的系統(tǒng)調(diào)用函數(shù) sendfile() 首先,它可以替代前面的 read()和 write()這兩個(gè)系統(tǒng)調(diào)用,這樣就可以減少一次系統(tǒng)調(diào)用,也就減少了 2 次上下文切換的開銷。 其次,該系統(tǒng)調(diào)用,可以直接把內(nèi)核緩沖區(qū)里的數(shù)據(jù)拷貝到 socket 緩沖區(qū)里,不再拷貝到用戶態(tài),這樣就只有 2 次上下文切換,和 3 次數(shù)據(jù)拷貝。 #include <sys/socket.h> ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);它的前兩個(gè)參數(shù)分別是目的端和源端的文件描述符,后面兩個(gè)參數(shù)是源端的偏移量和復(fù)制數(shù)據(jù)的長度,返回值是實(shí)際復(fù)制數(shù)據(jù)的長度。 
-  零拷貝(2次上下文切換2次拷貝) Linux 內(nèi)核 2.4 版本開始起,對于支持網(wǎng)卡支持 SG-DMA 技術(shù)的情況下, sendfile() 系統(tǒng)調(diào)用的過程發(fā)生了點(diǎn)變化,具體過程如下: 
-  -  第一步,通過 DMA 將磁盤上的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)里; 
-  第二步,緩沖區(qū)描述符和數(shù)據(jù)長度傳到 socket 緩沖區(qū),這樣網(wǎng)卡的 SG-DMA 控制器就可以直接將內(nèi)核緩存中的數(shù)據(jù)拷貝到網(wǎng)卡的緩沖區(qū)里,此過程不需要將數(shù)據(jù)從操作系統(tǒng)內(nèi)核緩沖區(qū)拷貝到 socket 緩沖區(qū)中,這樣就減少了一次數(shù)據(jù)拷貝; 
 
-  
4.下一節(jié)預(yù)告
- kafka高效文件存儲設(shè)計(jì)特點(diǎn)
推薦閱讀
- 深入淺出kafka原理-1-初識只作乍見之歡
- 深入淺出kafka原理-2-Kafka為何那么快(高效)
- 深入淺出kafka原理-3-高效文件存儲設(shè)計(jì)特點(diǎn)
- 深入淺出kafka原理-4-kafka網(wǎng)絡(luò)機(jī)制原理
總結(jié)
以上是生活随笔為你收集整理的深入浅出kafka原理-2-Kafka为何那么快(高效)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 土堆Pytorch学习笔记(三)
- 下一篇: Joint Event and Temp
