写点什么

TiDB 元信息管理方式

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

    阅读完需:约 6 分钟

作者: TiDB_ 猫原文来源:https://tidb.net/blog/7ea38c14


【是否原创】否


【首发渠道】TiDB 社区


【首发渠道链接】其他平台首发请附上对应链接


【正文】


TiDB 中每个 DatabaseTable 都有元信息,也就是其定义以及各项属性。这些信息也需要持久化,TiDB 将这些信息也存储在了 TiKV 中。


每个 Database / Table 都被分配了一个唯一的 ID,这个 ID 作为唯一标识,并且在编码为 Key-Value 时,这个 ID 都会编码到 Key 中。TiDB 还用一个专门的 (Key, Value) 键值对存储当前所有表结构信息的最新版本号。这个键值对是全局的,每次 DDL 操作的状态改变时其版本号都会加 1。目前,TiDB 把这个键值对持久化存储在 PD Server 中,其 Key 是 “/tidb/ddl/global_schema_version”,Value 是类型为 int64 的版本号值。TiDB 采用 Online Schema 变更算法,有一个后台线程在不断地检查 PD Server 中存储的表结构信息的版本号是否发生变化,并且保证在一定时间内一定能够获取版本的变化。


TiDB 中数据到 (Key, Value) 键值对的映射方案:


  • 表中每一行的数据,以下简称表数据

  • 表中所有索引的数据,以下简称索引数据

  • 表数据与 Key-Value 的映射关系

表数据与 Key-Value 的映射关系

本小节介绍 TiDB 中数据到 (Key, Value) 键值对的映射方案。这里的数据主要包括以下两个方面:


  • 表中每一行的数据,以下简称表数据

  • 表中所有索引的数据,以下简称索引数据

表数据与 Key-Value 的映射关系

在关系型数据库中,一个表可能有很多列。要将一行中各列数据映射成一个 (Key, Value) 键值对,需要考虑如何构造 Key。首先,OLTP 场景下有大量针对单行或者多行的增、删、改、查等操作,要求数据库具备快速读取一行数据的能力。因此,对应的 Key 最好有一个唯一 ID(显示或隐式的 ID),以方便快速定位。其次,很多 OLAP 型查询需要进行全表扫描。如果能够将一个表中所有行的 Key 编码到一个区间内,就可以通过范围查询高效完成全表扫描的任务。


基于上述考虑,TiDB 中的表数据与 Key-Value 的映射关系作了如下设计:


  • 为了保证同一个表的数据放在一起,方便查找,TiDB 会为每个表分配一个表 ID,用 TableID 表示。表 ID 是一个整数,在整个集群内唯一。

  • TiDB 会为表中每行数据分配一个行 ID,用 RowID 表示。行 ID 也是一个整数,在表内唯一。对于行 ID,TiDB 做了一个小优化,如果某个表有整数型的主键,TiDB 会使用主键的值当做这一行数据的行 ID。


每行数据按照如下规则编码成 (Key, Value) 键值对:


Key:   tablePrefix{TableID}_recordPrefixSep{RowID}Value: [col1, col2, col3, col4]
复制代码


其中 tablePrefixrecordPrefixSep 都是特定的字符串常量,用于在 Key 空间内区分其他数据。其具体值在后面的小结中给出。



存储为:


索引数据和 Key-Value 的映射关系

TiDB 同时支持主键和二级索引(包括唯一索引和非唯一索引)。与表数据映射方案类似,TiDB 为表中每个索引分配了一个索引 ID,用 IndexID 表示。


对于主键和唯一索引,需要根据键值快速定位到对应的 RowID,因此,按照如下规则编码成 (Key, Value) 键值对:


Key:   tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValueValue: RowID
复制代码


对于不需要满足唯一性约束的普通二级索引,一个键值可能对应多行,需要根据键值范围查询对应的 RowID。因此,按照如下规则编码成 (Key, Value) 键值对:


Key:   tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID}Value: null
复制代码


上图中表存储在 TiKV 上的索引数据为:


映射关系小结

上述所有编码规则中的 tablePrefixrecordPrefixSepindexPrefixSep 都是字符串常量,用于在 Key 空间内区分其他数据,定义如下:


tablePrefix     = []byte{'t'}recordPrefixSep = []byte{'r'}indexPrefixSep  = []byte{'i'}
复制代码


另外请注意,上述方案中,无论是表数据还是索引数据的 Key 编码方案,一个表内所有的行都有相同的 Key 前缀,一个索引的所有数据也都有相同的前缀。这样具有相同的前缀的数据,在 TiKV 的 Key 空间内,是排列在一起的。因此只要小心地设计后缀部分的编码方案,保证编码前和编码后的比较关系不变,就可以将表数据或者索引数据有序地保存在 TiKV 中。采用这种编码后,一个表的所有行数据会按照 RowID 顺序地排列在 TiKV 的 Key 空间中,某一个索引的数据也会按照索引数据的具体的值(编码方案中的 indexedColumnsValue )顺序地排列在 Key 空间内。


用户头像

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

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

评论

发布
暂无评论
TiDB 元信息管理方式_TiDB 底层架构_TiDB 社区干货传送门_InfoQ写作社区