AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁(yè) > 科技  > 軟件

基于Netty的自研流系統(tǒng)緩存實(shí)現(xiàn)挑戰(zhàn): 內(nèi)存碎片與OOM困境

來(lái)源: 責(zé)編: 時(shí)間:2024-07-17 16:54:43 604觀看
導(dǎo)讀1.前言Kafka 作為流處理平臺(tái),在實(shí)時(shí)流計(jì)算和在線業(yè)務(wù)場(chǎng)景,追尾讀追求端到端低延遲。在離線批處理和削峰填谷場(chǎng)景,數(shù)據(jù)冷讀追求高吞吐。兩個(gè)場(chǎng)景都需要很好的數(shù)據(jù)緩存設(shè)計(jì)來(lái)支撐,Apache Kafka 的數(shù)據(jù)存儲(chǔ)在本地文件,通過(guò) mm

1.前言

RVl28資訊網(wǎng)——每日最新資訊28at.com

Kafka 作為流處理平臺(tái),在實(shí)時(shí)流計(jì)算和在線業(yè)務(wù)場(chǎng)景,追尾讀追求端到端低延遲。在離線批處理和削峰填谷場(chǎng)景,數(shù)據(jù)冷讀追求高吞吐。兩個(gè)場(chǎng)景都需要很好的數(shù)據(jù)緩存設(shè)計(jì)來(lái)支撐,Apache Kafka 的數(shù)據(jù)存儲(chǔ)在本地文件,通過(guò) mmap 將文件映射到內(nèi)存中訪問(wèn),天然就可以依托操作系統(tǒng)來(lái)完成文件的緩沖持久化、緩存加載和緩存驅(qū)逐。RVl28資訊網(wǎng)——每日最新資訊28at.com

AutoMQ 采用存算分離的架構(gòu),將存儲(chǔ)分離至對(duì)象存儲(chǔ),本地沒(méi)有數(shù)據(jù)文件,因此無(wú)法像 Apache Kafka 一樣直接使用數(shù)據(jù)文件 mmap 來(lái)進(jìn)行數(shù)據(jù)緩存。這時(shí)候通常緩存對(duì)象存儲(chǔ)的數(shù)據(jù)有兩種做法:RVl28資訊網(wǎng)——每日最新資訊28at.com

  • 第一種是將對(duì)象存儲(chǔ)文件下載到本地文件,然后再通過(guò) mmap 讀取本地文件。這種做法在實(shí)現(xiàn)上比較簡(jiǎn)單,但是需要一塊額外的磁盤來(lái)緩存數(shù)據(jù),然后根據(jù)緩存所需的大小和速率,還需要購(gòu)買磁盤空間和 IOPS,該做法不夠經(jīng)濟(jì);
  • 第二種是根據(jù)流處理的數(shù)據(jù)消費(fèi)特征,直接基于內(nèi)存來(lái)進(jìn)行數(shù)據(jù)緩存。這種做法實(shí)現(xiàn)起來(lái)會(huì)復(fù)雜一些,相當(dāng)于需要實(shí)現(xiàn)一個(gè)類似操作系統(tǒng)的內(nèi)存管理。但是就像萬(wàn)事萬(wàn)物都有兩面性一樣,自己實(shí)現(xiàn)內(nèi)存緩存管理,就可以根據(jù)業(yè)務(wù)場(chǎng)景取得最佳的緩存效率和經(jīng)濟(jì)性。

為了降低運(yùn)維的復(fù)雜性和持有成本,提高緩存的效率,AutoMQ 最終選擇的是第二種做法:“直接基于內(nèi)存來(lái)進(jìn)行數(shù)據(jù)緩存”。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

2.AutoMQ 緩存設(shè)計(jì)

RVl28資訊網(wǎng)——每日最新資訊28at.com

直接基于內(nèi)存來(lái)進(jìn)行數(shù)據(jù)緩存,AutoMQ 針對(duì)追尾讀和冷讀兩個(gè)場(chǎng)景,根據(jù)兩者的數(shù)據(jù)訪問(wèn)特點(diǎn),設(shè)計(jì)了兩套緩存機(jī)制:LogCache 和 BlockCache。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

