-
Notifications
You must be signed in to change notification settings - Fork 26
MemTable
MemTable在架构图中是调用频率最高的组件,内存中将多次随机写转换为单次顺序写入,作为整合随机写入的Buffer,以及加速热数据读取的Cache,是LSM最重要的组件之一。
MemTable分为其内部分为主要操作的mem_和只读且正在被转换为SSTable的immut_,Compacter会类似定时检查mem_的数据条数,当到达阈值后,通过乐观锁进行切换。
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数据分配紧凑且简化生命周期管理