比較直觀的方式是打開 RocksDB 的 native metrics 棍潘,在默認使用 Flink managed memory 方式的情況下肥隆,
state.backend.rocksdb.metrics.block-cache-usage ,
state.backend.rocksdb.metrics.mem-table-flush-pending疏虫,
state.backend.rocksdb.metrics.num-running-compactions
以及 state.backend.rocksdb.metrics.num-running-flushes 是比較重要的相關 metrics。
Flink-1.10 之后,由于引入了 RocksDB 的內存托管機制吊说,在絕大部分情況下, RocksDB 的這一部分 native 內存是可控的优炬,不過受限于 RocksDB 的相關 cache 實現(xiàn)限制(這里暫不展開颁井,后續(xù)會有文章討論),在某些場景下蠢护,無法做到完美控制雅宾,這時候建議打開上文提到的 native metrics,觀察相關 block cache 內存使用是否存在超用情況葵硕,可以將相關內存添加到 taskmanager.memory.task.off-heap.size 中眉抬,使得 Flink 有更多的空間給 native 內存使用
在 RocksDB 中,每個 state 獨享一個 Column Family懈凹,而每個 Column family 使用各自獨享的 write buffer 和 block cache蜀变,上圖中的 window state 和 value state實際上分屬不同的 column family。
從Flink-1.10 開始引入的 managed memory [1][2]介评,會將slot上的RocksDB的實際可用內存限制在 managed memory / number of slots库北,也就是說對于你配置的10個slot爬舰,20GB的process memory,0.75的managed fraction寒瓦,真實的per slot managed memory其實只有不到1.5GB情屹,也就是說你配置的write buffer count以及max write buffer啥的并沒有真正“生效”。RocksDB的write buffer manager會提前將write buffer 置為immutable并flush出去杂腰。應該增大 managed memory / number of slots 來增大單個slot內多個RocksDB的共享可用內存
rocksDB 使用優(yōu)勢:
1垃你、在1.10版本及以后,由于 TaskManager 內存模型重構颈墅,RocksDB 內存默認成為了堆外托管內存的一部分蜡镶,可以免去一些手動調整的麻煩。如果性能仍然不佳恤筛,需要干預官还,則必須將 state.backend.rocksdb.memory.managed 參數設為 false 來禁用 RocksDB 內存托管
2、不受 Java 垃圾回收的影響毒坛,與 heap 對象相比望伦,它的內存開銷更低,并且是目前唯一支持增量檢查點(incremental checkpointing)的選項煎殷。
3屯伞、使用 RocksDB,狀態(tài)大小僅受限于本地可用的磁盤空間大小豪直,這很適合 state 特別大的 Flink 作業(yè)
4劣摇、RocksDBStateBackend:無需擔心 OOM 風險,是大部分時候的選擇弓乙。
參數調優(yōu):
Tuning MemTable:
以下是一些值得注意的參數末融。為方便對比,下文都會將 RocksDB 的原始參數名與 Flink 配置中的參數名一并列出暇韧,用豎線分割勾习。
write_buffer_size | state.backend.rocksdb.writebuffer.size
單個 memtable 的大小,默認是64MB懈玻。當 memtable 大小達到此閾值時巧婶,就會被標記為不可變。一般來講涂乌,適當增大這個參數可以減小寫放大帶來的影響艺栈,但同時會增大 flush 后 L0、L1 層的壓力骂倘,所以還需要配合修改 compaction 參數眼滤,后面再提。
max_write_buffer_number | state.backend.rocksdb.writebuffer.count
memtable 的最大數量(包含活躍的和不可變的)历涝,默認是2诅需。當全部 memtable 都寫滿但是 flush 速度較慢時漾唉,就會造成寫停頓,所以如果內存充足或者使用的是機械硬盤堰塌,建議適當調大這個參數赵刑,如4。
min_write_buffer_number_to_merge | state.backend.rocksdb.writebuffer.number-to-merge
在 flush 發(fā)生之前被合并的 memtable 最小數量场刑,默認是1般此。舉個例子,如果此參數設為2牵现,那么當有至少兩個不可變 memtable 時铐懊,才有可能觸發(fā) flush(亦即如果只有一個不可變 memtable,就會等待)瞎疼。調大這個值的好處是可以使更多的更改在 flush 前就被合并科乎,降低寫放大,但同時又可能增加讀放大贼急,因為讀取數據時要檢查的 memtable 變多了茅茂。經測試,該參數設為2或3相對較好太抓。
Tuning Block/Block Cache:
block 是 sstable 的基本存儲單位空闲。block cache 則扮演讀緩存的角色,采用 LRU 算法存儲最近使用的 block走敌,對讀性能有較大的影響碴倾。
block_size | state.backend.rocksdb.block.blocksize
block 的大小,默認值為4KB掉丽。在生產環(huán)境中總是會適當調大一些影斑,一般32KB比較合適,對于機械硬盤可以再增大到128~256KB机打,充分利用其順序讀取能力。但是需要注意片迅,如果 block 大小增大而 block cache 大小不變残邀,那么緩存的 block 數量會減少,無形中會增加讀放大柑蛇。
block_cache_size | state.backend.rocksdb.block.cache-size
block cache 的大小芥挣,默認為8MB。由上文所述的讀寫流程可知耻台,較大的 block cache 可以有效避免熱數據的讀請求落到 sstable 上空免,所以若內存余量充足,建議設置到128MB甚至256MB盆耽,讀性能會有非常明顯的提升蹋砚。
Tuning Compaction
ompaction 在所有基于 LSM Tree 的存儲引擎中都是開銷最大的操作扼菠,弄不好的話會非常容易阻塞讀寫。建議看官先讀讀前面那篇關于 RocksDB 的 compaction 策略的文章坝咐,獲取一些背景知識循榆,這里不再贅述。
compaction_style | state.backend.rocksdb.compaction.style
compaction 算法墨坚,使用默認的 LEVEL(即 leveled compaction)即可秧饮,下面的參數也是基于此。
target_file_size_base | state.backend.rocksdb.compaction.level.target-file-size-base
L1層單個 sstable 文件的大小閾值泽篮,默認值為64MB盗尸。每向上提升一級,閾值會乘以因子 target_file_size_multiplier(但默認為1帽撑,即每級sstable最大都是相同的)泼各。顯然,增大此值可以降低 compaction 的頻率油狂,減少寫放大历恐,但是也會造成舊數據無法及時清理,從而增加讀放大专筷。此參數不太容易調整弱贼,一般不建議設為256MB以上。
max_bytes_for_level_base | state.backend.rocksdb.compaction.level.max-size-level-base
L1層的數據總大小閾值磷蛹,默認值為256MB吮旅。每向上提升一級,閾值會乘以因子 max_bytes_for_level_multiplier(默認值為10)味咳。由于上層的大小閾值都是以它為基礎推算出來的庇勃,所以要小心調整。建議設為 target_file_size_base 的倍數槽驶,且不能太小责嚷,例如5~10倍。
level_compaction_dynamic_level_bytes | state.backend.rocksdb.compaction.level.use-dynamic-size
這個參數之前講過掂铐。當開啟之后罕拂,上述閾值的乘法因子會變成除法因子,能夠動態(tài)調整每層的數據量閾值全陨,使得較多的數據可以落在最高一層爆班,能夠減少空間放大,整個 LSM Tree 的結構也會更穩(wěn)定辱姨。對于機械硬盤的環(huán)境柿菩,強烈建議開啟。
Generic Parameters
max_open_files | state.backend.rocksdb.files.open
顧名思義雨涛,是 RocksDB 實例能夠打開的最大文件數枢舶,默認為-1懦胞,表示不限制。由于sstable的索引和布隆過濾器默認都會駐留內存祟辟,并占用文件描述符医瘫,所以如果此值太小,索引和布隆過濾器無法正常加載旧困,就會嚴重拖累讀取性能醇份。
max_background_compactions/max_background_flushes | state.backend.rocksdb.thread.num
后臺負責 flush 和 compaction 的最大并發(fā)線程數,默認為1吼具。注意 Flink 將這兩個參數合二為一處理(對應 DBOptions.setIncreaseParallelism() 方法)僚纷,鑒于 flush 和 compaction 都是相對重的操作,如果 CPU 余量比較充足拗盒,建議調大怖竭,在我們的實踐中一般設為4。
state.backend.rocksdb.thread.num
后臺 flush 和 compaction 的線程數. 默認值 ‘1‘. 建議調大
state.backend.rocksdb.writebuffer.count
每個 column family 的 write buffer 數目陡蝇,默認值 ‘2‘. 如果有需要可以適當調大
state.backend.rocksdb.writebuffer.size
每個 write buffer 的 size痊臭,默認值‘64MB‘. 對于寫頻繁的場景,建議調大
state.backend.rocksdb.block.cache-size
每個 column family 的 block cache大小登夫,默認值‘8MB’广匙,如果存在重復讀的場景,建議調大
state.clear() 實際上只能清理當前 key 對應的 value 值恼策,如果想要清空整個 state鸦致,需要借助于 applyToAllKeys 方法,具體代碼片段如下:
管理 RocksDB 內存的 3 種配置
1.block_cache_size 的配置
此配置最終將控制內存中緩存的最大未壓縮塊數涣楷。隨著塊數的不斷增加分唾,內存大小也會增加。因此狮斗,通過預先配置绽乔,您可以保持固定的內存消耗水平。
2.write_buffer_size 的配置
這種配置控制著 RocksDB 中 MemTable 的最大值碳褒∑矗活躍 MemTables 和只讀的 MemTables 最終會影響 RocksDB 中的內存大小,所以提前調整可能會在以后為您避免一些麻煩骤视。
3.max_write_buffer_number 的配置
在 RocksDB 將 MemTables 導出到磁盤上的 SSTable 之前,此配置決定并控制著內存中保留的 MemTables 的最大數量鹃觉。這實際上是內存中“只讀內存表“的最大數量专酗。
https://mp.weixin.qq.com/s/YpDi3BV8Me3Ay4hzc0nPQA -- 調參
https://mp.weixin.qq.com/s/mjWGWJVQ_zSVOgZqtjjoLw
https://mp.weixin.qq.com/s/ylqK9_SuPKBKoaKmcdMYPA
https://mp.weixin.qq.com/s/r0iPPGWceWkT1OeBJjvJGg
rocksDB 讀取機制
RocksDB 中的讀取操作首先訪問活動內存表(Active Memory Table)來反饋查詢。如果找到待查詢的 key盗扇,則讀取操作將由新到舊依次訪問祷肯,直到找到待查詢的 key 為止沉填。如果在任何 MemTable 中都找不到目標 key,那么 READ 操作將訪問 SSTables佑笋,再次從最新的開始翼闹。
SSTables 文件可以:
優(yōu)先去 RocksDB 的 BlockCache 讀取蒋纬;
如果 BlockCache 沒有的話猎荠,就去讀操作系統(tǒng)的文件,這些文件塊又可能被操作系統(tǒng)緩存了蜀备;
最差的情況就是去本地磁盤讀裙匾 ;
SST 級別的 bloom filter 策略可以避免大量的磁盤訪問碾阁。
JVM Overhead 配置大小不夠输虱。這個默認大小是 TM 大小的 10%,但是不會超過 1G脂凶。你的情況是 TM
的總內存比較大宪睹,可以嘗試調大一點。相關配置項:taskmanager.memory.jvm-overhead.[min|max|fraction]
- UDF 中使用了 native memory蚕钦,可能是用戶代碼亭病,也可能是依賴的第三方庫。這種屬于 task off-heap 內存冠桃,默認大小是
0命贴,相關配置項:taskmanager.memory.task.off-heap.size - 如果使用了 RocksDBStateBackend,也有可能 RocksDB 的內存超用食听。Flink 會設置 RocksDB
使用的緩存大小為 managed memory 大小胸蛛,但是我們發(fā)現(xiàn) RocksDB 存在缺陷,在極個別情況下有可能會限制不住樱报≡嵯睿可以嘗試關閉
RocksDB 的內存控制,這樣 RocksDB 會使用默認緩存大小迹蛤,不會隨著 Flink TM
的增大而增大民珍。配置項:state.backend.rocksdb.memory.managed