LogCache 針對(duì)于追尾讀場(chǎng)景設(shè)計(jì),數(shù)據(jù)上傳到對(duì)象存儲(chǔ)的同時(shí),也會(huì)以單個(gè) RecordBatch 的形式在 LogCache 中緩存一份,這樣熱數(shù)據(jù)就可以從直接緩存中獲取,提供極低的端到端延遲。相比操作系統(tǒng)通用的緩存設(shè)計(jì),LogCache 還具備以下兩個(gè)特質(zhì):RVl28資訊網(wǎng)——每日最新資訊28at.com

  • FIFO:針對(duì)追尾讀持續(xù)訪問(wèn)新數(shù)據(jù)的特點(diǎn),LogCache 采用先入先出的緩存驅(qū)逐策略,優(yōu)先保證新數(shù)據(jù)緩存的可用性;
  • 低延遲:LogCache 有獨(dú)占的緩存空間,只負(fù)責(zé)熱數(shù)據(jù)的緩存,避免了冷數(shù)據(jù)讀取影響熱數(shù)據(jù)消費(fèi)的問(wèn)題。

BlockCache 針對(duì)冷讀場(chǎng)景設(shè)計(jì),當(dāng)無(wú)法在 LogCache 中訪問(wèn)到需要的數(shù)據(jù)時(shí),則從 BlockCache 中讀取。BlockCache 相比 LogCache 具備以下兩個(gè)不同點(diǎn):RVl28資訊網(wǎng)——每日最新資訊28at.com

  • LRU:BlockCache 使用 Least Recently Used 策略來(lái)進(jìn)行緩存驅(qū)逐,在多倍 Fanout 的冷讀場(chǎng)景可以獲得更佳的緩存利用率;
  • 高吞吐:冷讀關(guān)注的是吞吐量,因此 BlockCache 會(huì)大塊(~4MB)的從對(duì)象存儲(chǔ)讀取 & 緩存數(shù)據(jù),并且通過(guò)預(yù)讀策略來(lái)提前加載后續(xù)可能讀取的數(shù)據(jù);

Java 程序中在內(nèi)存中緩存數(shù)據(jù)可以選擇堆內(nèi)內(nèi)存或堆外內(nèi)存。為了減輕 JVM GC 的負(fù)擔(dān),AutoMQ 使用堆外內(nèi)存 Direct Memory 來(lái)緩存數(shù)據(jù),并且為了提高 Direct Memory 的申請(qǐng)效率,采用業(yè)界成熟的 Netty PooledByteBufAllocator 從池化內(nèi)存中進(jìn)行內(nèi)存的申請(qǐng)和釋放。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

3.“慘案”發(fā)生

RVl28資訊網(wǎng)——每日最新資訊28at.com

期望是使用 Netty 的 PooledByteBufAllocator 后,AutoMQ 既可以通過(guò)池化來(lái)獲得高效的內(nèi)存分配速度,又有久經(jīng)打磨的內(nèi)存分配策略來(lái)最小化內(nèi)存分配的 Overhead,就可以高枕無(wú)憂無(wú)憂了,然而在 AutoMQ 1.0.0 RC 壓測(cè)過(guò)程中被現(xiàn)實(shí)給了當(dāng)頭一棒。RVl28資訊網(wǎng)——每日最新資訊28at.com

AutoMQ 生產(chǎn)機(jī)型為 2C16G,設(shè)置堆外內(nèi)存使用上限 6GiB -XX:MaxDirectMemorySize=6G,內(nèi)存分配為 2GiB LogCache + 1GiB BlockCache + 1GiB 其他小項(xiàng) ~= 4GiB < 6GiB。理論計(jì)算下,堆外內(nèi)存還綽綽有余,然而在實(shí)際 AutoMQ 1.0.0 RC 版在各種不同負(fù)載下長(zhǎng)時(shí)間運(yùn)行后發(fā)現(xiàn),分配內(nèi)存有 OOM OutOfMemoryError 異常拋出。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

本著優(yōu)先懷疑自己而不是懷疑成熟的類庫(kù)和操作系統(tǒng)的原則。RVl28資訊網(wǎng)——每日最新資訊28at.com

觀測(cè)到異常后,首先懷疑的是代碼中哪里有遺漏調(diào)用 ByteBuf#release。于是調(diào)整 Netty 的泄漏檢測(cè)等級(jí) -Dio.netty.leakDetection.level=PARANOID,檢測(cè)每個(gè)的 ByteBuf 是否有存在被 GC 但是還沒(méi)有被釋放的問(wèn)題。跑了一段時(shí)間未發(fā)現(xiàn)有 Leak 日志,于是乎排除漏釋放的可能。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

接著懷疑點(diǎn)轉(zhuǎn)移到是否代碼中有哪塊內(nèi)存分配量超出了預(yù)期值。Netty 的 ByteBufAllocatorMetric只提供全局的內(nèi)存占用統(tǒng)計(jì),傳統(tǒng)的內(nèi)存分配火焰圖也只能提供特定時(shí)間的內(nèi)存申請(qǐng)量,而我們需要的是某個(gè)時(shí)刻各種類型的內(nèi)存使用量。因此 AutoMQ 將 ByteBuf 的申請(qǐng)收口到自己實(shí)現(xiàn)的 ByteBufAlloc工廠類中,通過(guò)WrappedByteBuf 跟蹤各種類型內(nèi)存的申請(qǐng)和釋放,以此來(lái)記錄當(dāng)前時(shí)刻各個(gè)類型的內(nèi)存使用量。并且將 Netty 的實(shí)際內(nèi)存使用量也記錄下來(lái),這樣就知道 AutoMQ 總體內(nèi)存和分類內(nèi)存的使用量。RVl28資訊網(wǎng)——每日最新資訊28at.com

