TiFlash 是 TiDB 的剖析引擎,是 TiDB HTAP 形状的关键组件。TiFlash 源码阅览系列文章将从源码层面介绍 TiFlash 的内部完结。在上一期源码阅览中,咱们介绍了 TiFlash 的存储引擎,本文将介绍 TiFlash DDL 模块的相关内容,包含 DDL 模块的规划思路, 以及详细代码完结的办法。

本文基于写作时最新的 TiFlash v6.1.0 规划及源码进行剖析。随着时刻推移,新版别中部分规划或许会产生改动,使得本文部分内容失效,请读者留意鉴别。TiFlash v6.1.0 的代码可在 TiFlash 的 git repo 中切换到 v6.1.0 tag 进行检查。

Overview

本章节,咱们会先对 DDL 模块做一个 overview 的介绍,介绍 DDL 在 TiFlash 中相关的场景,以及 TiFlash 中 DDL 模块全体的规划思维

这边的 DDL 模块指的是对应担任处理 add column, drop column, drop table recover table 等这一系列 DDL 句子的模块,也是担任跟各数据库和表的 schema 信息打交道的模块。

DDL 模块在 TiFlash 中的相关场景

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图一 TiFlash 架构示意图

图一 是 TiFlash 的架构示意图,上方是 TiDB/TiSpark 的核算层节点,虚线的左面是四个 TiKV 的节点,右边便是两个 TiFlash 节点。这张图表现的是TiFlash 一个重要的规划理念:经过利用 Raft 的一致算法,TiFlash 会作为 Raft 的 Learner 节点参加 Raft group 来进行数据的异步仿制。Raft Group 指的是 TiKV 中由多个 region 副本组成的 raft leader 以及 raft follower 组成的 group。从 TiKV 同步到 TiFlash 的数据,在 TiFlash 中相同是依照 region 区分的,可是在内部会经过列存的办法来存到 TiFlash 的列式存储引擎中。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图二 TiFlash 架构示意图(含 Schema)

图二是一个概览的架构规划,掩盖了许多细节部分。其间图中这两个红圈对应的部分,便是本文要讨论的主角 DDL模块

下方的红圈是关于 TiFlash 的写操作。TiFlash 节点是以 learner 角色参加到了 TiKV 中一个个 region 对应的 raft group 中,经过 raft leader 不断发送 raft log 或许 raft snapshot 来给 learner 节点同步数据。 可是由于 TiKV 中数据都是行存的格局,而咱们 TiFlash 中需求的数据则是列存的格局,所以 TiFlash 节点在接收到 TiKV 发送过来的这个行存格局的数据今后,需求把他进行一个行转列的转化,转化成需求的列存的格局。而这个转化,就需求依靠对应表的 schema 信息来完结。相同,上方的红圈指的是在 TiDB/TiSpark 来 TiFlash 中读取数据的进程,这个读数据的进程相同也是依靠 schema 来进行参与解析的。因而,TiFlash 的读写操作都是需求强依靠 schema 的,schema 在 TiFlash 中亦是有重要的作用的

DDL 模块全体规划思维

在详细了解 TiFlash DDL 模块的全体规划思维之前,咱们先来了解一下 DDL 模块在 TiDB 和 TiKV 中的对应状况,由于 TiFlash 接收到的 schema 的改动信息亦是从 TiKV 节点发送的。

TiDB 中 DDL 模块基本状况

TiDB 的 DDL 模块是学习 Google F1 来完结的在分布式场景下,无锁而且在线的 schema 改动。详细的完结能够参阅 TiDB 源码阅览系列文章(十七)DDL 源码解析 | PingCAP。TiDB 的 DDL 机制供给了两大特点:

  1. DDL 操作会尽或许防止产生 data reorg(data reorg 指的是在表中进行数据的增删改)。

    • 图三这个 add column 的比方里边,原表有 a b 两列以及两行数据。当咱们进行 add column 这个 DDL 操作时,咱们不会在原有两行中给新增的 c 列填上默认值。如果后续有读操作会读到这两行的数据,咱们则会在读的成果中给 c 列填上默认值。经过这样的办法,咱们来防止在 DDL 操作的时分产生 data reorg。比方 add column, drop column,以及整数类型的扩列操作,都不需求触发 data reorg 的。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图三 add column 样例
  • 可是关于有损改动的 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 信息的办法来完结的)*

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图四 modify column 样例

别的关于缩列这个 DDL 操作自身,咱们要求缩列进程中不会产生数据的丢失。比方要从 int 缩成 tinyint时,就要求原有列的值都是在 tinyint 规模内的,而不支撑呈现自身超出 tinyint 的值转化成 tinyint 类型。关于后者的状况,会直接报错 overflow,缩列操作失利。

  1. 相对数据更新的 schema 永久能够解析旧的数据。这一条结论亦是咱们后边 TiFlash DDL 模块依靠的一条重要的确保。这个确保是依靠咱们行存数据的格局来完结的。在存数据的时分,咱们是将column id 和 column value 一同存储的,而非column name和column value一同存储。别的咱们的行存格局能够简化的理解为是一个 column_id → data 的一个 map 办法(实际上咱们的行存并非一个 map,而是用二进制编码的办法来存储的,详细能够参阅 Proposal: A new storage row format for efficient decoding)。

    • 咱们能够经过图五这个比方,来更好的理解一下这条特性。左面是一个两列的原表,经过 DDL 操作,咱们删除了 a 列,新增了 c 列,转化为右边的 schema 状况。这时,咱们需求用新的 schema 信息去解析原有的老数据,依据新 schema 中的每个 column id,咱们去老数据中找到每个 column id 对应的值,其间 id_2 能够找到对应的值,但 id_3 并没有找到对应的值,因而,就给 id_3 补上该列的默认值。而关于数据中多个 id_1 对应的值, 就挑选直接舍弃。经过这样的办法,咱们就正确的解析了原来的数据。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图五 新 schema 解析旧数据样例

TiKV 中 DDL 模块基本状况

TiKV 这个行存的存储层,自身是没有在节点中保存各个数据表对应的 schema 信息的,由于 TiKV 自身的读写进程都不需求依靠自身供给的 schema 信息。

  1. TiKV 的写操作自身是不需求 shcema ,由于写入 TiKV 的数据是上层现已完结转化的行存的格局的数据(也便是 kv 中的 v)。
  2. 关于 TiKV 的读操作
    • 如果读操作只需求直接把 kv 读出,则也不需求 schema 信息。
    • 如果是需求在 TiKV 中的 coprocesser 上处理一些 TiDB 下发给 TiKV 承担的下推核算任务的时分,TiKV 会需求 schema 的信息。可是这个 schema 信息,会在 TiDB 发送来的恳求中包含,所以 TiKV 能够是直接拿 TiDB 发送的恳求中的 schema 信息来进行数据的解析,以及做一些反常处理(如果解析失利的话)。因而 TiKV 这一类读操作也不会需求自身供给 schema 相关的信息。

TiFlash 中 DDL 模块规划思维

TiFlash 中 DDL 模块的规划思维首要包含了以下三点:

  1. TiFlash 节点上会保存自己的 schema copy。一部分是由于 TiFlash 对 schema 具有强依靠性,需求 schema 来协助解析行转列的数据以及需求读取的数据。另一方面也由于 TiFlash 是基于 Clickhouse 完结的,所以许多规划也是在 Clickhouse 原有的规划上进行演进的,Clickhouse 自身规划中便是坚持了一份 schema copy。
  2. 关于 TiFlash 节点上保存的 schema copy,咱们挑选经过定时从 TiKV 中拉取最新的 schema(本质其实是拿到 TiDB 中最新的 schema 信息)来进行更新,由于不断继续地更新 schema 的开支是十分大的,所以咱们是挑选了定时更新。
  3. 读写操作,会依靠节点上的 schema copy 来进行解析。如果节点上的 schema copy 不满足当下读写的需求,咱们会去拉最新的schema信息,来确保schema 比数据新,这样就能够正确成功解析了(这个便是前面说到的 TiDB DDL 机制供给的确保)。详细读写时对 schema copy 的需求,会在后边的部分详细给大家介绍。

DDL Core Process

本章节中,咱们将介绍 TiFlash DDL 模块中心的作业流程。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图六 DDL Core Process

图六左面是各个节点的一个缩略展示,右边放大显现了TiFlash 中跟 DDL 相关的中心流程,分别为:

  1. Local Schema Copy 指的是 TiFlash 节点上存的 schema copy 的信息。
  2. Schema Syncer 模块担任从 TiKV 拉取 最新的 Schema 信息,依此来更新 Local Schema Copy。
  3. Bootstrap 指的是 TiFlash Server 启动的时分,会直接调用一次 Schema Syncer,取得现在一切的 schema 信息。
  4. Background Sync Thread 是担任定时调用 Schema Syncer 来更新 Local Schema Copy 模块。
  5. Read 和 Write 两个模块便是 TiFlash 中的读写操作,读写操作都会去依靠 Local Schema Copy,也会在有需求的时分来调用 Schema Syncer 进行更新。

下面咱们就逐个来看每个部分是怎样完结的。

Local Schema Copy

TiFlash 中 schema 信息最首要的是跟各个数据表相关的信息。在 TiFlash 的存储层中,每一个物理的表,都会对应一个 StorageDeltaMerge 的实例对象,在这个对象中有两个变量,是担任来存储跟schema 相关的信息的。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图七 Schema Copy 存储示意图
  1. tidb_table_info 这个变量存的是 table 中各种 schema 信息,包含 table id,table name,columns infos,schema version等等。而且 tidb_table_info 的存储结构跟 TiDB / TiKV 中存储 table schema 的结构是完全一致的。
  2. decoding_schema_snapshot 则是依据 tidb_table_info 以及 StorageDeltaMerge 中的一些信息生成的一个对象。decoding_schema_snapshot 是为了优化写入进程中行转列的性能而提出的。由于咱们在做行转列转化的时分,如果依靠 tidb_table_info 获取对应需求的 schema 信息,需求做一系列的转化操作来进行适配。考虑到 schema 自身也不会频繁更新,为了防止每次行转列解析都需求重复做这些操作,咱们就用 decoding_schema_snapshot 这个变量来保存转化好的成果,而且内行转列进程中依靠 decoding_schema_snapshot 来进行解析。

Schema Syncer

Schema Syncer 这个模块是由 TiDBSchemaSyncer 这个类来担任的。它经过 RPC 去 TiKV 中获取最新的 schema 的更新内容。关于获取到的 schema diffs,会找到每个 schema diff 对应的 table,在 table 对应的 StorageDeltaMerge 对象中来更新 schema 信息以及对应存储层相关的内容。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

Schemas 流程图

整个进程是经过 TiDBSchemaSyncer 函数 syncSchema来完结的,详细的进程能够参阅图八:

  1. 经过 tryLoadSchemaDiffs, TiKV 中拿到这一轮新的 schema 改动信息。
  2. 随后遍历一切的 diffs 来一个个进行 applyDiff
  3. 对每个 diff,咱们会找到他对应的 table,进行 applyAlterPhysicalTable
  4. 在这其间,咱们会 detect 到这轮更新中,跟这个表相关的一切 schema 改动,然后调用 StorageDeltaMerge::alterFromTiDB 来对这张表对应的 StorageDeltaMerge 对象进行改动。
  5. 详细改动中,咱们会修正 tidb_table_info , 相关的 columns 和主键的信息。
  6. 别的咱们还会更新这张表的建表句子,由于表自身产生了改动,所以他的建表句子也需求对应改动,这样后续做 recover 等操作的时分才能正确作业。

