zl程序教程

您现在的位置是:首页 >  后端

当前栏目

TiDB HTAP 的架构演进及实践

架构 实践 演进 TiDB HTAP
2023-06-13 09:18:11 时间

在访问量和数据量急剧膨胀的今天,关系型数据库已经难以支撑庞大复杂的系统规模。在此背景下,备受关注的数据库新理念 HTAP,会是一条“正确”的路吗?在刚过去的 QCon 全球软件开发大会上,PingCAP 实时分析产品负责人马晓宇发表了《TiDB HTAP 的架构演进及实践》的主题演讲,它从 HTAP 的历史入手,详述了 HTAP 的技术挑战以及 TiDB 的应对方案。本文为其演讲整理文,enjoy~

大家好,今天为大家分享以下几方面内容。首先是分享 HTAP 的历史,其次是 TP 和 AP 之间存储和计算的设计选择、HTAP 的技术挑战以及 TiDB 的应对方案,接着是 TiDB HTAP 的使用情况,最后是展望和总结。

1HTAP 的历史

七八十年代的传统型关系数据库,所有数据库都是 HTAP 的,并没有分为 TP 或者 AP,最近出现的 Oracle、DB2、SQL Server 才加了一些 AP 属性,也可以做一些轻量的数仓,像 SQL Server 有列存索引,Oracle 也有并行与列存。

传统的数据库不一定需要购买 Teradata 或者大数据,它们在一定的场景下,可以当做 HTAP 的数据库来使用。

80 年代到 90 年代左右,数仓开始兴起,很多企业开始建设数仓,但是有一些数仓与传统的 TP 业务不一样的 Workload,于是数据库界开始研究以传统的形式与技术去应对 Workload 。这时候开始出现针对数仓产品特化的数据库,例如 Teradata、Vertica、Greenplum、Apache Hadoop 生态引擎等,这些产品的出现也带来了技术的变革。例如,传统的数据库使用的 MPP 引擎都是单机的,那么随着数仓的诞生,它们是在 MPP 上的一个先锋。C-Store、KDB 以及 Mona DB 开始研究由行转成列的数据存储方式,Mona DB 的项目分支,很早开始进行向量化引擎。

与此同时,2005 年老祖师爷 Michael Stonebraker 谈到:“One size fit all idea has gone”,言下之意是数据库将要开始各种分支——各种特型的场景需要用特型数据库。在祖师爷看来,传统厂商需要在已有的一套巨大代码上进行维护,是一件非常低效并且麻烦的事情,传统数据厂商肯定不太愿意做这件事情。它认为每个不同的场景需要不同的数据库,比如分型使用分型数据库,图样使用图样据库。于是它身体力行,开发了 C-Store——非常早的列存数据库,后来 C-Store 演变成为了 Vertica。

在那之后,数据库 AP 和 TP 开始分支,分支的结果和下图一些相近。

例如,TP 数据通过某种方式 ETL 到数据库,数据库跑一些报表或建模,需要做一些流注入的时候,会跑到 HBase,HBase 和数据仓库或数据湖进行沟通,然后将数据湖计算完成的数据进行高速的服务,但是这些工作没有办法直接在 Hadoop 或者数仓的软件上进行,所以又需要把这些数据再转到前台 Data Serving 的数据集群。不同的平台上数据,需要用不同的技术将数据搬来搬去,这是 AP 和 TP 之间分叉之后的架构设计。

这时候,数据库界着手研究不使用 TP 和 AP 拆分的方式去处理数据。Gartner 提出用内存来做存储介质,把 AP 和 TP 两套引擎统一,2015 年 Gartner 提出的专有名词 HTAP(Hybrid Transactional Analytical Processing),本质上就是 TP 和 AP 的合流。

如果可以满足需求,用户也希望用更简单的方案去架构,从业务角度出发,人们希望更快地分析数据,例如作为风控公司,提早分析风控数据可以避免更多的损失;物流生鲜电商可以实时地进行资源的调配;电商公司能够更快地调整促销实时推荐;公共服务也可以提供更方便快捷的渠道。

2AP & TP 设计选择

TP 和 AP 是很难分清楚的两个词,业务侧和技术侧有不尽相同的理解。从技术面来说 TP 是 Transactional Processing,TP 数据库会有以下几种特性:

  • 首先,TP 数据库需要支持高并发和强一致需求。
  • 其次,TP 数据库可以实现高并发,很大程度上依赖索引支持,细粒度技术索引可以支持点查,可以让我们从海量数据中定位到其中的一部分,这是 TP 数据库支持高并发的根源之一。因此索引支持、快速定位明细数据也是 TP 数据库非常典型的特征。
  • 最后,数据库需要支持实时地插入及修改,例如实时订单,电商购物在购买之后,订单状态必须实时改变,而不是通过 T+1 的方式才能改变订单状态。

