Skip to content
This repository has been archived by the owner on Aug 4, 2024. It is now read-only.

MemTable

Kould edited this page Apr 9, 2023 · 2 revisions

MemTable在架构图中是调用频率最高的组件,内存中将多次随机写转换为单次顺序写入,作为整合随机写入的Buffer,以及加速热数据读取的Cache,是LSM最重要的组件之一。

MemTable分为其内部分为主要操作的mem_和只读且正在被转换为SSTable的immut_,Compacter会类似定时检查mem_的数据条数,当到达阈值后,通过乐观锁进行切换。

MemTable的Key封装

InternalKey = |User Key|Sequence| 插入数据时将用户输入的Key在低位生成一个时间有序(升序)Sequence用于实现MemTable的版本查询(MVCC)

事务查询(指定时段最新数据)

创建事务后,为了避免此后插入的数据干扰事务的数据读取

通过 |User Key|事务Sequence| 获取向下最接近的数据并判断User Key是否相等后返回

因为SkipMap通过Key进行顺序排列,UserKey作为高位会使相同UserKey以Sequence作为排序依据紧凑排列在一块,因此可以通过事务会在创建时获取生成当前的Sequence在其中获取最接近的数据(向下)以避免新数据干扰

pub(crate) fn find_with_inner(key: &[u8], seq_id: i64, inner: &TableInner) -> Option<Bytes> {
   let internal_key = InternalKey::new_with_seq(Bytes::copy_from_slice(key), seq_id);
   if let Some(value) = MemTable::find_(&internal_key, &inner.mem_table) {
      Some(value)
   } else if let Some(mem_map) = &inner.immut_table {
      MemTable::find_(&internal_key, mem_map)
   } else {
      None
   }
}

普通查询(最新数据)

获取最接近的 |User Key|最大Sequence| 的数据并判断User Key是否相等后返回

pub(crate) fn find(&self, key: &[u8]) -> Option<Bytes> {
   // 填充SEQ_MAX使其变为最高位以尽可能获取最新数据
   let internal_key = InternalKey::new_with_seq(Bytes::copy_from_slice(key), SEQ_MAX);
   self.loop_read(|inner| {
      Self::find_(&internal_key, &inner.mem_table)
         .or_else(|| {
            inner.immut_table.as_ref()
               .and_then(|mem_map| Self::find_(&internal_key, mem_map))
         })
   })
}

内存分配

LevelDB的MemTable中使用了Arena对SkipList进行内存分配,使数据集中与内存块中,符合SkipList本身的连续空间而使数据能够的CPU缓存命中率高

KipDB为了更高的稳定性并没有造轮子去实现SkipMap且手动控制SkipMap内数据的生命周期容易导致内存泄漏(例如用户传入的Value分配问题),为了简单的设计(怕麻烦),因此对键值对使用现成的Tokio Bytes对键值对的二进制数据进行分配:

Bytes是一串连续的内存块且会通过引用计数自行进行内存回收

因此贴近LevelDB数据分配紧凑且简化生命周期管理

Clone this wiki locally