Skip to content

Instantly share code, notes, and snippets.

@devillove084
Last active October 26, 2022 05:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devillove084/f16402cfb982f43fa11560471246cc7f to your computer and use it in GitHub Desktop.
Save devillove084/f16402cfb982f43fa11560471246cc7f to your computer and use it in GitHub Desktop.
TiDB Hackthon 2022 RFC

TiBee设计文档(Final)

项目背景

这个项目的动机实际上是源自于东旭的一段演讲内容:

但是, FDW 意义仅仅停留在"联邦查询"吗?我在思考这个 feature 是为什么?因为我在看这个 feature 的时候联想到关于数据库的本质,数据库这种软件的本质是什么?当你抛开所有的数据结构,存储的能力,抛开所有功能,数据库里面到底存了什么东西?数据库把所有刚才我说的概念都剥离开,它只干两件事情,一是存储真实的数据,另外一部分是叫做索引,数据库无非就是数据和索引,怎么在这两种概念中辗转腾挪。按照刚才的思路把整个数据库当成数据和索引的容器,索引这个概念其实就是一种特殊映射的表关系,索引也是一张表,你要索引的内容对应到数据上的映射关系。

img

img

那我们就跟着这个思路重新思考 FDW 。右边是我的灵魂画风,有点难以看懂,今天整个数据库行业的趋势,其中一个趋势是各种各样的细分领域的数据库诞生,图数据库,向量搜索数据库,全文检索数据库, TiDB 能不能把这些数据库的能力变成它的索引能力?比如说我有一张表这里面存储着用户的关系,用户的信息,大家知道在一个关系上的搜索、查询用图的模型更好,如果是用传统的比如说我用索引的数据结构查询得很慢,用图模型可以极大加速这个性能。如果从这个角度去思考, TiDB 的索引能够接入其他的这些数据库,让其他的数据库作为 TiDB 的索引,同时以一个统一的接口给用户提供服务,是不是打开了新世纪大门的感觉?

确实是打开了新世界大门的感觉,但是抽象和简单想要落实到具体的应用,具体的场景,想要能尽可能 cover 大部分的数据库,想要从现在的 TiDB 中去读到更多的存储引擎中的数据,就必须从现在在TiKV 、TiFlash等存储引擎以及 coprocessor 的中去 hardcode 算子的这种模式中去抽离出来,去思考如何规范目前的算子抽象。另外,项目也对比和参考了前面几届 Hackthon 中 FDW 的项目,看了很多前辈的尝试和心得,大都只是在优化器的角度上去考虑这件事,好像也能实现 FDW 的效果,但是,最终并没有进入主线分支。最大的原因就是因为复杂性。这一点在前不久刘奇的演讲《能否掌控复杂性,决定着分布式数据库的生死存亡》的演讲中也是着重强调了。其中有一段话:

“一个专家型用户跟我说: “人类几千年来应对复杂性只学会了一个道理,就是分而治之”。 这听起来好像是一个很简单的道理,回想一下会觉得他说得太对了,这也是人类几千年来应对复杂性的唯一办法。分而治之落在软件或者数据库的复杂性上面,应该是什么样的?这就是 TiDB 未来的演进方向,也是整个行业未来的演进方向。”

分而治之,这其实就是从大禹治水那会儿到现代社会,我们在面对大规模的、巨复杂的事物下的本能反应。所以本次项目就萌生了一个比较大胆的想法。将 TiDB 的优化器,也就是 Casacde 查询框架的一部分拆出来,加上抽象出来的通用的算子接口(类似 Spark RDD),再将 TiKV 的kv抽象以及算子处理部分剥出来,两个部分合并为一个中间件设计,名称暂定为 TiBee,因为可以希望借助这个中间件可以将同构、异构复杂的数据库集群像🐝一样良好的组织起来形成一个有机的可查询实体。

而纵观不同数据库的 FDW 设计,其实本质上都是用于数据同步的需求,这里以 PG 数据库为例,其本身提供了从其他数据库服务器到文件的多种wrapper形式。其实在最初的版本中, FDW 仅仅只能将远端数据源的数据原封不动地拉至 PG 中,但到了现如今借助 FDW 已经可以实现将更多的运算(如JOIN, 聚合等)下推至远端数据源, 并能够对远端数据源的数据进行更新。尽管基于 SQL /MED标准的 FDW 技术的初衷是为了统一异构数据源的访问方式。但是, 随着这些年 PG 的 FDW 内置功能(core functionality)支持将越来越多的运算下推到远端执行, 同时还有一个获得社区官方支持的用于访问远端 PG 服务器的 FDW 扩展postgres_ FDW 也变得越来越强大, PG 社区核心团队的大佬 Bruce Momjian在2016年给社区写了一封邮件提议了一个基于 FDW 技术的分布式水平扩展方案(即通称的所谓"Sharding"方案), 目前社区已经基本上认可了基于 FDW 的Sharding方案作为 PG 源生的分布式实现方案。

从 PG 的 FDW 方案的演变中可以窥视出这项技术带来的变革和收益,并且,在未来 TiDB 云上场景越来越多的情况下, FDW 作为 TiDB 的一项基本能力将在云上多变的场景下发挥更多的作用。

项目简介

2003 年,在 SQL 的标准中增加了一个名为“ SQL / MED ”,翻译过来是外部数据的 SQL 管理,是 SQL 为了管理外部数据,但是可以使用标准 SQL 查询的补充协议。其另外一个名字 Foreign data wrapper(FDW ) 可能更耳熟一些。而本次项目就为了尝试解决 TiDB 缺失的 FDW 机制提出的新设计。本次项目的设计目的有一:

  • 可以在 TiDB 端查询后端不同的数据库、存储引擎的数据内容;
  • 可以在 TiDB 端下推部分算子到不同的数据库、存储引擎进行计算;

并且,问题进一步放在云原生的环境下,放在 TiDB Cloud下进一步思考。

在云上场景下,用户使用多DB集群、异构DB集群的需求越来越多,而其中类似“ TiDB -> PG -> TiDB ->MySQL ”形式的查询需求也逐渐显现,而在目前阶段需要大量的手动调整或者数据同步才可以查询,而且臃肿、冗余、驳杂,所以复杂性是设计云上 FDW 最大的的拦路虎。

所以给出项目的设计目的之二:

  • 设计中间件,可以包裹 TiDB 、 PG 、 MySQL 等常用数据库,在云上复杂的多DB异构集群下,在“TiDB -> PG -> TiDB -> MySQL ”这种链式云上数据库组合下,可以在单台 TiDB 侧进行查询,一次把所需数据捞出来;
  • 尝试在目前 Cascade query framework 的基础上抽象一套算子的规范定义 TiDB 的 Execution Runtime。

项目目标

  1. 尝试实现一套更为抽象FDW机制,填补目前TiDB的FDW空白;
  2. 尝试进一步拓展FDW在云上的能力,尝试touch“TiDB以更通用的能力接入其他存储形式”的目标;

项目设计

在上一个部分的内容中简单阐述了本次项目的简单设计思路:从 TiDB 和 TiKV 中切出来算子的部分融合为一个中间件,但是其中的诸多问题,亟须深入的思考。首先,为了简化设计,在本次项目的设计中暂时只考虑只读的场景,并且,暂时不对接PD调度,先从最简单的出发:读取数据。

继续思考,这里用几个问题引入本次项目的设计:

  1. 要如何抽象出一套通用的算子接口?
  2. 对于外表的读写需不需要 TiDB 关心(本次设计不关心),应该如何接入和管理?
  3. 要如何和现有的 TiDB 计算框架相结合?

TiBee处在 TiDB 和底层存储引擎之间,经由Casacde框架优化的Task/Executor发送到TiBee中,TiBee了解到后端存储引擎的类型,生成一个分布式对象集合,提供一种抽象的数据架构,不必担心底层数据的分布和格式,只需将一系列算子表达式表达为一系列转换处理,这里的概念可以类比于 SparkRDD的玩法,不同 RDD之间的转换操作形成依赖关系,可以实现管道化,避免中间结果的存储,降低数据复制、磁盘IO和序列化的开销。

这里详细对本次项目中算子接口设计的思路稍微做展开,一个分布式对象集合本质上是一个只读的分区记录集合,可以分成多个分区,每个分区就是一个数据集片段,并且可以被保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算。项目提供了一种高度受限的共享内存模型,不能直接修改,只能基于稳定的物理存储中的数据或者通过在其他对象集合上执行确定的转换操作(如 map、join 和 groupBy )而创建得到新的对象集合。

对象集合提供了一组丰富的操作以支持常见的数据运算,分为“行动”( Action )和“转换”( Transformation )两种类型,前者用于执行计算并指定输出的形式,后者指定对象集合之间的相互依赖关系。两类操作的主要区别是,转换操作(比如 map、filter、groupBy、join 等)入参和返回都是对象集合,而行动操作(比如 count、collect 等)入参是对象集合但是返回事一个值或结果。这里提供的转换接口都非常简单,都是类似 map、filter、groupBy、join 等粗粒度的数据转换操作,而不是针对某个数据项的细粒度修改。 执行过程如下:

  1. 读取外部数据源进行创建;
  2. 经过一系列的“转换”操作,每一次都会产生不同的分布式对象集合,供给下一个“转换”使用;
  3. 最后一个 RDD 经“行动”操作进行处理,并输出到外部数据源。

而目前 TiDB 所使用的 Cascades Planner 过程简要如下图所示,详细数据结构设计不在这里展开。

img

这个规则匹配的 Pattern 是 Selection -> Aggregation,作用则是将这个 Selection 下推到 Aggregation 下面,例如 SQL : select a, sum(b) from t group by a having a > 10 and max(c) > 10中,having 条件里的 a > 10可以下推到 Aggregation 的下方。更具体地来说,只要 Selection 当中的一个 Expression 里的所有列都出现在 group by 的分组列时,就可以把这个 Expression 进行下推。

  1. 在 Group0 中的 Selection 匹配到了 Pattern Selection -> Aggregation
  2. 执行了 OnTransform() 的转换,Selection 中的 a > 10条件被下推到了新的 Aggregation 下方,并且保留的条件 max(c) > 10成为了一个新的 Selection。
  3. 由于 OnTransform()eraseOld返回了 True,因此最终把原来的 GroupExpr 从 Group 中删除。

img

而为了使 TiDB 能够使用在各种不同的存储组件之上,为 TiDB Cascades Planner 引入了 Adapter Model。所谓 Adapter Model 指的是,在 LogicalPlan 中添加各种用来从存储引擎收集数据的算子,例如 TiKV TableGatherTiFlashTableGather 甚至 MySQL Gather 等,这些 Gather 算子最终在物理优化阶段会被改写成 TableReaderIndexReader等用来读取数据的算子,因此 Gather 的所有父亲算子都是在 TiDB 中执行的,而 Gather 所有子节点的算子都是在相应的存储引擎上执行的。这样做有两个好处:

  • 可以在逻辑优化阶段就区分出不同的存储引擎,可以针对不同的存储引擎设计不同的算子下推策略。
  • 若 TiDB 想要使用别的存储引擎,在优化器中只需要实现对应的 Gather 算子以及物理优化阶段的 Reader 算子。

而本次设计尝试从另外一个角度进一步优化 Adapter Model,在LogicalPlan 中不收集不同存储引擎的算子,而是编写转换规则,原本从 TiGather-> MySQL Gather 的下推,转为 TiGather-> MySQL Gather 的转化。从原本抽象的算子 downcast 到具体的算子上,进而在物理阶段上得到具体的读取数据的算子。

总而言之,TiBee 的输入是未被优化,或者优化过的 Plan,输出是一个具体到某个存储引擎的算子实例。从而将算子下推和具体优化和具体的存储引擎之间进行解耦,从而保证 TiDB Cloud 云上的弹性调度和一致的数据读取入口。

项目收益

用户角度

从用户角度讲,FDW的目标本身就是为了解决异构数据库软件下的同步和查询问题,FDW 提供了一系列统一的公共接口,使得扩展程序可以轻松地在优化、执行、扫描、更新和统计等核心部分和数据库深度集成,从而可以用 SQL 语句直接查询和操作外部数据源。用户可以在外部数据包装器FDW的帮助下访问数据库的外部数据。外部数据包装器是一个库,它可以与外部数据源通信,隐藏连接到数据源的细节并从中获取数据。其中,一些FDW能够作为 contrib 模块获取,用户也可以自己创建FDW或者使用第三方所提供的 FDW。

并且,在 PG 创新的使用 FDW 作为分片能力的基石后,用户可以直接通过使用 FDW 的机制将原本在单点上的子句下推到叶子结点,靠近数据的位置去执行,提高在分片下的执行速度。而在数据库上云之后,分片以及缩扩容带来的扩展性天然更亲和 FDW,用户可以在云上动态切换后端数据库服务,而无需改变查询入口,后端自动切换数据库引擎,大大降低用户的查询心智负担,简化用户应用的入口。

TiDB收益

对于 TiDB 本身而言,本项目实际上是尝试从 TiDB 侧将优化器的部分拆解出来,尝试抽象一个更为通用的算子框架,能够天生兼容大部分主流数据库,精简TiDB的设计,同样的思路也可以用在其他组件、甚至后端的存储引擎上,进行进阶抽象,将原本的庞大的分布式数据库进行接口化,可以任意拆解和组合,将云上的天然能力融入数据库中。并且,FDW 机制随之可以进化为更为通用的中间件,套用在不同的数据库,不同类型的数据库都可以成为 TiDB 的“替身”,未来 TiDB 也可以拥有更为直接的“时序能力”,“图能力”,甚至,可以进一步考虑接入 PD ,为 FDW 包裹的外表提供一致的写入和同步能力,进一步挖掘 FDW 提供的能力。

社会价值

这里也借用唐刘前几日的分享《透明一切,是我们在复杂环境下与客户建立信任的最佳途径》,如果可以将原本冗杂、庞大的数据库接口化,抽象为更细小的组件,不仅能满足我们对削弱系统复杂性的希冀,还能满足对用户透明的需求,这两个目标本质上是一致的。这里引用一段话:

面对不确定的经济环境,我们如何从当前的复杂环境中生存下来?开源是建立信任的最佳途径,但只有开源也是远远不够的,PingCAP 认为唯有透明才能解决问题,透明一切能透明的事情。

开源只是解决了部分专业人员的透明问题,但是并不够,唐刘说需要开放进一步的计划和开发路线。其实不仅仅需要这些,还需要数据库本身的透明和简洁,对于数据、应用处理的透明和简洁,以及对用户和开发者的透明和简洁。让数据库不仅适用于个人开发者,中小企业,超大规模场景,还能适用于政企、保密单位、银行、国企,真正做到一个数据基石在时代背景下的应该承担的责任和义务。

@aierui
Copy link

aierui commented Oct 23, 2022

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment