深刻解析 ZNBase 分布式 SQL 引擎架构的五大服务组件

导读前端

与传统关系型数据库相比,分布式数据库系统具备多集群、多节点、高并发等特性,这就须要分布式数据库的 SQL 引擎可以在知足用户常规的 SQL 请求之外,提供多集群、多节点协同计算的能力,从而提升查询效率。本文将介绍分布式数据库 ZNBase 的 SQL 引擎架构特色,以及其中各大服务组件的技术原理与工做流程。node

分布式数据库架构

目前业界最流行的分布式数据库主要分为两种架构。一种是以 Google Spanner 为表明的 Shared nothing 架构,另外一种是以 AWS Auraro 为表明的计算/存储分离架构。 git

Spanner 是 shared nothing 的架构,内部维护了自动分片、分布式事务、弹性扩展能力,数据存储仍是须要 sharding,plan 计算也须要涉及多台机器,也就涉及了分布式计算和分布式事务。 数据库

Auraro 主要思想是计算和存储分离架构,使用共享存储技术,这样就提升了容灾和总容量的扩展。可是在协议层,只要是不涉及到存储的部分,本质仍是单机实例的 SQL 引擎,不涉及分布式存储和分布式计算,这样就和传统数据库兼容性很是高。缓存

浪潮云溪 NewSQL 数据库 ZNBase 完美地继承了 Spanner 的设计理念,实现了基于对等架构的分布式 SQL 引擎。数据结构

ZNBase 的 SQL 引擎

ZNBase 的 SQL 引擎在传统的 SQL 引擎基础上,引入了分布式的概念,经过多个集群节点协同计算更高效的执行用户 SQL 查询,整体架构图以下:架构

SQL 引擎静态结构,包含五大服务并发

集群中每一个节点 node 都独有链接服务(Connectivity Service)、编译服务(Compile Service) 和缓存服务(Cache Service)三大服务,能够完成用户的 SQL 查询执行的前端准备工做。框架

同时,全部节点又共同组成了分布式的目录服务(Distibuted Catalog Service)和分布式的执行服务(Distibuted Execute Service),经过这两个服务完成了多个 node 节点的协同执行,提升了分布式 SQL 引擎的执行性能。最终将结构化数据,转化为底层存储可识别的 KV 编码对,经过 Batch 批处理发送到事务层进行处理。异步

SQL 引擎执行流程

下文将对这五大服务进行展开介绍。

1.链接服务 Connectivity Service

分布式数据库 ZNBase 采用的是对等架构,集群中的任意节点均可以做为接入节点。同时,ZNBase 支持 PostgreSQL 协议,SQL 查询能够经过各类支持 PostgreSQL 协议的驱动发送到集群。

链接服务流程以下:

  1. 用户经过后台守护进程进行链接器管理,为每一个客户端构建新的 Executor。
  2. 当用户从客户端发起指令后,从客户端接收和解包流。
  3. 执行完毕后,将操做结果打包返回给客户端。
  4. 用户的每一次操做,都被认为是一个单独的事务操做。

2.分布式目录服务 Dist Catalog Service

ZNBase 的 Dist Catalog Service 不只实现了传统关系数据库的 schema metadata,包含了经常使用的库、表、列、模式等数据库元数据,并且实现了元数据信息的高可用,以及分布式访问。元数据采用多副本存储、分布式存储,保证少于一半数据不可用的状况下,元数据信息仍然可用。并且每一个对等节点在启动时会直接内存化元数据路由表的第一级 Root Meta Range 数据,保证任意节点都能访问到须要的元数据信息。 

Catalog 信息发生变化时,首先会更新到元数据存储的写入节点,经过 Raft 协议同步到多副本。同时使得各个节点的 Catalog 缓存失效,在使用时进行异步的更新,保证各节点数据的一致性。

3.编译服务 Compile Service

ZNBase 的编译服务包括了 SQL 前端和 SQL 中端功能,SQL 前端实现了传统数据库的 Scanner、Parser、SQL 语法、SQL 语义以及数据库对象和权限校验的处理,生成了 AST(抽象语法树)。

SQL 中端实现了数据库的优化器的功能。优化器负责给执行引擎提供输入,它接收来自 SQL 前端解析好的 AST 树,而后须要从全部可能的计划中选择代价最优的计划提供给执行引擎。 

ZNBase 的优化器是基于 Cascades 论文实现的搜索框架。从数据库的发展历程来看,基于 Cascades 的搜索框架已经成为了业界标准,包括商业数据库 SQL Server 以及开源数据库 GP/ORCA 都采用 Cascades 实现。编译服务的总体架构以下:

SQL 引擎编译服务结构图

如上图所示,Client 端输入的 SQL 语句经过 go-yacc 层的词法、语法、语意义解析为 AST 语法树,通过 Memo construction 转换为 CBO 初始的 Memo 树。Memo 由一些列等价的 group 组成,每一个 group 表示一个逻辑等价表达式集合,Memo 自己是树状结构化的,能够表明查询语句,可是又不包含大量的元数据信息,能够被缓存以提升执行效率,这点在 Cache Service 中会给出解析。构造好的 Memo 直接应用于基本的 RBO 转换。以后,Memo 数据根据统计信息通过 CBO 优化(等价发掘和最优化Cost)选择转换为最优路径的计划。 

RBO 根据指定的优先顺序规则,对指定的表进行执行计划的选择。好比在规则中:索引的优先级大于全表扫描。 

当某些 SQL 语句的写法并不利于快速从存储中查询数据的场景下,RBO 会对其进行相应转化,例:

SELECT * FROM  t1 ,t2  WHERE  t1.a > 4  AND  t2.b >5;

若是先进行笛卡尔积再进行过滤条件时,则会产生不少没必要要的元组。可是若是先过滤 t1 , t2 的关系,在进行笛卡尔积,那么表达式的消耗将大大减小。在进行过滤时,能作到一个select算子中就作到算子中,不能的话,就在具备过滤须要的列时及时作好,好比 a.a > 5 and b.b > 10 and a.c > a.b,第一个和第二个条件均可以推到 select 算子中,在这两个算子上面当即加一个 a.c > a.b 的过滤条件。 

CBO 则基于统计信息对代价进行代价预估,获得一条较优的查询路径。例如:咱们在作三个表链接的时候,若是有统计信息的话,咱们就能够知道,哪两个表先作链接会使接下来执行的代价更小,由于在作 hashjoin 时,咱们总但愿小的表先进入,而后制做成一个小的 hashtable,由于 hashtable 比较小,因此以后的大表在作 join 的时候,就会有更高的命中率。

4.缓存服务 Cache Service

ZNBase 提供了两种类型的缓存服务,主要是用来提升数据访问效率,减小重复消耗。 

第一种是 Session 级的 Querycache,主要是缓存用户 SQL 语句指纹对应的 Memo 树数据结构,减小同一 Session 的 SQL 语句屡次构建逻辑计划的开销。SQL 语句指纹含有 SQL 语句的相关 Catalog 信息和权限等校验信息。 

在重用 Memo 以前,会对 Memo 是否过时进行检查:解析元数据所依赖的每一个数据源和 schema,以便检查彻底限定的对象名是否仍解析为相同对象的相同版本,检查和时间相关的类型的构造和比较方式,以及用户是否仍有足够的权限访问这些对象。若是依赖项再也不是最新的,则断定该 Memo 过时,须要从新构建。 

第二种是集群级别的元数据相关 Cache。其中 Catalog 信息包含了数据库经常使用的 scheme 信息和元数据路由信息。元数据路由信息由 Dist Catalog service 提供。经过元数据路由信息集群任意节点能够访问到全部须要的元数据或者数据。

5.分布式执行服务 Dist Execution Service

ZNBase 的 SQL 引擎总体设计模型参考了 Volcano 模型[1],Volcano 模型的提出者是 Goetz Graefe,其 1994 年发表此文,并于 2017 年得到 Edgar F. Codd(关系模型奠定人)创新奖。 

ZNBase 的分布式执行提出了一些与 Map-Reduce 相似,但与 Map-Reduce 的执行模型又彻底不一样的概念。 

ZNBase 的逻辑计划由优化后的 Memo 自底而上构建出一个 Plan node 树状结构,为后续构建物理计划添加一些额外的表信息,列信息等。 

分布式执行的关键思想是如何从逻辑执行计划到物理执行计划,这里主要涉及两方面的处理,一个是计算的分布式处理,一个是数据的分布式处理。 

一旦生成了物理计划,系统就须要将其拆分并分布到各个 node 之间进行运行。每一个 node 负责本地调度数据处理器 data processors 和输入同步器 synchronizers。node还须要可以彼此通讯以将输出 output router 链接到 input synchronizer。特别是,须要一个 streaming interface 来链接这些组件。为了不额外的同步成本,须要足够灵活的执行环境以知足上面的全部这些操做,以便不一样的 node 除了执行计划初始的调度以外,能够相对独立的启动相应的数据处理工做,而不会受到 gateway 节点的其余编排影响。 

ZNBase 的集群中的 Gateway node 建立一个 Scheduler 调度器,它接受一组 flow,设置输入和输出相关的信息,建立本地 processor 并开始执行。在 node 对输入和输出数据进行处理的时候,咱们须要对 flow 进行一些控制,经过这种控制,咱们能够拒绝 request 中的某些请求。 

执行 Flow 示意图

每一个 Flow 表示整个物理计划中跨节点执行的一个完整片断,由 processors 和 streams 组成,能够完成该片断的数据拉取、数据计算处理和最终得数据输出。以下图所示:

计划执行示意图

对于跨节点的执行,Gateway node 首先会序列化对应的 FlowSpec 为 SetupFlowRequest,并经过 grpc 发送到远端 node,远端 node 接收后,会先还原 flow,并建立其包含的 processor 和交互使用的 stream(TCP 通道),完成执行框架的搭建,以后开始由网关节点发起驱动的多节点计算。Flow 之间经过 box 缓存池进行异步调度,实现整个分布式框架的并行执行。 

对于本地执行,就是并行执行,每一个 processor,synchronizer 和 router 均可以做为 goroutine 运行,它们之间由 channel 互联。这些 channel 能够缓冲信道以使生产者和消费者同步。

为实现分布式并发执行,ZNBase 在执行时引入了 Router 的概念,对于 JOIN 和AGGREGATOR 等复杂算子根据数据分布特征,实现了三种数据再分布方式,mirror_router、hash_router 和 range_router,经过数据再分布实现 processor 算子内部拆分为两阶段执行,第一阶段在数据所在节点作部分数据的处理,处理后结果,根据算子类型会进行再分布后,第二阶段聚集处理,从而实现了单个算子多节点协做执行。
 

小结

本文介绍了基于谷歌 Spanner 论文设计的分布式 NewSQL 数据库 ZNBase 的 SQL 引擎架构,并详细介绍了每一个节点中的链接服务、编译服务、缓存服务,以及系统中的分布式目录服务、分布式执行服务五大服务组件的技术原理与工做流程。下期文章咱们将介绍在原有 SQL 引擎架构的基础上,ZNBase 团队针对编译服务、分布式执行服务等组件进行的一系列优化改进工做。 

下篇:深刻解析分布式数据库 ZNBase 的 SQL 引擎优化

关于 ZNBase 的更多详情能够查看:

官方代码仓库:https://gitee.com/ZNBase/zn-kvs

ZNBase 官网:http://www.znbase.com/ 

对相关技术或产品有任何问题欢迎提 issue 或在社区中留言讨论。同时欢迎广大对分布式数据库感兴趣的开发者共同参与 ZNBase 项目的建设。

联系邮箱:haojingyi@inspur.com