AP(Analytical Processing)对数据库的要求则完全相反。比如,从引擎的角度来说,分型处理的引擎都是低并发的,因为每次都需要处理大量数据,例如当产生含有几十亿数据的年度报表之时,必须把这一年所有的订单处理,才可以看到整张明细报表。这与 TP 数据是完全不同的,TP 海量数据选中订单、修改以及塞回去就可以。并且 AP 的查询是低并发,它并不需要占用这么多资源去处理报表。另外,由于传统数仓的处理模式,分型数据库支持批次更新,而不支持实时更新。

最后,分型数据库需要处理大量的历史数据。TP 数据库往往只存储几天之内的数据,但是 AP 数据库存储所有数据,所以 AP 需要可扩展的计算和存储能力。

传统的存储设计 TP 与 AP 的设计需求完全不同。

TP 用法是点状更新或者少量数据的更新和查找,整个设计的目标是更少的行访问和更好的实时更新能力。Ap 方面,它需要最快速地访问大批量数据,整套的流程可以进行平衡,牺牲实时更新的能力来换取批量扫描的速度。

存储方面的取舍,根据不同的访问模式,尽可能地让被访问的数据呆在一起,在一次访问、查询、定位就可以找到所需数据。TP 类的单行访问,单行的数据尽可能放在一起,一次访问读取完整的一行;AP 类扫批访问,尽可能把一列的数据放在一起,这是 TP 和 AP 设计最大的区别。

展开来说,TP 往往使用行存,少量读盘便可以将这行的数据扫完。如果是列存,当我们希望计算报表里面所有订单总量的时候,总出货量做一次聚合,然后在订单出货的这一个字段上,做一个 Aggregation,这时候我们肯定希望所有这一列的出货量数据尽可能放在一起,定位之后进行一次平扫,就可以达到较好的速度。

从直观上来说,列存是这张图上的样子。行存之时,所有的列是对齐的,一列加一列放置在一起,只要定位到某一个 ID,往后扫描一点数据,就可以将这行读出来。列存之时,先分若干组,按照列的方式去竖着切,然后放在一起,只要定位到一列的起始,顺序向下读就可以,这分别就是行存和列存访问最快的方式。

计算层方面,TP 的计算层是单机的,最近 NoSQL 出现之后,把 TP 做到了分布式上,以往对 TP 业务来说,数据量会比 AP 更少,所以 TP 不依赖类似于像 MPP 的架构。

从引擎设计的角度而言,经典的火山模型对 TP 来说已经足够用。相反来说,AP 则需要用大量的资源去堆积,换取同一个查询可以以很快的方式快速地响应。很早之前, AP 的标配是 MPP 或者以并行计算的方式去计算同一个查询。

另外,有一些执行优化的技术只与 AP 有关。例如图中表示的向量化引擎,传统的 TP 类的数据库(火山引擎)每次计算迭代,每个算子迭代只访问一行,这对于 TP 类的引擎火山模型非常合适。在工程上,火山模型维护的代价和工程封装都是最好的。

如果现在在 AP 的场景中,同时计算大量的数据,火山引擎的成本会非常高,所以大家开始采用向量化引擎,每次会迭代一批的列数据,引擎当中的 if-else 判断会达到比较好的效果。

3HTAP 的技术挑战 与 TiDB 的应对方案

HTAP 把两套设计目标完全南辕北辙的东西放到一起,这将会带来哪些挑战?

首先架构会变得更加复杂,代码的复杂度必然会呈指数级上升。引擎做的非常复杂,可以做到自适应,当感知到 TP 的场景之时,让它体现出 TP 的效果,感知到 AP 的场景时,体现出 AP 的效果,以这种单引擎的适配方式,复杂度非常大。

其次是资源隔离,资源隔离是必须要面对的话题。假设有一套数据库需要同时跑 TP 和 AP,那么 TP 的交易将会非常脆弱,TP 交易需要交易的延迟或吞吐保持非常稳定的状态。但是 AP 使用资源的方式——当 AP 查询之后,所有机器疯狂的运转,所有的 CPU 都在计算查询,然后计算之后,尽可能快速地呈现结果。这种使用资源的方式,如果 TP 和 AP 放在一起,一个 IP 任务查询之后,AP 疯狂抖动,TP 马上没有吞吐,所以隔离是整个架构中很大的挑战。