在整个 syncSchema 的进程中,咱们是不会更新 decoding_schema_snapshot的。decoding_schema_snapshot选用的是选用的是懒惰更新的办法,只要在详细的数据要产生写入操作了,需求调用到 decoding_schema_snapshot,它才会去检测自己现在是不是最新的 schema 对应的状况,如果不是,就会依据最新的 tidb_table_info 相关的信息来更新。也是经过这样的办法,咱们能够削减许多不必要的转化。比方如果一张表频繁产生了许多 schema change,可是没有做任何的写操作, 那么就能够防止 tidb_table_infodecoding_schema_snapshot 之间的诸多核算转化操作。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

DDL Process

关于周围涉及到调用 Schema Syncer 的模块,Read,Write,BootStrap 这三个模块都是直接的调用 TiDBSchemaSyncer::syncSchema。而 Background Sync Thread 则是经过 SchemaSyncService 来担任,在 TiFlash Server 启动的最开端阶段,把 syncSchema 这个函数塞到 background thread pool里边去,坚持大约每隔10s调用一次,来完结定时更新。

Schema on Data Write

咱们先来了解一下,写的进程自身需求处理的状况。咱们有一个要写入的行格局的数据,需求把他每一列内容进行解析处理,写入列存引擎中。别的咱们节点中有 local schema copy 来协助解析。可是,这行要写入的数据和咱们的 schema copy 在时刻上的先后顺序是不确定的。由于咱们的数据是经过 raft log / raft snapshot 的形式发送过来的,是一个异步的进程。schema copy 则是定时来进行更新的,也能够看作是一个异步的进程,所以对应的 schema 版别和 这行写入的数据 在 TiDB 上产生的先后顺序咱们是不知道的。写操作便是要在这样的场景下,正确的解析数据进行写入。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十 写入数据

关于这样的场景,会有个十分直接的处理思路:咱们能够在做行转列解析前,先拉取最新的 schema,然后确保咱们的 schema 必定比要写入的数据更新,这样必定是能够成功解析的。可是一方面 schema 不是频繁改动的,别的每次写都要拉取 schema 是十分大的开支,所以咱们写操作终究挑选的做法是,咱们先直接用现有的 schema copy 来解析这行数据,如果解析成功了就完毕,解析失利了,咱们再去拉取最新的 schema 来从头解析

在做第一轮解析时,除了正确解析完结以外,咱们还或许遇到以下三种状况:

  1. 第一种状况 Unknown Column, 即待写入的数据比 schema 多了一列 e。产生这种状况的或许有下面两种或许。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十一 unknown column 场景
  • 第一种或许,如图十一(左)所示,待写入的数据比 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 进行第二轮解析。
  1. 第二种状况 Missing Column,即待写入的数据比 schema 少了一列 e。相同,也有两种产生的或许性。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十二 missing column 场景
  • 第一种或许,如图十二(左)所示,待写入的数据比 schema 新。在 TiDB 时刻线上,先 drop column e,再刺进数据(a,b,c,d)。
  • 第二种或许,如图十二(右)所示,待写入的数据比 schema 旧。 在 TiDB 时刻线上,先刺进了数据 (a,b,c,d),然后再刺进了 e 列。

相同咱们这时分也没有办法判别是属于哪种状况,依照前面的做法,咱们还是应该解析失利回来从头拉取在解析了。可是在这种状况下,如果多出来的 e 列 是有默认值的或许是支撑填 NULL 的,咱们能够直接给 e 列填上默认值或许 NULL 来回来解析成功。咱们分别看一下在两种或许性下,咱们这种填默认值或许 NULL 的处理会有什么样的影响。

第一种或许的状况下,由于咱们现已 drop 了 column e,所今后续一切的读操作都不会读到 column e 的操作,所以其实给 e 列填任何值,都不会影响正确性。而关于第二种或许的状况,自身 (a,b,c,d) 这行数据便是缺失 e 的值的,需求在读的时分给这行数据填 e 的默认值 或许 NULL 的,所以在这个状况下,我直接先给这行数据的 column e 填了默认值或许 NULL,也是完全能够正常作业的。所以这两种状况下,咱们给 e 列填默认值或许 NULL 都是能够正确作业的,因而咱们就不需求回来解析失利了。可是如果多出的 e 列并不支撑填默认值或许 NULL,那就只能回来解析失利,去触发拉取最新的 schema 进行第二轮解析。

  1. 第三种状况 Overflow Column,即咱们待写入的数据中有一列数值大于了咱们 schema 中这一列的数据规模的。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十三 overflow column 场景

