MyRocks之memtable切換與刷盤
title: MySQL · myrocks · MyRocks之memtable切換與刷盤
author: 張遠
概述
MyRocks的memtable默認是skiplist,其大小和個數分別由參數write_buffer_size和max_write_buffer_number控製。數據寫入時先寫入active memtable, 當active memtable寫滿時,active memtable會轉化為immutable memtable. immutable memtable數據是不會變化的,最終會刷入level0的sst文件中。
memtable 內存分配
RocksDB有自己的內存分配機製,稱為Arena. Arena由固定的inline_block_和動態的blocks_組成。
inline_block_固定為2048bytes, blocks_由一係列的block組成,這些block大小一般為KBlockSize, 但從arena申請較大內存時(> KBlockSize/4)單獨分配一個所申請大小的block. KBlockSize由參數arena_block_size指定,arena_block_size 不指定時默認為write_buffer_size的1/8.
這裏有兩個重要的概念
- blocks_memory_
Arena當前已分配的內存 - alloc_bytes_remaining_
Arena當前block已分配但未使用的內存,注意不是整個Arena已分配而未使用的內存
RocksDB在實際使用內存中用的是ConcurrentArena, 它是在Arena的基礎上封裝,是線程安全的。
同時ConcurrentArena為了提高並發對內存進行了分片,分片數由cpu個數決定,例如cpu核數為24, 則分片數為32,以下是分片的算法
// find a power of two >= num_cpus and >= 8
auto num_cpus = std::thread::hardware_concurrency();
index_mask_ = 7;
while (index_mask_ + 1 < num_cpus) {
index_mask_ = index_mask_ * 2 + 1;
}
shards_.reset(new Shard[index_mask_ + 1]);
每個分片都有已分配但未使用的內存, 分片越多浪費的內存越多。
一個有趣的例子
測試環境:CPU核數64,write_buffer_size=1G, arena_block_size=0
根據前麵的算法,CPU核數64, 內存分片數為64, arena_block_size 默認為write_buffer_size的1/8,對齊後是131072000
我們用1200個連接進行並發插入,這樣能夠充分使用內存分片數
這是測試某個瞬間取得的內存數據
allocated_memory:1179650048
AllocatedAndUnused:1172297392
write_buffer_size:1048576000
BlockSize:131072000
注意AllocatedAndUnused和allocated_memory是如此的接近,也就是說存在**巨大的內存浪費**。然而這不是最嚴重的,更嚴重的是這種情況導致memtable的切換,後麵會進行分析。
memtable 切換
memtable 發生切換的條件有
1) memtable內存超過write_buffer_size會切換
2) WAL日誌滿,WAL日誌超過rocksdb_max_total_wal_size,會從所有的colomn family中找出含有最老日誌(the earliest log containing a prepared section)的memtable進行切換,詳見HandleWALFull
3) Buffer滿,全局的write buffer超過rocksdb_db_write_buffer_size時,會從所有的colomn family中找出最先創建的memtable進行切換,詳見HandleWriteBufferFull
4) flush memtable前會切換memtable, 下節會介紹
下麵詳細介紹memtable滿切換
- memtable 滿切換
memtable內存超過write_buffer_size會切換,由於arena的內存使用,memtable控製內存使用的算法更加精細,切換條件從源碼中很容易理解
bool MemTable::ShouldFlushNow() const {
// This constant variable can be interpreted as: if we still have more than
// "kAllowOverAllocationRatio * kArenaBlockSize" space left, we'd try to over
// allocate one more block.
const double kAllowOverAllocationRatio = 0.6;
// If arena still have room for new block allocation, we can safely say it
// shouldn't flush.
auto allocated_memory = table_->ApproximateMemoryUsage() +
range_del_table_->ApproximateMemoryUsage() +
arena_.MemoryAllocatedBytes();
// if we can still allocate one more block without exceeding the
// over-allocation ratio, then we should not flush.
if (allocated_memory + kArenaBlockSize <
moptions_.write_buffer_size +
kArenaBlockSize * kAllowOverAllocationRatio) {
return false;
}
// if user keeps adding entries that exceeds moptions.write_buffer_size,
// we need to flush earlier even though we still have much available
// memory left.
if (allocated_memory > moptions_.write_buffer_size +
kArenaBlockSize * kAllowOverAllocationRatio) {
return true;
}
return arena_.AllocatedAndUnused() < kArenaBlockSize / 4;
}
而上一節舉出的例子正好符合切換的條件,正如前麵所說的,內存都分配好了,還沒來得及使用就發生切換了,白忙活了一場。
這裏的現象是雖然write_buffer_size是1G,但最後刷到level0的sst都遠遠小於1G。
那麼如何避免這種情況呢
- 減少內存分片數,不建議
- 調小arena_block_size, 親測可用
這裏有一個原則是arena_block_size*內存分片數應該小於write_buffer_size
-
memtable 切換實現
** NewWritableFile //創建日誌文件
** ConstructNewMemtable //創建memtable
** cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_); //設置immutable
** cfd->SetMemtable(new_mem); //設置新的memtable
flush memtable
immutable memtable會不斷flush到level0的SST文件中
觸發flush的條件有
- WAL日誌滿,WAL日誌超過rocksdb_max_total_wal_size,會從所有的colomn family中找出含有最老日誌(the earliest log containing a prepared section)的column family進行flush,詳見HandleWALFull
- Buffer滿,全局的write buffer超過rocksdb_db_write_buffer_size時,會從所有的colomn family中找出最先創建的memtable的column family進行flush,詳見HandleWriteBufferFull
- 手動設置參數force_flush_memtable_now/rocksdb_force_flush_memtable_and_lzero_now時
- CompactRange時
- 創建checkpoint時
- shutdown時avoid_flush_during_shutdown=0會flush所有memtable
other
rocksdb中設置max_background_flushes=-1可以禁止flush,而MyRocks中rocksdb_max_background_flushes最小值限製為0. 因此,MyRocks若要禁止flush需放開此限製。
最後更新:2017-06-20 10:01:52