写点什么

TiKV 源码略读 -Config

  • 2022 年 7 月 11 日
  • 本文字数:3833 字

    阅读完需:约 13 分钟

作者: alexshen 原文来源:https://tidb.net/blog/984a0a04


TiKV 是一个分布式事务型的键值数据库,是 TiDB 的存储层,提供了满足 ACID 约束的分布式事务接口,并且通过 Raft 协议保证了多副本数据一致性以及高可用。关于 TiDB、TiKV 的详细介绍可以从官网查阅,这里就不多赘述了。


知乎上已经有一篇高屋建瓴的文章,由 Ed 写的 TiKV 代码初探,可以从整体了解 TiKV 的内部功能,不过作为喜欢阅读代码的攻城狮来说,更喜欢来一个庖丁解牛式的分析。所以我们从代码级别粗略的分析一下 TiKV。



知乎专栏

TiKV 源码初探

概述本文档主要面向 TiKV 社区开发者,主要介绍 TiKV 的系统架构,源码结构,流程解析。目的是使得开发者阅读文档之后,能对 TiKV 项目有一个初步了解,更好的参与进入 TiKV 的开发中。 需要注意,TiKV 使用 Rust …


我们首先选择了 Config 这部分代码逻辑来分析,一个是相对其他功能模块来说,这部分代码没有太烧脑的算法逻辑,另一个原因是这部分代码是整个 TiKV 启动后马上运行的部分,是最先碰到的代码逻辑,再有就是可以通过配置代码大体了解 TiKV 内部那些重要的功能模块们,从而避免繁杂的细节导致窥测一斑的局限。如果想整体了解配置字段,我们可以从官网上查阅完整的配置说明文档。


TiKV Configuration File


文章中所参考的代码是基于 Oct 29, 2020 master 分支来进行分析的,可能会和最新的代码有出入,读者需要按照实际情况判别。



从配置流程主干流程上可以看到,系统从 cmd/bin/tikv-server 开始运行,进入 cmd/src/setup 获取配置文件参数,之后进入 src/config.rs 执行各个模块的配置逻辑。



Setup Config Main Flow


src/config.rs 内以 TiKvConfig struct 为起始点,从外部读取配置信息后完成配置初始化工作,以下是 TiKvConfig 内部的字段。



所以我们下面按照代码顺序,依次介绍每个模块的作用和配置检查逻辑所做的事情,流程图内镂空的图例是每个流程图的起始点。

readpool

这是一个独立的用于数据读取的线程池,主要解决单一线程池导致的读写性能阻塞问题,具体的设计细节可以查看 Read Pool RFC。


2017-12-22-read-pool.md


readpool 内的 unifed read pool 还处于试验阶段,其他两个 config 都是通过 readpool_config 宏来定义的,所以他们的逻辑都是一样的,针对并发配置做了检查。


storage

从名字上就能看出这块代码主要负责存储相关的内容,打开项目代码,可以看出不光包含 kv 数据落盘的逻辑,还包括 mvcc,txn 一系列相关操作。validate 部分比较简单,就是对数据存储的目录进行检查和校验,另外对 4.0 版本之后的优化配置也进行了检测。



src/storage/config.rs, Config struct


paths and grpc

这部分配置逻辑没有独立成一个 validate 方法,我们作为一个整体看一下主要做了哪几件事情:


  1. 设置 region 拆分检查的大小 =6MB

  2. 配置 config 目录,当空时配置 cfg_path 为 storage.data_dir,默认配置下为 ”./”

  3. 配置 raftdb 目录,当空时配置为 storage.data_dir”/raft”,默认配置下为 ”./raft”

  4. 配置 raft-engine 目录,当空时配置为 storage.data_dir”/raft-engine”,默认配置下为 ”./raft-engine”

  5. 配置 rocksdb 目录,默认配置为 storage.dat_dir”/db”,也就是 ”./db”。但是这个路径不能和 raftdb 放在一起,所以会有一个检查,检查有问题会抛出 ”raft_store.raftdb_path can not same with storage.data_dir/db” 的错误。之后会根据 kv_db_path, raft_store.raftdb_path, raft_engine.config.dir 对目录内的数据库文件进行检查,判断是否存在对应的数据库。


rocksdb

从变量名字上可以知道,这部分的代码逻辑是配置核心数据库的,也就是 rocksdb 的配置。我们可以看到这里主要是对 cf 做检测,cf 的命名猜测应该是 column family 的简称,而主要检查内容就是块大小不能大于 32MB。另外还有对 titan 和 rocksdb 的 unordered_write 配置检查。titan 是 PingCAP 开发的一个用来减少写放大问题的 rocksdb 插件,titan 的理论基础来自于 WiscKey。而 unorder_write 是一个提高 rocksdb 写性能的配置。这里有几篇文章可以扩展阅读一下。


RocksDB Config


How TiKV reads and writes


Tune TiKV Performance


Titan Config


WiscKey: Separating Keys from Values in SSD-conscious Storage


Higher write throughput with unordered_write feature



src/config.rs, DbConfig struct


raftdb

每一个 TiKV 都包含两个 rocksdb 实例,一个是用来存储真实数据的我们称之为 kv rocksdb,就是我们上面介绍的 rocksdb 变量对应的配置;另一个用来存储 raft log,我们称之为 raft rocksdb,也就是这个 raftdb 变量对应的配置,用来存放 multi-raft log 的数据。可以参考下面的文章进行配置和调优工作。


Raftstore Config


Tune TiKV Performance



src/config.rs, RaftDbConfig struct


raft_engine

raft engine 应用了另一个 tikv 的 git 库,所对应的 validate 逻辑也不复杂,就检验了 purge 阈值。但从 raft engine 的 readme 看,这是一个存放 multi-raft log 的数据引擎,用来解决 raftdb 出现的性能问题,最终会替换 raftdb。从文中声称的性能测试结果来看,确实有非常大的提升。


server

从流程图上也可以看出,服务器配置相对来说是所有配置里面算逻辑复杂的,但大体上分成几个部分:对 ip 地址 / 端口的检查,snapshot 收发量检查,被调用的递归深度和时长的检查,grpc 的配置检查。



src/server/config.rs, Config struct


raft_store

这个是所有配置逻辑里面最复杂的部分了,不过主要还是围绕 raft 算法进行配置,包括:Leader 选举(Leader election)、日志同步(Log replication)、安全性(Safety)、日志压缩(Log compaction)、成员变更(Membership change)等。到此我们其实看到有三处地方与 raft 配置有关系的代码,设置 raft 存储目录,raftdb 配置,raftstore 配置,是不是可以把这些代码放在一起来维护?


以下是对于 raft store 逻辑的扩展阅读:


Raftstore Config


关于 raft 算法这里就不详细讲解了,需要深入了解的同学可以参考这篇知乎。


Raft 算法详解


当然 pingcap 在 raft paper 的基础上做了很多优化措施,具体细节可以参考这篇文章。


TiKV 功能介绍 - Raft 的优化



component/raftstore/src/store/config.rs, Config struct


let split_size = ReadableSize::mb(coprocessor::config::SPLIT_SIZE_MB) // SPLIT_SIZE_MB=96
复制代码


pd

pd 是 placement driver 的缩写,用来管理整个 tikv 集群,是整个集群的中央控制器,负责整个集群的调度工作。tikv 内是 pd client 的逻辑,所以对配置的检查逻辑相对比较简单。



component/pd_client/src/config.rs, Config struct


coprocessor

类似于 Hbase,tikv 提供了一个协处理器框架来支持分布式计算。主要的功能是,每个节点在接收到分布式请求处理之后把自己负责的数据先做一次处理。这里不仅能提高整体运算效率,还能有效减少网络开销。需要具体了解 coprocessor,可以参考这篇文章。


TiKV 源码解析系列文章(十四)Coprocessor 概览



components/raftstore/src/coprocessor/config.rs, Config struct


let split_size = ReadableSize::mb(96);
复制代码


security

security 一看就知道是负责安全相关的逻辑,validate 代码里主要检查了证书,密钥等一系列配置。security 的 config struct 和其他模块不太一样,居然放在 lib.rs 里面,是不是可以考虑移出来,跟其他模块保持一致呢?



components/security/src/lib.rs, SecurityConfig struct


import

这里的 import 老实说没有能从代码引用关系找到对应代码文件,当然在浏览全项目文件之后找到了一个有近似逻辑的文件,所以这部分之后还需要跟 tikv 团队确认一下。


import 的主要功能是磁盘加载 rocksdb 的文件,在代码中会看到 sst 这样的缩写,sst 其实是 rocksdb 生成的文件格式名称,但由于不同的应用场景,这种文件格式其实有好几种结构:block-based table, plain table, cuckoo table 和 index block。


block-based table 是 sst 文件的默认格式结构,这种格式下默认 block 大小为 4kb,所有存储在文件内的 key 都是被排序过的,所以利用二叉搜索算法能够快速搜索到对应的键值。具体 block-based table 详解和其他格式结构可以参考一下文章:



validate 检查的内容也很简单,就是 threads 和 window 两个变量。



components/sst_importer/src/config.rs, Config struct


backup

这部份应该是关于数据备份相关的配置代码,里面其实就一个字段 num_threads,默认最多 75%cpu 来做备份的工作。


pessimistic_txn

从名字上看,这是一个悲观锁事务的配置验证。悲观锁和乐观锁的概念是大学数据库课程的基本概念,这里可以看一下 tikv 的解释说明,这篇文章也指出 tikv 主要使用乐观锁,所以这里的配置检查只是保证在使用悲观锁时的锁定时长不能等于 0 毫秒。


Locking



src/server/lock_manager/config.rs, Config struct


gc

这部分代码应该是检查垃圾回收配置的逻辑,关于垃圾回收的官方文档目前只有在 2.1 版本里能找到,不过在用户文档里有相关的内容。


TiDB 垃圾回收 (GC)


GC 机制简介


gc 的任务也比较清楚,就是清理不再需要的旧数据。这里检查的 batch_keys 会在 src/server/gc_worker/gc_worker.rs 内被用来设定批量扫描的范围,所以这个值肯定不能等于 0。


// Scans at most `GcConfig.batch_keys` keys.let (keys, updated_next_key) = reader.scan_keys(next_key, self.cfg.batch_keys)?;
复制代码



src/server/gc_worker/config.rs, GcConfig struct



最后贴上完整的 config 流程图,图有点大,只能看个大概。



发布于: 刚刚阅读数: 3
用户头像

TiDB 社区官网:https://tidb.net/ 2021.12.15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
TiKV源码略读-Config_TiDB 社区干货传送门_InfoQ写作社区