引入资源的强隔离之后,势必会带来其它的问题。首先是两组节点之间的数据如何进行同步的问题,如何把 TP 的数据,以快速实时地方式传输到 AP 节点上,传输的同时又如何尽可能地保证数据的一致性?例如,前台有很多同时发生交易 TP 的节点, AP 则需要进行持续不断的对账,像类似于这样比较严肃的场景,弱一致同步将会减少很多可用场景。

其次是如何保证用户入口尽可能透明的问题,尽可能地让用户使用统一的入口,而不是一套 TP、AP 的集群。对于 TiDB 来说,架构的 TP 和 AP 是完全隔离的,TiDB 使用了两组不同的资源节点,可以最大程度的保证 TP 和 AP 之间没有任何干扰。

拆分了两组节点之后,两组节点之间如何进行通讯?TiDB 使用的是 Raft 一致性复制的协议,通过 Raft 一致性复制协议,可以保障 TP 的高可用,所以我们选用 Raft 的协议来做 TP 和 AP 之间的沟通。

AP 节点其实是 TP 节点的特殊副本,TiDB server 作为 SQL 引擎统一的入口,共享优化器与执行单元,配合 Flink 和 Spark,可以进行深度的大数据集成。

上面是今年的总体架构,存储层分成两部分,一部分是列存左边的 TiFlash 节点(AP 的节点),右边是 TiKV 节点(TP 的节点),每一笔数据都有若干副本(同样颜色的数据块是一个数据块不同的副本),副本和副本之间通过共识协议进行串联,而共识协议可以保证数据的一致性。

通过 Raft 协议,在左边虚线的部分,通过同样的协议复制一套列存的副本,这套列存的副本单独作为 AP 的使用,列存副本的整个复制不会影响 TP 的可用性。即使列存网络中断或延迟抖动,那么 TP 函数可以进行交易,一笔交易写完之后,TP 可以直接返回,并不需要等待列存这边复制完成才能返回。

即使 TP 这边写完没有等到 AP 的回执,也可以继续提供服务,如果这时候 TiDB 发起一套查询,如何确定数据是最新的。假设现在向列存这边发起一次查询,列存并不会马上提供服务,而是先向行存主节点或者说主副本发起一个校对请求,整个复制是线性的 log 复制,这个请求本身仅仅包含很短的一段信息——在收到这个请求的同时,需要告知最新的复制进度,行存的主副本收到这份校对请求之后,就会返回。

举例说明,图中行存的主副本最新请求的复制进度是 4 ,列存在收到 4 之后,等待 4 进度被满足之后,才正常发起读取,再配合上层 MVCC 的时间戳隔离机制, TiFlash 会 根据 TiDB 发起的时间戳,发起请求的时间戳进行过滤,把所有大于时间戳的数据全都过滤掉,最后再返回。

这样的读取过程将会保证 T0 时间写下去的数据,在 T+1 时间经过校对之后,肯定能读取成功,另外整个数据可以保证快照的切片是完整的,不会缺少数据,也不会增多数据。换句话说,这种异步方式提供的服务,提供了与 KB 一样的隔离性与一致性保证。

这是 Suspense + AP 测试,Suspense 在读写发生的同时,约有 10 秒左右的延迟,在 AP 查询发生的同时,TP 的延迟以及吞吐量基本都没有任何变化。

传统的数仓选择批量更新,除了批量更新本身需要支持 ETL 的流程之外,其最大的好处是可以换取更快的读取速度。我们常常认为列存只能进行批量更新,其实并不是完全正确,市面上很多列存更新的系统本质是 Delta mean 的设计机制。

再来谈谈,列存本身为什么不好更新?

当行存写入一条数据,依顺序读取便可以完成,而进行一次列存更新,需要数据的所有列分别拆开,分别写入到数据应该在的地方,需要把一条顺序磁盘访问写入转化成多次的 random access,这导致了整个列存无法做很好地更新。如何让 random access 变得开销更小?进行攒批的时候,把 random access 均摊到大量数据当中,随机访问的开销就会变得非常小,所谓的 data main 方式就是这种处理方式。

攒批最常见的 LSM 结构,这个结构也可以用来做列存更新,但是我们选择另外一套设计模式。

上图是整个 LSM 结构示意图,LSM 把数据分成不同的层,层和层之间通过 compassion 的方式进行归整(规整可以使得数据按照组件或 key 的方式去组织,查找以及更新将会变得非常容易以及快速)。

LSM 渐进式的整理方式会带来很多分层,这为批量扫描带来了很多困难,因为每次扫描都需要访问很多层,然后这很多层需要做一次多路归并,才能获取最后的扫描结果。