Buffer usage: ByteBufAllocMetric{allocatorMetric=PooledByteBufAllocatorMetric(usedDirectMemory: 2294284288; ...), // Physical Memory Size Allocated by NettyallocatedMemory=1870424720, // Total Memory Size Requested By AutoMQ1/write_record=1841299456, 11/block_cache=0, ..., // Detail Memory Size Requested By AutoMQpooled=true, direct=true} (com.automq.stream.s3.ByteBufAlloc)

加上分類內(nèi)存統(tǒng)計(jì)后,發(fā)現(xiàn)各種類型的內(nèi)存使用量都在預(yù)期范圍內(nèi)。不過(guò)異常的是,AutoMQ 申請(qǐng)的內(nèi)存量和 Netty 實(shí)際申請(qǐng)的內(nèi)存量有較大的差距,并且隨著運(yùn)行兩者之間的差值越來(lái)越大,甚至有時(shí)候 Netty 實(shí)際升級(jí)的內(nèi)存是 AutoMQ 申請(qǐng)的內(nèi)存量的兩倍,這個(gè)差值為內(nèi)存分配的內(nèi)存碎片。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

最終 OOM 的誘發(fā)原因定位為 Netty PooledByteBufAllocator 的內(nèi)存碎片。初步定位了問(wèn)題的原因,那么問(wèn)題轉(zhuǎn)換為 Netty 為什么會(huì)有內(nèi)存碎片和 AutoMQ 如何規(guī)避內(nèi)存碎片問(wèn)題。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

4.Netty 內(nèi)存碎片

RVl28資訊網(wǎng)——每日最新資訊28at.com

首先我們來(lái)探索一下 Netty 內(nèi)存碎片的原因。Netty 的內(nèi)存碎片分為內(nèi)部碎片和外部碎片:RVl28資訊網(wǎng)——每日最新資訊28at.com

  • 內(nèi)部碎片:由于 size 規(guī)約化對(duì)齊引起的碎片,例如期望分配 1byte,但是底層實(shí)際占用了 16byte,那么內(nèi)部碎片就浪費(fèi)了 15byte;
  • 外部碎片:簡(jiǎn)單的來(lái)說(shuō),所有除了內(nèi)部碎片以外引起的碎片都算外部碎片,通常是由于分配算法導(dǎo)致的內(nèi)存布局碎片導(dǎo)致的;

內(nèi)部碎片和外部碎片,在不同的 Netty 版本有不同的表現(xiàn),下面將以 Netty 4.1.52 版本為分割線簡(jiǎn)要介紹一下 Buddy 分配算法和 PageRun/PoolSubPage 分配算法的工作機(jī)制和內(nèi)存碎片成因。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

4.1 Buddy 分配算法 Netty < 4.1.52

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

Netty < 4.1.52 采用 Buddy 分配算法,算法源自 jemalloc3。Netty 為了提升內(nèi)存申請(qǐng)的效率,會(huì)一次性從操作系統(tǒng)申請(qǐng)一段連續(xù)內(nèi)存(PoolChunk),在上層申請(qǐng) ByteBuf 時(shí),按需將這一段內(nèi)存邏輯拆分返回給上層。默認(rèn) PoolChunk 的大小為 16MB,PoolChunk 邏輯上被劃分為 2048 個(gè) 8KB 大小的 Page,通過(guò)一個(gè)完全二叉樹(shù)來(lái)表示內(nèi)存的使用情況。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

完全二叉樹(shù)的每個(gè)節(jié)點(diǎn)用一個(gè) byte 來(lái)表示節(jié)點(diǎn)的狀態(tài)(memoryMap):RVl28資訊網(wǎng)——每日最新資訊28at.com

  • 初始值為層數(shù),狀態(tài)值 == 層數(shù)代表該節(jié)點(diǎn)完全空閑;
  • 當(dāng)層數(shù) < 狀態(tài)值 < 12 時(shí),代表該節(jié)點(diǎn)被使用了一部分,但仍舊有剩余空間;
  • 當(dāng)狀態(tài)值 == 12 時(shí),代表該節(jié)點(diǎn)已經(jīng)被完全分配;

內(nèi)存分配分為 Tiny [0, 512byte] 、 Small (512byte, 8KB) 、 Normal [8KB, 16M] 和 Huge (16M, Max) 四種類型,其中 Tiny 和 Small 由 PoolSubpage 負(fù)責(zé),Normal 由 PoolChunk 負(fù)責(zé),Huge 直接分配。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

先來(lái)看看小內(nèi)存塊的分配效率,Tiny [0, 512byte] 和 Small (512byte, 8KB) 將一個(gè) Page 通過(guò) PoolSubpage 切分成等長(zhǎng)的邏輯塊,由一個(gè) bitmap 來(lái)標(biāo)記塊的使用情況:RVl28資訊網(wǎng)——每日最新資訊28at.com

  • Tiny 內(nèi)存分配的基礎(chǔ)單位為 16 byte,意味著如果請(qǐng)求大小為 50 byte,實(shí)際分配的是 64 byte,內(nèi)部碎片率為 28%;
  • Small 內(nèi)存分配的基礎(chǔ)單位是 1KB,意味著請(qǐng)求大小為 1.5KB,實(shí)際分配的是 2KB,內(nèi)部碎片率為 25%;

再來(lái)看看中等的內(nèi)存塊 Normal [8KB, 16M],假設(shè)從一個(gè)完全空閑的 PoolChunk 申請(qǐng) 2MB + 1KB = 2049KB 內(nèi)存:RVl28資訊網(wǎng)——每日最新資訊28at.com

  • 2049KB 以 2 為底向上規(guī)格化后變?yōu)?4MB,于是查找目標(biāo)為 Depth-3 的空閑節(jié)點(diǎn);
  • 檢查 index=1 節(jié)點(diǎn),發(fā)現(xiàn)節(jié)點(diǎn)有空閑,則檢查左子樹(shù);
  • 檢查 index=2 節(jié)點(diǎn),發(fā)現(xiàn)節(jié)點(diǎn)有空閑,則繼續(xù)檢查左子樹(shù);
  • 檢查 index=4 節(jié)點(diǎn),發(fā)現(xiàn)節(jié)點(diǎn)未被分配,則將 index=4 的狀態(tài)標(biāo)記為 12,并且將父節(jié)點(diǎn)的狀態(tài)更新為兩個(gè)子節(jié)點(diǎn)中最小的那個(gè),也就是將 index=2 的狀態(tài)變?yōu)?3,同理依次更新父節(jié)點(diǎn)狀態(tài);
  • 分配完成;

