TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析
TiFlash 是 TiDB 的分析引擎,是 TiDB HTAP 形态的关键组件。TiFlash 源码阅读系列文章将从源码层面介绍 TiFlash 的内部实现。在上一期源码阅读 TiFlash 源码阅读(三) DeltaTree 存储引擎设计及实现分析中,我们介绍了 TiFlash 的存储引擎,本文将介绍 TiFlash DDL 模块的相关内容,包括 DDL 模块的设计思路, 以及具体代码实现的方式。
本文基于写作时最新的 TiFlash v6.1.0 设计及源码进行分析。随着时间推移,新版本中部分设计可能会发生变更,使得本文部分内容失效,请读者注意甄别。TiFlash v6.1.0 的代码可在 TiFlash 的 git repo 中切换到 v6.1.0 tag 进行查看。
作者:洪韫妍,TiFlash 研发工程师
Overview
本章节,我们会先对 DDL 模块做一个 overview 的介绍,介绍 DDL 在 TiFlash 中相关的场景,以及 TiFlash 中 DDL 模块整体的设计思想。 这边的 DDL 模块指的是对应负责处理 add column, drop column, drop table, recover table 等这一系列 DDL 语句的模块,也是负责跟各数据库和表的 schema 信息打交道的模块。
DDL 模块在 TiFlash 中的相关场景
DDL 模块整体设计思想
在具体了解 TiFlash DDL 模块的整体设计思想之前,我们先来了解一下 DDL 模块在 TiDB 和 TiKV 中的对应情况,因为 TiFlash 接收到的 schema 的变更信息亦是从 TiKV 节点发送的。
TiDB 中 DDL 模块设计思想
图三这个 add column 的例子里面,原表有 a, b 两列以及两行数据。当我们进行 add column 这个 DDL 操作时,我们不会在原有两行中给新增的 c 列填上默认值。如果后续有读操作会读到这两行的数据,我们则会在读的结果中给 c 列填上默认值。通过这样的方式,我们来避免在 DDL 操作的时候发生 data reorg。诸如 add column, drop column,以及整数类型的扩列操作,都不需要触发 data reorg 的。
但是对于有损变更的 DDL 操作(例如:缩短列长度(后续简称缩列)的操作,可能会导致用户数据截断的 DDL 变更),我们不可避免会发生 data reorg。但是在有损变更的场景下,我们也不会在表的原始列上进行数据修改重写的操作,而是通过新增列,在新增列上进行转换,最后删除原列,对新增列更名的方式来完成 DDL 操作。图四这个缩列 (modify column) 的例子中,我们原表中有 a, b 两列 ,此次 DDL 操作需要把 a 列从 int 类型缩成 tiny int 类型。整个 DDL 操作的过程为:
先新增一列隐藏列 _col_a_0。 把原始 a 列中的数值进行转换写到隐藏列 _col_a_0 上。 转换完成后,将原始的 a 列删除,并且将 _col_a_0 列重命名为 a 列。(这边提到的删除 a 列也并非物理上把 a 列的数值删除,是通过修改 meta 信息的方式来实现的)
我们可以通过图五这个例子,来更好的理解一下这条特性。左边是一个两列的原表,通过 DDL 操作,我们删除了 a 列,新增了 c 列,转换为右边的 schema 状态。这时,我们需要用新的 schema 信息去解析原有的老数据,根据新 schema 中的每个 column id,我们去老数据中找到每个 column id 对应的值,其中 id_2 可以找到对应的值,但 id_3 并没有找到对应的值,因此,就给 id_3 补上该列的默认值。而对于数据中多个 id_1 对应的值, 就选择直接舍弃。通过这样的方式,我们就正确的解析了原来的数据。
TiKV 中 DDL 模块基本情况
TiKV 的写操作本身是不需要 shcema ,因为写入 TiKV 的数据是上层已经完成转换的行存的格式的数据(也就是 kv 中的 v)。 对于 TiKV 的读操作
如果读操作只需要直接把 kv 读出,则也不需要 schema 信息。 如果是需要在 TiKV 中的 coprocesser 上处理一些 TiDB 下发给 TiKV 承担的下推计算任务的时候,TiKV 会需要 schema 的信息。但是这个 schema 信息,会在 TiDB 发送来的请求中包含,所以 TiKV 可以是直接拿 TiDB 发送的请求中的 schema 信息来进行数据的解析,以及做一些异常处理(如果解析失败的话)。因此 TiKV 这一类读操作也不会需要自身提供 schema 相关的信息。
TiFlash 中 DDL 模块设计思想
TiFlash 节点上会保存自己的 schema copy。一部分是因为 TiFlash 对 schema 具有强依赖性,需要 schema 来帮助解析行转列的数据以及需要读取的数据。另一方面也因为 TiFlash 是基于 Clickhouse 实现的,所以很多设计也是在 Clickhouse 原有的设计上进行演进的,Clickhouse 本身设计中就是保持了一份 schema copy。 对于 TiFlash 节点上保存的 schema copy,我们选择通过定期从 TiKV 中拉取最新的 schema(本质其实是拿到 TiDB 中最新的 schema 信息)来进行更新,因为不断持续地更新 schema 的开销是非常大的,所以我们是选择了定期更新。 读写操作,会依赖节点上的 schema copy 来进行解析。如果节点上的 schema copy 不满足当下读写的需求,我们会去拉最新的 schema 信息,来保证 schema 比数据新,这样就可以正确成功解析了(这个就是前面提到的 TiDB DDL 机制提供的保证)。具体读写时对 schema copy 的需求,会在后面的部分具体给大家介绍。
DDL Core Process
本章节中,我们将介绍 TiFlash DDL 模块核心的工作流程。
Local Schema Copy 指的是 TiFlash 节点上存的 schema copy 的信息。
Schema Syncer 模块负责从 TiKV 拉取 最新的 Schema 信息,依此来更新 Local Schema Copy。
Bootstrap 指的是 TiFlash Server 启动的时候,会直接调用一次 Schema Syncer,获得目前所有的 schema 信息。
Background Sync Thread 是负责定期调用 Schema Syncer 来更新 Local Schema Copy 模块。
Read 和 Write 两个模块就是 TiFlash 中的读写操作,读写操作都会去依赖 Local Schema Copy,也会在有需要的时候来调用 Schema Syncer 进行更新。
Local Schema Copy
StorageDeltaMerge
的实例对象,在这个对象中有两个变量,是负责来存储跟 schema 相关的信息的。decoding_schema_snapshot
则是根据 tidb_table_info 以及 StorageDeltaMerge 中的一些信息生成的一个对象。decoding_schema_snapshot 是为了优化写入过程中行转列的性能而提出的。因为我们在做行转列转换的时候,如果依赖 tidb_table_info 获取对应需要的 schema 信息,需要做一系列的转换操作来进行适配。考虑到 schema 本身也不会频繁更新,为了避免每次行转列解析都需要重复做这些操作,我们就用 decoding_schema_snapshot 这个变量来保存转换好的结果,并且在行转列过程中依赖 decoding_schema_snapshot 来进行解析。Schema Syncer
StorageDeltaMerge
对象中来更新 schema 信息以及对应存储层相关的内容。通过 tryLoadSchemaDiffs,会从 TiKV 中拿到这一轮新的 schema 变更信息。 随后遍历所有的 diffs 来一个个进行 applyDiff。 对每个 diff,我们会找到他对应的 table,进行 applyAlterPhysicalTable。 在这其中,我们会 detect 到这轮更新中,跟这个表相关的所有 schema 变更,然后调用 StorageDeltaMerge::alterFromTiDB 来对这张表对应的 StorageDeltaMerge 对象进行变更。 具体变更中,我们会修改 tidb_table_info, 相关的 columns 和主键的信息。 另外我们还会更新这张表的建表语句,因为表本身发生了变化,所以他的建表语句也需要对应改变,这样后续做 recover 等操作的时候才能正确工作。
decoding_schema_snapshot
采用的是懒惰更新的方式,只有在具体的数据要发生写入操作了,需要调用到 decoding_schema_snapshot,它才会去检测自己目前是不是最新的 schema 对应的状态,如果不是,就会根据最新的 tidb_table_info
相关的信息来更新。也是通过这样的方式,我们可以减少很多不必要的转换。比如如果一张表频繁发生了很多 schema change,但是没有做任何的写操作, 那么就可以避免 tidb_table_info
到 decoding_schema_snapshot 之间的诸多计算转换操作。SchemaSyncService
来负责,在 TiFlash Server 启动的最开始阶段,把 syncSchema 这个函数塞到 background thread pool 里面去,保持大概每隔 10s 调用一次,来实现定期更新。Schema on Data Write
第一种可能,如图十一(左)所示,待写入的数据比 schema 新。在 TiDB 的时间线上,先新增了一列 e,随后再插入了 (a,b,c,d,e) 这行数据。但是插入的数据先到到了 TiFlash ,add column e 的 schema 变更还没到 TiFlash 侧,所以就出现了数据比 schema 多一列的情况。 第二种可能,如图十一(右)所示,待写入的数据比 schema 旧。在 TiDB 的时间线上,先插入了这行数据 (a,b,c,d,e),然后 drop column e。但是 drop column e 的 schema 变更先到达 TiFlash 侧, 插入的数据后到达,也会出现了数据比 schema 多一列的情况。
第一种可能,如图十二(左)所示,待写入的数据比 schema 新。在 TiDB 时间线上,先 drop column e,再插入数据(a,b,c,d)。 第二种可能,如图十二(右)所示,待写入的数据比 schema 旧。在 TiDB 时间线上,先插入了数据 (a,b,c,d),然后再插入了 e 列。
第一种情况 Unknown Column。因为 schema 比 待写入的数据新,所以我们可以肯定是因为在这行数据后,又发生了 drop column e 的操作,但是这个 schema change 先到达了 TiFlash 侧,所以导致了 Unknown Column 的场景。因此我们只需要直接把 e 列数据直接删除即可。 第二种情况 Missing Column。这种情况则是由于在这行数据后进行了 add column e 的操作造成的,因此我们直接给多余的列填上默认值即可。 第三种情况 Overflow Column。因为目前我们的 schema 已经比待写入的数据新了,所以再次出现 overflow column 的情况,一定是发生了异常,因此我们直接抛出异常。
decoding_schema_snapshot
。在行转列的过程中,RegionBlockReader
在拿 decoding_schema_snapshot
的时候会先检查 decoding_schema_snapshot 是否跟最新的 tidb_table_info
版本是对齐的,如果没对齐,就会触发 decoding_schema_snapshot 的更新,具体逻辑可以参考 getSchemaSnapshotAndBlockForDecoding 这个函数。Schema on Data Read
tidb_table_info
去建立 stream 进行读取。Schema 相关的流程,我们可以在 InterpreterSelectQuery.cpp 的 getAndLockStorageWithSchemaVersion 以及 DAGStorageInterpreter.cpp
的 getAndLockStorages
中进行进一步的了解。InterpreterSelectQuery.cpp
和 DAGStorageInterpreter.cpp 都是来负责对 TiFlash 进行读表的操作,前者是负责 clickhouse client 连接下读取的流程,后者则是 TiDB 支路中读取的流程。Special Case
但是如果 t3 的时候我们又进行了 recover 的操作,将这张表恢复了,那最后插入的这条 row 数据就丢失了。数据丢失是我们不能接受的结果。因此 TiFlash 对于 drop table 这类的 DDL,会对这张表设上 tombstone,具体的物理回收延后到做 gc 操作的时候再发生。对于 drop table 后这张表上还存在的写操作,我们会继续进行解析和写入,这样在后续做 recover 的时候,我们也不会发生数据的丢失。
小结
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 习主席今年首次出访牵动世界目光 4908147
- 2 梅大高速塌方 官方发布安全提示 4982347
- 3 五一淄博烧烤“梅开二度” 4811678
- 4 文旅深度融合优质活动频繁“上新” 4735932
- 5 狐狸和狗一起生活 一开口狗里狗气 4657346
- 6 西安三步一个公主五步一个贵妃 4519915
- 7 广东高速塌方遇难人数升至36人 4416734
- 8 男子河边钓鱼突遇mini龙卷风 4301748
- 9 72岁老人喜得贵子系谣言 4208773
- 10 网友寻找疑因梅大高速事故失联的人 4116532