我们决定放弃 LSM 的设计,采用了另外一套设计。这套设计是一个巨大子叶节点的必加数,每一个子叶节点分成两部分,一部分是用来快速写入和快速更新 Delta 部分,所有的写入和更新的数据会不断地 PAD 到 Delta 的部分,然后 Delta 部分会经过不断的 compassion,compact 重新的 stable,所有的子叶节点之间没有 overlap,所有的数据与读取是两层的归并,速度会比传统的 LSM 列存更新速度更快速。

我们明年将会发布新特性——列存直接写入。

HTAP 计算层挑战方面,TP 处理单笔事务数据量小的任务, AP 处理单笔事务数量非常大的任务,最主要的挑战是单机和分布式进行某种程度上的融合。

TiDB 在 4.0 之前,借助外部的大数据引擎来完成 AP 和 TP 之间不同的引擎。TiDB 计算层使用的是 TiDB 的单机引擎,配合所有存储层进行一部分的计算分摊。

如果进行计算本身,解决方案是 TiSpark,也就是 Spark 的 connecter,对 connecter 做了比较深的定制,可以认为它改写了整个 Spark SQL 的执行计划。

TiDB 5.0 发布之后,将会支持原生的 MPP 引擎,这套原生的 MPP 引擎在 TP 入口连接 TiDB server,因为 TP 不需要 MPP 的架构,所以当做单机引擎的方式来处理。当处理 AP 作业的时候,同样地先行连接 TiDB 本身,但是所有的 MPP 计算节点会分摊计算,类似于传统的 MPP 的架构来方式来执行。

然后这套引擎所有的 SQL 的进入点仍然是 TiDB server 本身,无需处理两套语法不同的引擎,权限管理以及整个 SQL 的兼容以及其它的入口都是统一的,最后调优化器的调优行为也完全一样。

用户的 SQL 进入 TiDB 的 Server,然后 SQL 层经过 Parser 共享一套 Optimizer,如果需要进行 TP 的处理,它直接由单机的方式去 TP 读取,读取它 KV 的数据。如果需要一个 MPP 进行大型数量的的 drawing 或 discount,那么它会路由到 MPP 节点,然后由 Optimizer 优化器之后,追加分布式执行计划处理,最后发送到 MPP worker 去读取。

2021 年第一季度,我们将会发布 5.0 GA 版本。上图是前一段时间测试 Benchmark 的效果,存储引擎是与 TiFlash 一样的列存,计算引擎分别是 Spark 和 TiFlash MPP,大家可以看到未经优化的版本,性能已经有很大的优势。

4TiDB 的使用状况

TiDB HTAP 列存 + 行存引擎发布后,到现在大约有 9 个月时间,通过 telemetry 监控剔除玩具和实验性部署之外,TiFlash 大概有 100 - 200 的使用规模。

关于 TiDB HTAP 的主流用法有以下几种情况。首先使用 TiDB TP 部分作为交易库,通过 TiFlash 或 AP 节点,进行实时交易数据的查询。

其次 TiDB 本身带有 TP 属性,可以作为一个实时热数据偏分析场景的应用,TiDB 在没有列存的版本之下,其实是很主流的用法,比如 Single View,前端有不同的数据源,通过实时的 CDC 汇总到 TiDB 里。在这个场景下,TiDB 会有很大的优势,除去 TiDB 本身具有 TP 属性之外,TiDB 还可以进行扩展,并且可以随时在线变更表结构。多元汇聚用户非常希望可以进行重型分析,我们在多元汇聚的场景下增加列存之后,可以三位一体地进行重型的分析、在线的多元汇聚以及在汇聚层进行数据服务。

另外利用 TiDB 和大数据进行数据层整合的用法也非常常见。即使客户已经有完整的大数据团队、大数据数仓或者整个这套架构,也完全适合 TiDB 的使用,TiDB 本身可以补充整个数仓层的实时能力,使用 Data Warehouse 或 Data Lake,数据有很大的可能性是通过 T+1 方式来处理,用户非常希望有一套数据库,既能提供实时的数据服务,也能提供实时的数据变更和同步。

接着,针对数据计算和数据流式计算的全能的 Sink 方案也很常见。例如,使用 Flink 往下游灌数据的时候,传统的方案是需要实时变更的数据流去同步 CDC 的数据。首先先做一个 CDC,然后再 Flink 写下去,下游可能是个 NoSQL 。这种方案牺牲了查询性能与 SQL。一般来说,使用 No SQL 只能作为点查点写的介质,进行实时分析,性能不足够好。其次如果选择 Hadoop 进行落地,数据可能没有办法直接进行更新,那么简单方式可能做 T+1,把 Delta 数据与历史数据做一次 Stage Emerge,才能得到最新的数据。

对于 TiDB 来说,它既可以体现出 NoSQL 的点状更新和可扩展的优势,又同时兼具了使用 Hadoop 之时,需要的列存以及列存上的分析型性能。所以流计算加 TiDB 是非常有意思的选择。我们的社区用户已经在使用 Flink 的同时也在使用 TiDB。

常见的典型场景是用户使用 MySQL 的时候,希望 MySQL 同步到某个数据分析数据库,然后提供高效的分析型作业,现在用户可以直接使用 TiDB 整套服务,TP 接入行存,AP 接入列存,这个架构对于一些业务,特别是一些专项的业务场景很实用,比如用户把原有的 CRM 或财务系统接到这上面。

另外实时数仓方面,上图是某一位用户的架构图,用户的数据从多个数据源,通过 Syncer 或者某种同步工具放到 TiDB 里面,把 TiDB 当作为一个 ODS(操作数据存储)来使用。除了 ODS 之外,TiDB 可以提供直接的数据查询和数据访问。

用户使用 TiSpark 结合数仓的数据,进行数据建模(数仓里的数据是偏冷数据,在 TiDB 里的数据是偏热的数据),两套数据进行整合、建模,建模的数据回写到 TiDB ,这时候 TiDB 就是它的服务层。Hadoop 直接对外进行数据服务会有一些问题,这套数据做完建模和整理之后,写到整个 TiDB ,这时候 TiDB 是数据应用层。

这是某一家著名的社交电商的用法,很有意思。它们将一部分的电商社交数据和订单数据存储在 TiDB,存储的数据是 TP 类的表设计,并不是适合分析,用户把 TiDB 集群,通过 TiDB 本身的 TiCDC 通一份实时数据出来,放置到 Flink 中,然后 Flink 会和其它的开拓过来的数据进 Join,再进行实时的聚合,写入到 TiDB ,这写回的 TiDB 可以作为用户的看板与报表数据。总体来说,实时的订单数据、整体用户的分析以及实时报表数据都使用同一套 TiDB。

5总结和展望

TiDB HTAP 不是简单的 1+1 的一个效果,很多所谓“差不多”的数据库,也有 TP 和 AP ,但是与两个积木拼在一起没有什么区别。真正的 HTAP 并不是选择 TP 还是 AP,而是这套数据库两种功能同时都有,在同一个业务场景当中可以同时使用这个功能,并且可以简化业务架构以及带来更好的实时分析能力。

Raft 的这套架构让我们有机会以优雅的方式去做松耦合,这将会带来一致性、高可用以及实时的复制能力。

结合 Flink 生态,我们可以更深入地把 TP 向实时和 AP 的方向进行推进,原始的架构 TiDB 加上 Raft 的复制一套列存副本,其实缺了非常重要的 ETL 环节,一部分数据的表现形式并不完全地适合做分析场景,所以配合 Flink 的实施能力,可以把整套 ETL 的环节很大程度上退回到实施层。

这套架构在云上将会有很大的想象空间。对用户来说,云下搭配多种不同的数据引擎,存在很大的部署和困难,不同的数据引擎组合,需要考虑放置多少节点,上云之后部署和运维会变得非常简单,所以这套架构在云上会发挥更大的威力。

数据库本身是一个合久必分,分久必合的过程,原生的数据库如 Oracle 或者 DBTool 代表性的数据库都是 HTAP,本质上是两者合一且多能的。在不断的需求向前推动的时候,在某些场特定场景下,这些数据库会被会一些特型的数据库所代替。比如,当我们选择数仓的时候,可以选择 Teradata,可以选择 Hadoop,而不是选择使用 Oracle。这仅仅是很简短的发展过程,当需求推进更复杂的时候,像 Teradata,Greenplum 曾经被大数据的东西代替了。

作为用户,它们非常期待一个数据库——我无需知道数据库里面都在做什么,也无需知道数据库有多少引擎,我希望尽可能地减少心智负担,仅仅用一个简单的盒子就可以完成,因此我们认为至少在某些领域上数据库在向更融合的方向发展。

 活动推荐:

想要了解更多关于数据层面的前沿技术吗?即将于 5 月 29-31 日落地北京的 QCon 全球软件开发大会,大数据实时计算与分析专题将主要介绍大数据实时计算和分析领域的最新趋势和最佳实践,用以为公司和行业提供更加智能的服务和决策。

点击【阅读原文】或识别上图二维码了解更多关于 QCon 全球软件开发大会,目前 8 折购票中,现在购票立减 1760 元,团购还有更多优惠!

有任何问题欢迎联系票务小姐姐 :电话 13269078023,微信 qcon-0410