Rocksdb 的一些参数调优策略
文章目錄
- 寫性能優(yōu)化
- CF write buffer size
- DB write buffer size
- 讀性能優(yōu)化
- block cache
- bloom filter
- Compression 壓縮
- Compaction優(yōu)化
- 通用workload的配置
本文在rocksdb 整個讀寫鏈路基礎上給出一些簡單的調(diào)優(yōu)策略,主要是通過調(diào)整一些 參數(shù)來滿足我們大多數(shù)workload的性能需求。
更詳細的源代碼分析暫不涉及,一部分調(diào)優(yōu)機制的源代碼分析之前的部分文章中已經(jīng)有過描述,需要的話會在文中提及。6.4及以上版本的實現(xiàn)中有超過200個參數(shù),對于大多數(shù)使用rocksdb的同學來說實在是負擔太重,想要通過分析源代碼來明確每一個參數(shù)的深層含義以及在不同workload下的其表現(xiàn)最優(yōu)的值顯然是不可能的,所以只需要明確核心鏈路的可優(yōu)化點即可。
通過本文,能夠清楚 讀寫性能調(diào)優(yōu),compaction的核心調(diào)優(yōu),通用的優(yōu)化配置。
寫性能優(yōu)化
我們知道rocksdb是基于LSM架構的單機存儲引擎,其擁有高效的寫吞吐。為了保證一致性,整個寫入鏈路會先寫WAL,再寫memtale即可返回。其中WAL如果開了sync,則需要走一次I/O ,memtable是一個內(nèi)存數(shù)據(jù)結構,通常是跳表實現(xiàn)的,基本屬于CPU計算。后續(xù)數(shù)據(jù)在memtable的羅盤則通過異步的flush來進行。
wal的寫入無法避免,寫入?yún)?shù)上的優(yōu)化就主要集中在寫memtable上了。
rocksdb提供了write buffer size的配置,即單個memtable能夠接受的最大寫入量,并且提供了可以設置每一個DB/ColumnFamily 的寫緩存配置。
CF write buffer size
cf_options.write_buffer_size = 64 << 20; 默認是64M。
可以用來設置以Column Family為單位的緩存大小。設置之前最好預估一下最壞情況下的內(nèi)存使用量,如果發(fā)現(xiàn)內(nèi)存不夠用,那么需要降低這個數(shù)值。
DB write buffer size
db_options.write_buffer_size = 64 << 30;默認值是0。
這個配置用來設置整個db 所有column family 共享的write buffer size, 表示整個db的write buffer size達到閾值,會對當前db的所有cf進行flush。
如果需要改變這個值,可以通過如上參數(shù)的方式將該值設置為64G。
讀性能優(yōu)化
block cache
block cache 主要是用來緩存解壓后的數(shù)據(jù)。blockcache的大小,社區(qū)給的建議是 整個內(nèi)存有效負載大小的1/3。即如果內(nèi)存有效負載是240G(240G的used的情況下操作系統(tǒng)仍能正常工作,不需要回收低優(yōu)先級的進程內(nèi)存),那么blockcache的大小就設置為80G。
block cache的配置可以通過table_options來進行設置,需要注意的是為了保證所有的column family共用一個blockcache,需要讓table_options對象所有的cf_options的table_factory公用。
auto cache = NewLRUCache(128 << 20); // LRUcacheBlockBasedTableOptions table_options;
table_options.block_cache = cache;auto table_factory = new BlockBasedTableFactory(table_options);
cf_options.table_factory.reset(table_factory);
bloom filter
如果你的系統(tǒng)中有查找相關的操作,建議打開bloom filter這個配置。bloom filter主要是用來過濾sst文件中不存在的key的查找請求,可以在O(1)時間內(nèi)完成這個操作,而不需要額外的CPU計算和昂貴的I/O操作。
需要注意的是bloom filter能夠有效提升點查性能,卻無法提升range scan的性能。
通過如下配置來打開bloom filter,并將bloom filter位數(shù)設置為10位。
rocksdb::BlockBasedTableOptions table_options;
table_options.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10, false));auto table_factory = new rocksdb::BlockBasedTableFactory(table_options);
cf_options.table_factory.reset(table_factory);
Compression 壓縮
壓縮的主要目的是為了節(jié)省空間但卻有讀性能 以及 系統(tǒng)CPU和 磁盤I/O的額外消耗,因為需要將讀到的datablock 進行解壓,這個過程會有CPU的計算和I/O代價,所以這個配置選項是一個權衡。
rocksdb提供了不同壓縮算法的選擇:
cf_options.compression這個配置控制的是前n-1層的壓縮算法,建議使用lz4(kLZ4Compression)算法,如果不可用的話再選擇snappy(kSnappyCompression)cf_options.bottommost_compression控制最后一層的壓縮算法,建議使用ZStandard(kZSTD),如果不可用的話可以選擇Zlib(kZlibCompression)
為什么要有這樣的針對不同層的不同壓縮算法的配置?
因為n-1層 的sst文件還是有比較高的概率被讀到,所以空間的壓縮比不需要那么高,為了降低壓縮對系統(tǒng)資源和性能的消耗,編解碼的效率則會優(yōu)先考慮。但文件落到了第n層, 這個文件被讀到的概率就比較低,可以設置相對較高的壓縮比。
Compaction優(yōu)化
關于Compaction 的原理及其所帶來的問題,之前的多篇文章也有詳細描述。
Rocksdb Compaction 源碼詳解(一):SST文件詳細格式源碼解析
Rocksdb Compaction源碼詳解(二):Compaction 完整實現(xiàn)過程 概覽
Rocksdb 的 rate_limiter實現(xiàn) – compaction限速
LSM 優(yōu)化系列(三)SILK- Preventing Latency Spikes in Log-Structured Merge Key-Value Stores ATC‘19
這里也推薦大家使用rocksdb的Rate limiter進行限速,限速能夠達到從I/O層面限制compaction的過程對系統(tǒng)資源CPU和IO的競爭的目的,從而保證客戶端的請求無論是qps還是延時(當然qps和請求延時是有相關性的)能夠較為平穩(wěn)。
大家在使用rocksdb的過程中如果發(fā)現(xiàn)客戶端的請求受到compaction的I/O 競爭,可以選擇RateLimiter的配置接入。
通過如下配置進行設置:
db_options.rate_limiter.reset(rocksdb::NewGenericRateLimiter(rate_bytes_per_sec /* int64_t */,refill_period_us /* int64_t */,fairness /* int32_t */));
核心參數(shù)如下三個:
rate_bytes_per_sec這個是最常用也是大家使用起來最有效的一個參數(shù),用來控制compaction或者flush過程中每秒寫入的量。比如,設置了200M, 表示當compaction 累積的總寫入token達到 200M /s 時才會觸發(fā)系統(tǒng)調(diào)用的write.refill_period_us用來控制 token 更新的頻率;比如設置的rate_bytes_per_sec是10M/s, 且refill_period_us設置的是100ms,那么表示 每100ms即可重新調(diào)用一次compaction的寫入。針對1M的大value 可以立即寫入,而小于1M的數(shù)據(jù)則需要消耗CPU, 累積到1M 觸發(fā)一次寫入。fairness表示低優(yōu)先級請求獲得處理的概率。
RateLimiter 支持接受高優(yōu)先級線程 和 低優(yōu)先級線程的請求,一半flush操作是最高的優(yōu)先級,其次是 L0 --> L1 compaction優(yōu)先級較高,最后則是Higher Level compactions 優(yōu)先級最低。那么這個參數(shù)fairness表示 即使現(xiàn)在有較多的高優(yōu)先級任務在調(diào)度,低優(yōu)先級的任務也有 1 / fairness 的機會能夠被調(diào)度,從而防止被餓死。
更加詳細的原理實現(xiàn)可以參考:
Rocksdb 的 rate_limiter實現(xiàn) – compaction限速
通過測試接入RateLimiter(限速128M),在100B 的9:1讀寫比場景,無壓縮,單db 500G, blockcache 10G的配置下
L5的P9999 讀延時由原來的120ms量級
降到了2-3ms的量級
具體的RateLimiter配置需要在實際場景中進行測試,如果寫入量比較大,帶來的compaction的量也會很大,相應的限速帶寬應該更高。
通用workload的配置
下面的這一些配置也是社區(qū)提供的他們認為比較通用的配置,大家在檢查的過程中會發(fā)現(xiàn)這一些配置和我們的默認配置還是有較大差異。 社區(qū)為了防止新老版本的db 配置差異導致的一些問題,所以基本沒有怎么修改過默認rocksdb的配置項,一直沿用最初的默認配置。
如果用戶需要重新加載一個 新的db,那么建議使用這里的配置,能夠有較好的性能提升,并不建議直接在舊的db上應用。
基本的配置選項如下:
// 動態(tài)調(diào)整L0--Ln的大小,保證L0-->L1能夠及時刷新,不會因為容量不足而阻塞上層的flush。能夠比較好得維護
// LSM tree的樹狀結構
cf_options.level_compaction_dynamic_level_bytes = true;// 最大compaction的線程數(shù)
options.max_background_compactions = 4;
options.max_background_flushes = 2;// flush/compaction 每次sync的數(shù)據(jù)量,即data block累計達到1M 觸發(fā)一次sync
options.bytes_per_sync = 1048576;// compaction 過程中選擇文件的優(yōu)先級,能夠降低寫放大
// 配置了這個參數(shù),會優(yōu)先選擇和下一層sst文件覆蓋度較低的文件進行compaction,減少compaction過程中被反復讀出寫入
options.compaction_pri = kMinOverlappingRatio; // 設置block_size,這里的block是指sst文中的一個個數(shù)據(jù)block(data,index,filter,meta,range del)
table_options.block_size = 16 * 1024;
table_options.cache_index_and_filter_blocks = true;
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
以上通用的配置就社區(qū)的推薦 以及 實際測試過程中發(fā)現(xiàn) 并不會有成倍的性能提升, 在不同的通用workload下?lián)碛休^好的性能。
所以如果擁有較強的研發(fā)實力 以及 對特定的業(yè)務場景擁有強需求 ,那么對rocksdb進行定制化改造是一個更加合適的選擇。就像阿里的x-engine,針對電商業(yè)務的洪峰,泄洪,洋流三個問題 進行了LSM 重寫,這樣的優(yōu)化在特定的業(yè)務場景所產(chǎn)生的收益肯定比通用的rocksdb的收益來的更加徹底。
總結
以上是生活随笔為你收集整理的Rocksdb 的一些参数调优策略的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Go 分布式学习利器(15) -- Go
- 下一篇: Rocksdb 的优秀代码(一) --