LevelDB 学习笔记4:读写
LevelDB 学习笔记4:读写
部分内容参考 LevelDB Handbook
写操作
写操作分为两步
- 先写日志
- 可以防止宕机时内存数据的丢失
- 但如果是异步写,日志可能没有落盘,而是在操作系统的缓存里,如果操作系统宕机,就会丢失这些日志
- 再写到内存数据库中
LevelDB 提供两种写操作
Status Put(const WriteOptions& options,
const Slice& key,const Slice& value);
Status Delete(const WriteOptions& options, const Slice& key);
Delete 会被转换为 value 为空的 Put 操作
- 插入记录的类型会被标记为删除操作
- 但真正的删除操作发生在合并的时候
🔑 Slice 是 LevelDB 中定义的一种基础类型
- 内容是 char 指针加长度
- 不拥有字符串的所有权
- 和 string_view 有些像
批量操作
此外, LevelDB 还支持批量操作
Status Write(const WriteOptions& options, WriteBatch* updates);
实际上,Put 和 Delete 都会被转换为 Write 操作
WriteBatch
WriteBatch 内部是一个字符串,结构如下:
其中,表示长度的整数都是变长的,编码的方式为 Varint
- 1 个字节是一组
- 用 most significant bit 表示是否已经结束
- 剩下的 7 位用来存储整数的有效内容
写日志时,会将 Write 操作 WriteBatch 中的内容转换为单条日志记录
- 保证 Write 操作的原子性
- 如果没做完,但写了日志,DB 崩溃了,重启时,整个 Batch 的操作都会被重做
- 如果没写日志,整个 Batch 的操作都会被抛弃
序列号
LevelDB 中每个操作都会被赋予一个序列号
- 内部维护一个计数器,每次操作 +1
- 由于 LevelDB 中更新和删除数据都是 append 的方式
- 因此同一个 key 可能存在多条记录
- 通过序列号可以区分记录的新旧
- 快照(snapshot)代表 DB 某个时刻下的状态,它也是通过序列号实现的
- 一个序列号就代表数据库的一份快照
- 所有用户主动创建的快照存在一个双向链表里
合并写
当存在多个并发写入的操作时,只允许一个 writer 执行写入,为了提高效率,这个 writer 会将其他 writer 的写入操作合并进来,替他们执行,执行完成后通知他们
合并写的整体流程:
线程会先将自己加入写入队列,如果发现自己是队首就有资格执行写入操作,用写入队列而不是直接加锁来决定哪个线程能写入的好处
- 支持合并写
- 保证 FIFO,最先来的线程可以优先写入,使得响应时间更短
线程写入前会先合并队列中其他 writer 的 WriteBatch,直到
- 合并后的 WriteBatch 达到大小阈值
- 如果发现写入线程的 WriteBatch 比较小,会适当降低大小阈值,避免小的写入操作被过分推迟
- 队列中所有 writer 的 WriteBatch 都已经被合并
- 写入线程不要求同步,但后续的某个 writer 要求同步
留足写入空间
写 memtable 前,会先调用 MakeRoomForWrite()
保证 memtable 里有足够的内存可用
- 如果 L0 文件数量触发
kL0_SlowdownWritesTrigger
阈值,就延迟 1ms 再写入- 一次写入操作只会被延迟一次
- 延迟会移交部分 CPU 时间给后台线程(如果它和写线程在同一 CPU 上)
- 如果当前的 memtable 满了
- 如果上次的 immutable memtable 还没写到磁盘里
- 等待后台合并任务完成
- 因为内存里最多同时存在两个 memtable
- 如果 L0 文件数量触发
config::kL0_StopWritesTrigger
阈值- 等待后台合并任务完成
- 否则,将当前的 memtable 转换为 immutable memtable,并创建新的 memtable
- 如果后台线程没有在运行,就拉起后台线程
- 如果上次的 immutable memtable 还没写到磁盘里
读操作
- 用 user_key 生成一个 internal_key
- 如果
Get()
操作指定了快照,就用快照做序列号 - 否则,用当前最新的序列号
- 如果
- 用 internal_key 做查询操作保证了只会读到比 snapshot 更旧的数据
- LevelDB MVCC 实现的一部分
- 按顺序读取,查到后就不再接着往下查了
- memtable
- immutable memtable
- 按 L0,L1,L2..... 的顺序读取 sstable
- 可能会触发 Seek Compaction
本文来自博客园,作者:路过的摸鱼侠,转载请注明原文链接:https://www.cnblogs.com/ljx-null/p/16163394.html