從分配結(jié)果可以看出,申請(qǐng) 2049KB 內(nèi)存,實(shí)際標(biāo)記占用 4MB 內(nèi)存,意味著內(nèi)部碎片率為 49.9%。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

假設(shè)再申請(qǐng)一個(gè) 9MB 的內(nèi)存,雖然剛才的 PoolChunk 仍有 12MB 的剩余空間,但是由于 Buddy 內(nèi)存分配算法的原理,index=1 已經(jīng)被占用了部分,此時(shí)只能新開(kāi)一個(gè) PoolChunk 來(lái)分配 9MB 的內(nèi)存。分配后的外部碎片率為 1 - (4MB + 9MB) / 32MB = 59.3%。最終所需內(nèi)存 / 底層實(shí)際占用內(nèi)存 = 有效內(nèi)存利用率 = 僅為 34.3%。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

更進(jìn)一步,在各種不同大小的內(nèi)存塊持續(xù)的分配釋放場(chǎng)景,即使 PoolChunk 實(shí)際分配出去的空間不大,也有可能被零散的內(nèi)存塊邏輯分割,進(jìn)一步增加更多的外部?jī)?nèi)存碎片。以下圖為例,雖然上層應(yīng)用最終只保留了 4 * 8KB,但是已經(jīng)無(wú)法再?gòu)倪@個(gè) PoolChunk 申請(qǐng) 4MB 的內(nèi)存了。RVl28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

4.2 PageRun/PoolSubpage 分配算法 Netty >= 4.1.52

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

Netty >= 4.1.52 參考 jemalloc4 將內(nèi)存分配升級(jí)到 PageRun/PoolSubPage 分配策略。相比原來(lái)的 Buddy 分配算法無(wú)論在小內(nèi)存的分配還是在大內(nèi)存的分配都有低的內(nèi)部 & 外部?jī)?nèi)存碎片率。RVl28資訊網(wǎng)——每日最新資訊28at.com

PageRun/PoolSubpage 分配算法相比原來(lái) Buddy 分配算法:RVl28資訊網(wǎng)——每日最新資訊28at.com

  • Chunk 默認(rèn)大小從 16MB 變?yōu)?4MB;
  • 保留了 Chunk 和 Page,增加了 Run 的概念,一串連續(xù)的 Pages 組成一個(gè) Run,通過(guò) Run 來(lái)分配 Normal (28KB, 4MB)  中等內(nèi)存;
  • 將 Tiny 和 Small 級(jí)別的內(nèi)存塊替換成可跨多個(gè) Page & [16byte ... 28KB] 共 38 級(jí)基礎(chǔ)分配大小的 PoolSubpage;

圖片圖片RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

首先仍舊是先看看小內(nèi)存塊的分配效率,以申請(qǐng) 1025 byte 為例:RVl28資訊網(wǎng)——每日最新資訊28at.com

  •  首先 1025 會(huì)根據(jù) PoolSubpage 級(jí)別規(guī)約到 1280 這個(gè)基礎(chǔ)分配大小;
sizeIdx2sizeTab=[16, 32, 48, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792, 2048, 2560, 3072, 3584, 4096, 5120, 6144, 7168, 8192, 10240, 12288, 14336, 16384, 20480, 24576, 28672, ...]
  • 然后 PoolChunk 會(huì)對(duì) 1280 byte 和 Page Size 8K 取最小公倍數(shù) 40KB 來(lái)決定該 PoolSubPage 包含  5 個(gè) Pages;
  • 從 PoolChunk 中分配 5 個(gè)連續(xù)的 Pages,并通過(guò) bitmapIdx 記錄已分配出去的 element;
  • 至此分配完成,內(nèi)部碎片率為 1 -  1025 / 1280 = 19.9%

得益于 PoolSubpage 相比原來(lái)分級(jí)更加精細(xì),從原來(lái)的 2 級(jí)變成 38 級(jí),小內(nèi)存塊的分配效率大大提高。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

然后再來(lái)看看中等的內(nèi)存塊 Normal (28KB, 4M] 的內(nèi)存分配效率。假設(shè)從一個(gè)完全空閑的 PoolChunk 申請(qǐng) 2MB + 1KB = 2049KB 內(nèi)存:RVl28資訊網(wǎng)——每日最新資訊28at.com

  • 2049KB 按照 8KB 向上規(guī)整后,發(fā)現(xiàn)需要 257 個(gè) Pages;
  • PoolChunk 中找到滿足大小的 Run Run{offset=0, size=512};
  • PoolChunk 將 Run 拆分成 Run{offset=0, size=257} 和 Run{offset=257, size=255},第一個(gè) Run 返回給請(qǐng)求方,第二個(gè) Run 加入到空閑 Run 列表(runsAvail)中;
  • 至此分配完成,內(nèi)部碎片率為 1 - 2049KB / (257 * 8K) = 0.3%;

通過(guò) PageRun 機(jī)制,Netty 可以控制大于 28KB 的內(nèi)存塊分配的內(nèi)存浪費(fèi)不超過(guò) 8KB,內(nèi)部碎片率小于 22.2%。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

假設(shè)再申請(qǐng)一個(gè) 1MB 的內(nèi)存,這時(shí)候 PoolChunk 仍舊運(yùn)行相同的邏輯將 Run{offset=257, size=255} 拆分成 Run{offset=257, size=128} 和 Run{offset=385, size=127},前者返回給上層,后者加入到空閑 Run 列表。此時(shí)外部碎片率為 25%。如果按照老的 Buddy 算法,在 PoolChunk 的大小為 4MB 的場(chǎng)景下,就需要新開(kāi)一個(gè) PoolChunk 了,外部碎片率為 62.5%。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

雖然 PageRun/PoolSubpage 分配算法在大小內(nèi)存上相比原有的 Buddy 分配算法有更低的內(nèi)部?jī)?nèi)存碎片率和外部?jī)?nèi)存碎片率,但是畢竟不像 JVM 內(nèi)通過(guò) GC 來(lái) Compact 零散的內(nèi)存,仍舊會(huì)出現(xiàn)在各種不同大小的內(nèi)存塊持續(xù)的分配釋放場(chǎng)景,將 PoolChunk 中的可用 Run 切分很零碎,內(nèi)存碎片率逐漸提升最終導(dǎo)致 OOM。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

5.AutoMQ 應(yīng)對(duì)之道

RVl28資訊網(wǎng)——每日最新資訊28at.com

前面介紹完 Netty 內(nèi)存分配的機(jī)制和內(nèi)存碎片產(chǎn)生的場(chǎng)景,那 AutoMQ 能怎么解決內(nèi)存碎片問(wèn)題的呢?RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

LogCache 針對(duì)追尾讀持續(xù)訪問(wèn)新數(shù)據(jù)的特點(diǎn),采用先入先出的緩存驅(qū)逐策略,換個(gè)角度思考就是在相鄰時(shí)間分配內(nèi)存的會(huì)在相鄰時(shí)間釋放。AutoMQ 采用的策略是抽象一個(gè) ByteBufSeqAlloc:RVl28資訊網(wǎng)——每日最新資訊28at.com

  • ByteBufSeqAlloc 每次向 Netty 申請(qǐng) ChunkSize 大小的 ByteBuf,避免產(chǎn)生外部?jī)?nèi)存碎片,做到零外部?jī)?nèi)存碎片;
  • ByteBufSeqAlloc分配內(nèi)存時(shí),通過(guò)底層 ByteBuf#retainSlice 緊挨著連續(xù)從底層大的內(nèi)存拆分出小的內(nèi)存,避免 size 規(guī)約化產(chǎn)生內(nèi)部?jī)?nèi)存碎片,做到零內(nèi)部?jī)?nèi)存碎片;
  • 釋放的時(shí)候是相鄰的一起釋放,有可能一塊里面大部分都釋放了,但其中少部分還在有效期內(nèi),這時(shí)候整個(gè)大塊都無(wú)法釋放,但這個(gè)大塊的浪費(fèi)有且僅會(huì)存在一個(gè),并且也只會(huì)浪費(fèi)一個(gè) ChunkSize 的大小;

BlockCache 的特點(diǎn)是追求冷讀高吞吐,會(huì)從對(duì)象存儲(chǔ)中大塊讀取數(shù)據(jù)段。AutoMQ 采用的策略是大塊緩存對(duì)象存儲(chǔ)中的原始數(shù)據(jù):RVl28資訊網(wǎng)——每日最新資訊28at.com

  • 按需解碼:等需要查詢時(shí),再解碼成具體的 RecordBatch,通過(guò)降低常駐內(nèi)存塊的數(shù)量來(lái)降低內(nèi)存碎片;
  • 規(guī)整化拆分:未來(lái)可以將大塊緩存規(guī)整化拆分成規(guī)整的 1MB 內(nèi)存塊,來(lái)避免各種不同大小的內(nèi)存塊持續(xù)的分配釋放導(dǎo)致的內(nèi)存碎片率逐漸提升;

圖片圖片RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

可以看到 LogCache 和 BlockCache 優(yōu)化的本質(zhì)都是根據(jù)自身緩存的特點(diǎn)通過(guò)大塊 & 規(guī)整的內(nèi)存分配來(lái)規(guī)避 Netty 內(nèi)存分配策略帶來(lái)的內(nèi)存碎片問(wèn)題。通過(guò)該方式,AutoMQ 在追尾讀、冷讀和大小消息等各種場(chǎng)景長(zhǎng)期運(yùn)行,也能將堆外內(nèi)存的內(nèi)存碎片率控制在 35% 以下,再也沒(méi)有出現(xiàn)過(guò)堆外內(nèi)存 OOM。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

6.總結(jié)

RVl28資訊網(wǎng)——每日最新資訊28at.com

Netty 的 PooledByteBufAllocator 不是銀彈,使用的時(shí)候需要考慮內(nèi)存碎片帶來(lái)的實(shí)際內(nèi)存占用的空間放大,規(guī)劃預(yù)留出合理的 JVM 內(nèi)存大小。如果只是使用 Netty 作為網(wǎng)絡(luò)層框架,由 PooledByteBufAllocator 分配的內(nèi)存生命周期會(huì)比較短,因此內(nèi)存碎片引起的內(nèi)存放大實(shí)際并不會(huì)很明顯,不過(guò)仍舊建議使用 Netty 的系統(tǒng)都將版本升級(jí)到 4.1.52 之上,以獲得更好的內(nèi)存分配效率。如果使用 Netty 的 PooledByteBufAllocator 來(lái)做緩存,建議根據(jù)緩存的特征,使用大塊內(nèi)存分配然后再自行連續(xù)拆分,來(lái)規(guī)避 Netty 的內(nèi)存碎片。RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

RVl28資訊網(wǎng)——每日最新資訊28at.com

參考文檔

  1. https://netty.io/wiki/reference-counted-objects.html
  2. https://netty.io/news/2020/09/08/4-1-52-Final.html

本文鏈接:http://www.tebozhan.com/showinfo-26-101380-0.html基于Netty的自研流系統(tǒng)緩存實(shí)現(xiàn)挑戰(zhàn): 內(nèi)存碎片與OOM困境

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 兩位巨佬吃了一頓晚飯,整個(gè)互聯(lián)網(wǎng)徹底被改變了!

下一篇: 如何利用負(fù)載均衡器實(shí)現(xiàn)終極自由

標(biāo)簽:
  • 熱門焦點(diǎn)
Top