Kafka作為一款分布式的消息隊列处窥,是如何做到百萬級TPS呢?玄组,用了哪些黑科技才能做到如此這般獨孤求敗呢滔驾?
1 頁緩存
將磁盤的數(shù)據緩存到內存中,把對磁盤的訪問變?yōu)閷却娴脑L問
kafka在寫數(shù)據的時候俄讹,會先將數(shù)據寫入到頁緩存哆致,滿足一定條件后刷寫到磁盤上,可以保證更高的讀寫性能患膛。
1.1 頁緩存-讀
在啟用頁緩存做讀取的情況下摊阀,會先查看對應的頁,是否在頁緩存中,如果在(命中)胞此,那么直接讀取并返回即可臣咖,避免了對磁盤的訪問;
如果沒有命中漱牵,則向磁盤發(fā)起讀請求夺蛇,并將讀取的數(shù)據存入頁緩存,之后將數(shù)據返回給進程酣胀。
1.2 頁緩存-寫
若一個進程需要將數(shù)據寫入到磁盤刁赦,會檢查對應的數(shù)據是否在頁緩存中,如果不存在灵临,會先在頁緩存添加相應的頁截型,然后將數(shù)據寫入對應的頁;這時候對應的頁就變成了臟頁儒溉;
臟頁達到一定的條件之后就會被操作系統(tǒng)寫入到磁盤宦焦。Linux系統(tǒng)中有幾個參數(shù)可以指定臟頁達到什么條件進行磁盤刷寫:
- vm.dirty_background_ratio:臟頁數(shù)量達到內存數(shù)量的百分之多少后,觸發(fā)pdflush/flush/kdmflush等后臺進程進行刷寫顿涣,默認為10波闹,不會阻塞其他IO
- vm_dirty_ratio:臟頁數(shù)量達到內存百分之多少,強制進行磁盤刷寫涛碑,所有的新IO都會被阻塞精堕,知道臟頁刷寫完畢,默認為20
- vm.dirty_expire_centisecs 指定臟數(shù)據能存活的時間蒲障。在這里它的值是30秒歹篓。當 pdflush/flush/kdmflush 在運行的時候,他們會檢查是否有數(shù)據超過這個時限揉阎,如果有則會把它異步地寫到磁盤中庄撮。畢竟數(shù)據在內存里待太久也會有丟失風險。
- vm.dirty_writeback_centisecs 指定多長時間 pdflush/flush/kdmflush 這些進程會喚醒一次毙籽,然后檢查是否有緩存需要清理洞斯。
雖然頁緩存的刷寫由操作系統(tǒng)來控制,但是Kafka自身也提供了同步刷盤及間斷性強制刷盤(fsync)的功能坑赡,可以顯著提高消息的可靠性烙如,防止宕機造成的頁緩存沒有及時寫到磁盤造成的數(shù)據丟失。具體的參數(shù)有:
- log.flush.interval.messages:消息達到多少條時將數(shù)據flush到磁盤毅否,默認10000
- log.flush.interval.ms:每隔多長時間亚铁,強制進行一次磁盤flush,默認null
不過建議刷盤任務還是由操作系統(tǒng)來做搀突,消息的可靠性應該由多分本機制來保證刀闷,而不是影響性能的同步刷盤操作熊泵。
1.3 為什么要用頁緩存
為什么要使用頁緩存,而不是在JVM內維護一個緩存呢甸昏?主要原因有以下幾點:
- 相同的數(shù)據顽分,頁緩存在全局只會存儲一份,JVM維護可能不同進程被緩存了多次施蜜;
- Java內存密度太低卒蘸,一個boolean類型,本來一個比特就能表示的翻默,Java需要16字節(jié)(8字節(jié)對象頭缸沃,8字節(jié)信息(有7字節(jié)是用于內存對齊))。
- JVM垃圾回收慢修械,且堆越大越慢
- Jvm重啟趾牧,頁緩存依舊存在
- 頁緩存和文件之間的一致性有操作系統(tǒng)來保障,簡化代碼邏輯肯污,同時比進程內維護更加安全有效
1.4 關于Swap
內存交換本身是為了再不足的時候將數(shù)據臨時寫入到磁盤翘单,避免影響服務。但它的作用和我們使用頁緩存的初衷背道而馳蹦渣,應當盡量避免這種內存交換哄芜,可以設置vm.swappiness=1來防止內存耗盡中止某些進程,也可以最大限度的限制對Kafka的影響
2 零拷貝
通過零拷貝技術柬唯,可以去掉那些沒有必要的數(shù)據復制操作认臊,同時減少上下文的切換次數(shù)
設想一種情況,需要將服務器磁盤的一個文件展示給用戶锄奢。
正常的做法是:
- 調用read()失晴,將文件A的內容復制到內核態(tài)模式的ReaderBuffer中;
- CPU控制將內核模式數(shù)據復制到用戶模式中拘央;
- 調用write()师坎,將用戶模式下的內容復制到內核模式下的SocketBuffer中;
- 將內核模式下的SocketBuffer的數(shù)據復制到網卡設備中進行發(fā)送堪滨;
整個過程,一共進行了4次復制蕊温,內核和用戶模式的上下文切換也是4次袱箱;
采用零拷貝
如果不拷貝到用戶控件,有內核模式的ReaderBuffer直接拷貝到SocketBuffer是否可行呢义矛?
其實是可行的发笔,但這依次有3次復制。直到引入DMA技術凉翻,才將拷貝減少到2次:
- 使用DMA技術將文件內容復制都內核的ReaderBuffer中
- DMA將數(shù)據從內核的ReaderBuffer直接發(fā)送到網卡設備
整個過程了讨,進行了2次復制,上下文切換也是2次。針對內核而言前计,數(shù)據在內核模式下實現(xiàn)了零拷貝胞谭。
DMA(Direct Memroy Access,直接內存讀饶需尽)
DMA就是為了解決批量數(shù)據的IO問題丈屹,允許不同速度的硬件溝通。不需要依賴CPU來進行復制伶棒,只需要CPU初始化這個傳輸動作旺垒,而傳輸本身是有DMA控制器來實現(xiàn)和完成的,無需CPU直接控制傳輸肤无,也沒有終端處理方式那樣保留現(xiàn)場和恢復現(xiàn)場的過程先蒋,通過硬件為RAM和IO設備開辟一條直接傳輸數(shù)據的通道,是的CPU效率大大提升宛渐。
如果不使用DMA竞漾,CPU需要從來源把數(shù)據復制到暫存器,再將其寫會新的地方皇忿,這個時間內畴蹭,CPU無法處理其他工作。
零拷貝使用的場景有:
- 數(shù)據較大鳍烁,讀寫較慢叨襟,追求速度
- 內存不足,不能加載太大數(shù)據
- 寬帶不足幔荒,存在其他線程大量的IO操作糊闽,導致寬帶不足
3 磁盤順序讀寫
磁盤的順序寫,要比內存隨機寫都要快
在磁盤上順序讀寫爹梁,不需要移動磁盤臂來尋找數(shù)據位置右犹,并且操作系統(tǒng)對于線性讀寫也做了很多優(yōu)化,比如預讀(read-ahead)技術姚垃,提前將一個比較大的磁盤讀入內存和后寫(write-behind)念链,將很多小的邏輯寫操作后合并組成一個大的物理寫操作。
參考: