暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

TiKV 存储引擎笔记

辣肉面加蛋加素鸡 2021-08-06
722

TiKV 里值得学习、研究的组件非常多,这里列出几个相对重要的知识点,做一个汇总和回顾。

LSM-Tree

更新模式

1. 原地更新:

MySQL 的 B+ tree 就是典型的原地更新模式。

  • 读友好

  • 写性能略差

2. 非原地更新:

LSM-Tree 属于非原地更新,优劣性如下:

  • 写友好(直接append)

  • 读性能略差(不知道准确位置,需要找多个地方)

  • 需要定期重新组织数据

与 SSD 的关系

NAND Flash 是以页 page 为单位进行写入的,而且只能写入空闲的 page ,不能覆盖原先有内容 page。

page 是最小单位,多个 page 组成 block。

在删除做 gc 的时候,因为电压高只能以 block 为担心进行擦除。

所以 LSM-Tree 追加写入的原理就不会出现这种性能浪费。

LSM-Tree 结构

把一棵大树拆成 N 颗小树,第一层是在内存里构建一颗有序小树,随着小树越来越大,内存里的小树会 flash 到磁盘上。磁盘中的树也会不断做 compaction 操作,最终合成一颗大树,最终弥补了读性能一般的问题。

LSM-Tree 会有很多层,每一层的容量都是成倍增长的,越往下容量越大。

RocksDB

  • RocksDB 是基于 levelDB 优化的

  • 是持久化的 key-value 单机存储引擎

  • 是基于 LSM-Tree (Log-Structured-Merge Tree)

  • 支持 MVCC 多版本并发控制

  • 基础操作包括:

    • Get key

    • Put key

    • Delete Key

    • Seek key

    • scan

Write Ahead Log

WAL 简单来说就是会先写入磁盘 log,然后再写入内存,流程如下:

  • 在写 memtable 之前会将该次操作的内容先写入 WAL;

  • 如果进程发生了异常,或者是宿主机发生了异常,都可以通过 redo WAL 来恢复;

  • 每个 memtable 都有对应的 WAL,当 memtable flush 成一个 sstable 文件并持久化完成后,与之对应的 WAL 会被删除;

SSTable:LSM-Tree 里核心的数据结构就是 SSTable,全称是 Sorted String Table。

SSTable是一种拥有持久化,有序且不可变的的键值存储结构,它的key和value都是任意的字节数组,并且了提供了按指定key查找和指定范围的key区间迭代遍历的功能。

RocksDB 后台线程与 compact

RocksDB 中,将内存中的 MemTable 转化为磁盘上的 SST 文件,以及合并各个层级的 SST 文件等操作都是在后台线程池中执行的。后台线程池的默认大小是 8,当机器 CPU 数量小于等于 8 时,则后台线程池默认大小为 CPU 数量减一。通常来说,用户不需要更改这个配置。如果用户在一个机器上部署了多个 TiKV 实例,或者机器的读负载比较高而写负载比较低,那么可以适当调低 rocksdb/max-background-jobs 至 3 或者 4。

WriteStall

Write Stall 是 RocksDB 的流控机制。

RocksDB 的 L0 与其他层不同,L0 的各个 SST 是按照生成顺序排列,各个 SST 之间的 key 范围存在重叠,因此查询的时候必须依次查询 L0 中的每一个 SST。为了不影响查询性能,当 L0 中的文件数量过多时,会触发 WriteStall 阻塞写入。

Write Stall 相关参数:

  • memtable 数量过多

    • 数量达到 max-write-buffer-number

  • L0 文件过多

    • 达到 level0-slowdown-writes-trigger 减缓写入

    • 达到 level0-stop-writes-trigger 停止写入

  • pending compaction bytes 过多

    • 达到 soft_pending_compaction_bytes 减缓写入

    • 达到 hard_pending_compaction_bytes 停止写入

相关参数

  • max-background-jobs:进行后台任务的最大线程数,后台任务包括 compaction 和 flush;

  • max-sub-compactions:同时进行一个 compaction 任务的最大线程数;

  • max-manifest-file-size:MANIFEST 文件的大小限制;

  • max-open-files:能够打开的最大文件句柄数;

  • block-size:数据块大小,RocksDB 是按照 block 为单元对数据进行压缩的,同时 block 也是缓存在 block-cache 中的最小单元;

  • compression-per-level:RocksDB 每一层数据的压缩方式;

  • write-buffer-size:RocksDB memtable 的大小;

  • target-file-szie-base:RocksDB SST 文件的大小;

  • block-cache-size:block-cache 的大小

键值分离思想

TiKV 的 key 和 value 被设计成了分离的模式,实际写入 LSM-Tree 的是 key+index ,这个 index 指向 value。这种设计主要还是为了应对 compaction 下的写放大。

  • 通过将大 value 分离出 LSM-Tree 以减少写放大

  • 对于查询需要额外的 I/O 消耗

    • 因为 value 被存放在其他单独的文件中

    • LSM-Tree 整体更小,可以更多节点被 cache 在内存中

  • 范围查询需要随机 I/O

    • 利用 SSD 的并行性

    • 顺序 IO 和随机 IO 的性能差距在减小

于是 Titan 出现了。

Titan 简介

Titan 是基于 RocksDB 的高性能单机 key-value 存储引擎插件。它是基于 WiscKey 设计理念开发的,相比 Titan ,WiscKey 没有 WAL 这类功能所以需要大幅改造。

Titan 设计目标

  • 将大 value 分离出 LSM-Tree 到 Blob 文件中以减少写放大,并可以选择性内联小value 以便提供更好的读性能。

  • 可以无缝从现有的 RocksDB 实例中升级,不需要额外操作,也不会影响在线业务。

  • 100% 兼容 TiKV 现有使用的 RocksDB 特性。

Titan 架构

Titan 在 Flush 和 Compaction 的时候将 value 分离出 LSM-tree,这样写入流程可以和 RocksDB 保持一致,减少对 RocksDB 的侵入性改动。

Titan 适用场景

  • 前台写入量较大,RocksDB 大量触发 compaction 消耗大量 I/O 带宽或者 CPU 资源,造成 TiKV 前台读写性能较差。

  • 前台写入量较大,由于 I/O 带宽瓶颈或 CPU 瓶颈的限制,RocksDB compaction 进度落后较多频繁造成 write stall。

  • 前台写入量较大,RocksDB 大量触发 compaction 造成 I/O 写入量较大,影响 SSD 盘的寿命。

Titan GC流程

  1. Compaction 完成后根据统计信息更新 Blob 文件被删除 value 的大小并计算 GC score;

  2. 根据score 从大到小选择多个 Blob 文件作为 GC 的候选目标;

  3. 对这些候选目标进行采样,排除那些被删除 value 比例未达到阈值(DISCARD_RATIO)的文件;

  4. 遍历候选文件归并所有有效的 value 成一个新的 Blob;

  5. 将新增的 Blob 文件记录在 MANIFEST 日志中;

  6. 将所有有效的(key,blob-index)回写 LSM-Tree;

  7. 将 GC 删除的 Blob 文件记录在 MANIFEST 日志中;


文章转载自辣肉面加蛋加素鸡,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论