关于这种状况,只要图十三(左)这种状况,即先进行了扩列的操作,然后刺进了新的数据,可是数据先于 schema 抵达了 TiFlash。咱们能够看一下图十三(右)来理解为什么不或许是先刺进数据再触发缩列的状况。如果咱们先刺进了数据(a,b,c,d,E),然后对 e 列做了缩列操作,将 e 列从 int 类型缩成 tinyint 类型。而由于刺进的这个 E 超过了 tinyint 的规模,所以这个 DDL 操作会报 overflow 的错误的,操作失利,因而无法导致 overflow column 这种现象。

因而呈现 overflow的场景,只或许是图十三(左)的这种状况。可是由于 schema change 还没有抵达 TiFlash,咱们并不知道新的列详细的数据规模是怎样样的,所以没有办法把这个 overflow 的值 E 写入 TiFlash 存储引擎,所以咱们也只能回来解析失利,去触发拉取最新的 schema 进行第二轮解析。

了解完再第一次解析的时分或许会遇到的三种反常状况,咱们再来了解一下在第一次解析失利下,从头拉取最新的 schema 今后,再进行第二轮解析下会呈现的状况。相同的,除了在第二轮正常的完结解析以外,咱们还或许遇到前面的三种状况,但不一样的是,在第二轮解析时,能够确保咱们的 schema 比待写入的数据更新了

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十四 第二轮解析反常场景
  1. 第一种状况 Unknown Column。由于 schema 比 待写入的数据新,所以咱们能够肯定是由于在这行数据后,又产生了 drop column e 的操作,可是这个 schema change 先抵达了 TiFlash 侧,所以导致了 Unknown Column 的场景。因而咱们只需求直接把 e 列数据直接删除即可。
  2. 第二种状况 Missing Column。这种状况则是由于在这行数据后进行了 add column e 的操作造成的,因而咱们直接给多余的列填上默认值即可。
  3. 第三种状况 Overflow Column。由于现在咱们的 schema 现已比待写入的数据新了,所以再次呈现 overflow column 的状况,必定是产生了反常,因而咱们直接抛出反常。

以上便是写数据进程的全体的思路,如果想了解详细的代码细节,能够查找一下 writeRegionDataToStorage 这个函数。别的咱们的行转列的进程是依靠 RegionBlockReader 这个类来完结的,这个类依靠的 schema 信息便是咱们前面说到的 decoding_schema_snapshot。内行转列的进程中,RegionBlockReader 在拿 decoding_schema_snapshot 的时分会先检查 decoding_schema_snapshot 是否跟最新的 tidb_table_info 版别是对齐的,如果没对齐,就会触发 decoding_schema_snapshot 的更新,详细逻辑能够参阅 getSchemaSnapshotAndBlockForDecoding 这个函数。

Schema on Data Read

和写不太一样的是,在开端内部的读流程之前,咱们需求先校验 schema version。咱们上层发送的恳求中,会带有 schema version 信息(Query_Version)。读恳求校验需求满足的要求则是,待读的表本地的 schema 信息和读恳求里边的 schema version 对应的信息坚持一致的

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十五 读数据

TiFlash 担任拉取 schema 的 TiDBSchemaSyncer 会记录全体的 schema version,咱们这边称它为Local_Version。因而读操作的要求即 Query_Version = Local_Version。如果 Query_Version 大于 Local_Version,咱们会以为本地 schema 版别落后了,因而触发 sync schema ,拉取最新的 schema,再从头进行校验。如果 Query_Version 小于 Local_Version,咱们就会以为 query 的 schema 版别太老,因而会回绝读恳求,让上层节点更新 schema version 后从头发送恳求。

在这种设定下,如果咱们有个表在十分频繁的产生 DDL 操作,那么他的 schema version 就会不断更新。因而如果此时又需求对这个表进行读操作,就很简单呈现读操作一直在 Query_Version > Local_Version 和 Query_Version < Local_Version 两种状况下交替来回的状况。比方一开端读恳求的 schema version 更大,触发 TiFlash sync schema,更新 local schema copy。更新后本地的 schema version 就比读恳求新,因而触发回绝读恳求。读恳求更新 schema version 后,咱们又发现读恳求的 schema version 比 本地 schema copy 更新了,周而复始 …. 关于这种状况,咱们现在是没有做特殊处理的。咱们会以为这种状况是十分十分罕见的,或许说不会产生的,所以如果不幸产生了这样的特殊状况,那只能等待他们到达一个平衡状况,顺利开端读操作。

前面咱们说到读操作要求 Query_Version 和 Local_Version 完全相等,因而十分简单呈现呈现不相等的状况,然后造成诸多从头建议查询或许从头拉取 schema 的状况。为了削减产生此种状况的次数,咱们做了一个小的优化。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十六 version 联系示意图

咱们除了 TiFlash 全体有 schema version外,每张表也有自己的 schema version,咱们称为 Storage_Version,而且咱们的 Storage_Version 永久小于等于 Local_Version 的, 由于只要在最新的schema 改动的时分,的确修正了这张表,Storage_Version 才会恰好等于 Local_Version, 其他状况下,Storage_Version 都是小于 Local_Version 的。因而在 [Storage_Version, Local_Version] 这个区间中,咱们这张表的 schema 信息是没有产生任何改动的。也便是 Query_Version只要在[Storage_Version, Local_Version] 这个区间内,读恳求要求的这张表的 schema 信息和咱们现在的 schema 版别便是完全一致的。所以咱们就能够把 Query_Version < Local_Version 这个限制放松到 Query_Version < Storage_Version。在 Query_Version < Storage_Version 时,才需求更新读恳求的 schema 信息。

在校验完毕后,担任读的模块依据咱们对应表的 tidb_table_info 去建立 stream 进行读取。Schema 相关的流程,咱们能够在 InterpreterSelectQuery.cppgetAndLockStorageWithSchemaVersion 以及 DAGStorageInterpreter.cppgetAndLockStorages 中进跋涉一步的了解。 InterpreterSelectQuery.cppDAGStorageInterpreter.cpp 都是来担任对 TiFlash 进行读表的操作,前者是担任 clickhouse client 连接下读取的流程,后者则是 TiDB 支路中读取的流程。

Special Case

最后咱们看一个比方,来了解一下 Drop Table 和 Recover Table 相关的状况。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十七 special case 示意图一

图十六中上方的线是 TiDB 的时刻线,下方的线是 TiFlash 的时刻线。在 t1 的时分,TiDB 进行了 insert 的操作,然后在 t2 的时分又进行了 drop table 的操作。t1′ 的时分,TiFlash 收到了 insert 这条操作的 raft log,可是还没进行到解析和写入的步骤,然后在 t2′ 的时分,TiFlash 同步到了 drop table 这条 schema DDL 操作,进行了 schema 的更新。等到 t2” 的时分,TiFlash 开端 解析前面那条新刺进的数据了,可是这时分由于对应的表现已被删除了,所以咱们就会丢掉这条数据。到现在为止还没有任何的问题。

TiFlash 源码阅读(四)TiFlash DDL 模块设计及实现分析

图十八 special case 示意图二

可是如果 t3 的时分咱们又进行了 recover 的操作,将这张表恢复了,那最后刺进的这条 row 数据就丢失了。数据丢失是咱们不能承受的成果。因而 TiFlash 关于 drop table 这类的 DDL,会对这张表设上 tombstone,详细的物理回收延后到做 gc 操作的时分再产生。关于 drop table 后这张表上还存在的写操作,咱们会继续进行解析和写入,这样在后续做 recover 的时分,咱们也不会产生数据的丢失。

小结

本篇文章首要介绍了 TiFlash 中 DDL 模块的规划思维,详细完结和中心的相关流程。更多的代码阅览内容会在后边的章节中逐步打开,敬请期待。