分布式系統(tǒng)Kafka和ES中,JVM內(nèi)存越大越好嗎?
這篇文章,給大家聊一個生產(chǎn)環(huán)境的實踐經(jīng)驗:線上系統(tǒng)部署的時候,JVM 堆內(nèi)存大小是越大越好嗎?
本文主要討論的是 Kafka 和 Elasticsearch 兩種分布式系統(tǒng)的線上部署情況,不是普通的 Java 應(yīng)用系統(tǒng)。
是否依賴 Java 系統(tǒng)自身內(nèi)存處理數(shù)據(jù)?
先說明一點,不管是我們自己開發(fā)的 Java 應(yīng)用系統(tǒng),還是一些中間件系統(tǒng),在實現(xiàn)的時候都需要選擇是否基于自己 Java 進程的內(nèi)存來處理數(shù)據(jù)。
大家應(yīng)該都知道,Java、Scala 等編程語言底層依賴的都是 JVM,那么只要是使用 JVM,就可以考慮在 JVM 進程的內(nèi)存中來放置大量的數(shù)據(jù)。
還是給大家舉個例子,大家應(yīng)該還記得之前聊過消息中間件系統(tǒng)。
比如說系統(tǒng) A 可以給系統(tǒng) B 發(fā)送一條消息,那么中間需要依賴一個消息中間件,系統(tǒng) A 要先把消息發(fā)送到消息中間件,然后系統(tǒng) B 從這個消息中間件消費到這條消息。
大家看下面的示意圖:
一條消息發(fā)送到消息中間件之后,有一種處理方式,就是把這條數(shù)據(jù)先緩沖在自己的 JVM 內(nèi)存里。
然后過一段時間之后,再從自己的內(nèi)存刷新到磁盤上去,這樣可以持久化保存這條消息,如下圖:
依賴 Java 系統(tǒng)自身內(nèi)存有什么缺陷?
如果用類似上述的方式,依賴 Java 系統(tǒng)自身內(nèi)存處理數(shù)據(jù),比如說設(shè)計一個內(nèi)存緩沖區(qū),來緩沖住高并發(fā)寫入的大量消息,那么是有其缺陷的。
***的缺陷,其實就是 JVM 的 GC 問題,這個 GC 就是垃圾回收,這里簡單說一下它是怎么回事。
大家可以想一下,如果一個 Java 進程里老是塞入很多的數(shù)據(jù),這些數(shù)據(jù)都是用來緩沖在內(nèi)存里的,但是過一會兒這些數(shù)據(jù)都會寫入磁盤。
那么寫入磁盤之后,這些數(shù)據(jù)還需要繼續(xù)放在內(nèi)存里嗎?明顯是不需要的了,此時就會依托 JVM 垃圾回收機制,把內(nèi)存里那些不需要的數(shù)據(jù)給回收掉,釋放掉那些內(nèi)存空間騰出來。
但是 JVM 垃圾回收的時候,有一種情況叫做 stop the world,就是它會停止你的工作線程,就專門讓它進行垃圾回收。
這個時候,它在垃圾回收的時候,有可能你的這個中間件系統(tǒng)就運行不了了。
比如你發(fā)送請求給它,它可能都沒法響應(yīng)給你,因為它的接收請求的工作線程都停了,現(xiàn)在人家后臺的垃圾回收線程正在回收垃圾對象。
大家看下圖:
雖然說現(xiàn)在 JVM 的垃圾回收器一直在不斷的演進和發(fā)展,從 CMS 到 G1,盡可能的在降低垃圾回收的時候的影響,減少工作線程的停頓。
但是你要是完全依賴 JVM 內(nèi)存來管理大量的數(shù)據(jù),那在垃圾回收的時候,或多或少總是有影響的。
所以特別是對于一些大數(shù)據(jù)系統(tǒng),中間件系統(tǒng),這個 JVM 的 GC(Garbage Collector,垃圾回收)問題,真是最頭疼的一個問題。
優(yōu)化為依賴 OS Cache 而不是 JVM
所以類似 Kafka、Elasticsearch 等分布式中間件系統(tǒng),雖然也是基于 JVM 運行的,但是它們都選擇了依賴 OS Cache 來管理大量的數(shù)據(jù)。
也就是說,是操作系統(tǒng)管理的內(nèi)存緩沖,而不是依賴 JVM 自身內(nèi)存來管理大量的數(shù)據(jù)。
具體來說,比如說 Kafka 吧,如果你寫一條數(shù)據(jù)到 Kafka,它實際上會直接寫入磁盤文件。
但是磁盤文件在寫入之前其實會進入 OS Cache,也就是操作系統(tǒng)管理的內(nèi)存空間,然后過一段時間,操作系統(tǒng)自己會選擇把它的 OS Cache 的數(shù)據(jù)刷入磁盤。
然后后續(xù)在消費數(shù)據(jù)的時候,其實也會優(yōu)先從 OS Cache(內(nèi)存緩沖)里來讀取數(shù)據(jù)。
相當(dāng)于寫數(shù)據(jù)和讀數(shù)據(jù)都是依托于 OS Cache 來進行的,完全依托操作系統(tǒng)級別的內(nèi)存區(qū)域來進行,讀寫性能都很高。
此外,還有另外一個好處,就是不要依托自身 JVM 來緩沖大量的數(shù)據(jù),這樣可以避免復(fù)雜而且耗時的 JVM 垃圾回收操作。
大家看下面的圖,其實就是一個典型的 Kafka 的運行流程:
然后比如 Elasticsearch,它作為一個現(xiàn)在***的分布式搜索系統(tǒng),也是采用類似的機制。
大量的依賴 OS Cache 來緩沖大量的數(shù)據(jù),然后在進行搜索和查詢的時候,也可以優(yōu)先從 OS Cache(內(nèi)存區(qū)域)中讀取數(shù)據(jù),這樣就可以保證非常高的讀寫性能。
依賴 OS Cache 的系統(tǒng),JVM 內(nèi)存越大越好?
現(xiàn)在就可以進入我們的主題了,那么比如就以上述說的 Kafka、Elasticsearch 等系統(tǒng)而言,在線上生產(chǎn)環(huán)境部署的時候,你知道它們是大量依賴于 OS Cache 來緩沖大量數(shù)據(jù)的。那么,給它們分配 JVM 堆內(nèi)存大小的時候是越大越好嗎?
明顯不是的,假如說你有一臺機器,有 32GB 的內(nèi)存,現(xiàn)在你如果在搞不清楚狀況的情況下,要是傻傻的認為還是給 JVM 分配越大內(nèi)存越好,此時比如給了 16G 的堆內(nèi)存空間給 JVM,那么 OS Cache 剩下的內(nèi)存,可能就不到 10GB 了,因為本身其他的程序還要占用幾個 GB 的內(nèi)存。
那如果是這樣的話,就會導(dǎo)致你在寫入磁盤的時候,OS Cache 能容納的數(shù)據(jù)量很有限。
比如說一共有 20G 的數(shù)據(jù)要寫入磁盤,現(xiàn)在就只有 10GB 的數(shù)據(jù)可以放在 OS Cache 里,然后另外 10GB 的數(shù)據(jù)就只能放在磁盤上。
此時在讀取數(shù)據(jù)的時候,那么起碼有一半的讀取請求,必須從磁盤上去讀了,沒法從 OS Cache 里讀,誰讓你 OS Cache 里就只能放的下 10G 的一半大小的數(shù)據(jù)啊,另外一半都在磁盤里,這也是沒辦法的,如下圖:
那此時你有一半的請求都是從磁盤上在讀取數(shù)據(jù),必然會導(dǎo)致性能很差。
所以很多人在用 Elasticsearch 的時候就是這樣的一個問題,老是覺得 ES 讀取速度慢,幾個億的數(shù)據(jù)寫入 ES,讀取的時候要好幾秒。
那能不花費好幾秒嗎?你要是 ES 集群部署的時候,給 JVM 內(nèi)存過大,給 OS Cache留了幾個 GB 的內(nèi)存,導(dǎo)致幾億條數(shù)據(jù)大部分都在磁盤上,不在 OS Cache 里,***讀取的時候大量讀磁盤,耗費個幾秒鐘是很正常的。
正確的做法:針對場景合理給 OS Cache 更大內(nèi)存
所以說,針對類似 Kafka、Elasticsearch 這種生產(chǎn)系統(tǒng)部署的時候,應(yīng)該要給 JVM 比如 6GB 或者幾個 GB 的內(nèi)存就可以了。
因為它們可能不需要耗費過大的內(nèi)存空間,不依賴 JVM 內(nèi)存管理數(shù)據(jù),當(dāng)然具體是設(shè)置多少,需要你精準(zhǔn)的壓測和優(yōu)化。
但是對于這類系統(tǒng),應(yīng)該給 OS Cache 留出來足夠的內(nèi)存空間,比如 32GB 內(nèi)存的機器,完全可以給 OS Cache 留出來 20 多 G 的內(nèi)存空間。
那么此時假設(shè)你這臺機器總共就寫入了 20GB 的數(shù)據(jù),就可以全部駐留在 OS Cache 里了。
然后后續(xù)在查詢數(shù)據(jù)的時候,不就可以全部從 OS Cache 里讀取數(shù)據(jù)了,完全依托內(nèi)存來走,那你的性能必然是毫秒級的,不可能出現(xiàn)幾秒鐘才完成一個查詢的情況。
整個過程,如下圖所示:
所以說,建議大家在線上生產(chǎn)系統(tǒng)引入任何技術(shù)的時候,都應(yīng)該先對這個技術(shù)的原理,甚至源碼進行深入的理解,知道它具體的工作流程是什么,然后針對性的合理設(shè)計生產(chǎn)環(huán)境的部署方案,保證***的生產(chǎn)性能。
中華石杉:十余年 BAT 架構(gòu)經(jīng)驗,一線互聯(lián)網(wǎng)公司技術(shù)總監(jiān)。帶領(lǐng)上百人團隊開發(fā)過多個億級流量高并發(fā)系統(tǒng)?,F(xiàn)將多年工作中積累下的研究手稿、經(jīng)驗總結(jié)整理成文,傾囊相授。微信公眾號:石杉的架構(gòu)筆記(ID:shishan100)。