MySQL · myrocks · MyRocks之memtable切换与刷盘

概述

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 切换实现
  1. NewWritableFile //创建日志文件
  2. ConstructNewMemtable //创建memtable
  3. cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_); //设置immutable
  4. 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需放开此限制。

时间: 2024-10-28 15:34:13

MySQL · myrocks · MyRocks之memtable切换与刷盘的相关文章

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数据是不

【MySQL】MyRocks 漫谈

一 前言     最近一两年,数据库技术尤其是MySQL方面的发展可谓百花齐放,TokuDB,MyRocks ,MySQL 5.7 GA,MySQL 8.0 doc release 其软件也在开发当中,ALiSQL 开源.其中有功能上的改进的,也有针对Innodb 本身缺陷(主要是存储空间方面的)做优化的,作为数据库技术方面的从业者多少有些应接不暇.结合今年ACMUG 技术大会上的技术分享,Percona官方对MyRocks的表态,阿里在技术上的研究,落地来看,可以明显感觉到Myrocks是一种

MySQL · myrocks · myrocks写入分析

写入流程 myrocks的写入流程可以简单的分为以下几步来完成 将解析后的记录(kTypeValue/kTypeDeletion)写入到WriteBatch中 将WAL日志写入log文件 将WriteBatch中的内容写到memtable中,事务完成 其中第2,3步在提交时完成 WriteBatch与Myrocks事务处理密切相关,事务中的记录提交前都以字符串的形式存储在WriteBatch->rep_中,要么都提交,要么都回滚. 回滚的逻辑比较简单,只需要清理WriteBatch->rep_

MySQL · myrocks · myrocks监控信息

rocksdb本身提供了丰富的监控信息,myrocks通过information_schema下的表和show命令等将这些信息展示出来,下面主要以示例的形式来简单介绍下 先创建测试表 CREATE TABLE t1 (a INT, b CHAR(8), pk INT AUTO_INCREMENT ,PRIMARY KEY(pk) comment 'cf_1', key idx2(b) comment 'cf_2') engine=rocksdb; SHOW STATUS show status

MySQL · myrocks · myrocks index condition pushdown

index condition pushdown Index condition pushdown(ICP)是直到mysql5.6才引入的特性,主要是为了减少通过二级索引查找主键索引的次数.目前ICP相关的文章也比较多,本文主要从源码角度介绍ICP的实现.讨论之前,我们先再温习下. 以下图片来自mariadb 引入ICP之前 引入ICP之后 再来看个例子 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` char(8) DEFAULT NULL,

MySQL · myrocks · myrocks之事务处理

前言 mysql目前支持的事务引擎有innodb,tokudb. rocksdb加入mysql阵营后,mysql支持的事务引擎增长至3个. myrocks目前支持的事务隔离级别有read-committed和repeatable-read. 同innodb一样,myrocks也支持MVCC机制. 可以说,myrocks提供了很好的事务支持,能够满足的一般业务的事务需求. sequence number 谈到rocksdb事务,就必须提及rocksdb中的sequence number机制.roc

MySQL · myrocks · myrocks之备份恢复

myrocks支持逻辑备份和物理备份,逻辑备份仍然采用mysqldump,物理备份采用自己开发的myrocks_hotbackup工具,传统的物理备份工具Xtrabackup不支持rocksdb.由于rocksdb的存储特性,myrocks不管是逻辑备份还是物理备份,与innodb的备份恢复均有较大差别. 逻辑备份 myrocks的mysqldump工具支持rocksdb的逻辑备份,其使用方式与原生的mysqldump备份innodb没有区别,一般的使用方式如下 mysqldump -uroot

MySQL · myrocks · myrocks之Bloom filter

Bloom filter 简介 Bloom filter用于判断一个元素是不是在一个集合里,当一个元素被加入集合时,通过k个散列函数将这个元素映射成一个位数组中的k个点,把它们置为1.检索时如果这些点有任何一个为0,则被检元素一定不在:如果都是1,则被检元素很可能在.这就是布隆过滤器的基本思想. 优点:布隆过滤器存储空间和插入/查询时间都是常数O(k). 缺点:有一定的误算率,同时标准的Bloom Filter不支持删除操作. Bloom Filter通过极少的错误换取了存储空间的极大节省. 设

MySQL · 源码分析 · InnoDB LRU List刷脏改进之路

之前的一篇内核月报MySQL · 引擎特性 · InnoDB Buffer Pool 中对InnoDB Buffer pool的整体进行了详细的介绍.文章已经提到了LRU List以及刷脏的工作原理.本篇文章着重从MySQL 5.7源码层面对LRU List刷脏的工作原理,以及Percona针对MySQL LRU Flush的一些性能问题所做的改进,进行一下分析. 在MySQL中,如果当前数据库需要操作的数据集比Buffer pool中的空闲页面大的话,当前Buffer pool中的数据页就必须