近日,第12届中国数据库技术大会(DTCC 2021)在北京国际会议中心召开。作为全球领先的云计算、数据库产品服务商,腾讯云数据库集结多位顶级技术大咖亮相本次大会,围绕当前比较热门的数据库技术主题,共同探讨最前沿的技术趋势与实践。
本期为大家带来腾讯专家工程师朱阅岸老师在本次大会上的分享,主题为“HTAP系统的问题与主义之争”。以下是分享实录:
问题与主义之争其实是上世纪初胡适与李大钊之间的一场论战。胡适主张改良,提倡解决一个个问题,也就是少谈些主义,多研究些问题;而李大钊则主张改革,认为只有解决了这个根本问题,其他的问题才能解决,二人代表着两个截然不同的路线。其实围绕着HTAP系统的演进也存在类似两条路线,一条路线是改良,一个路线是通过改革的方式打造全新的系统。今天我就为大家分享HTAP系统的技术实现相关路线。
1. HTAP的定义与挑战
我们先来解释HTAP的定义与挑战是什么。下图是经典的数据处理框架,我们在里面划分出两种数据库系统:一种是事务型的系统,这是数据源头产生的地方;另一种是分析型的系统,是我们的数仓。数据会定期从交易型数据库中借助ETL的方式流入到数仓. 然后在数据仓库做分析处理,产生相应的报表和报告。企业的经营决策者能够通过分析报告和决策报表去观察企业的经营行为,从而观察到未来的发展趋势。这是数据宝贵之处的体现之一。不少公司都在数据基础设施上投入人力物力,期待在数据变现上获得更好的回报。
研究表明,这些样本公司在每13美金的投入中就有1美金投入到数据分析里,有74%的公司想成为一个数据驱动型的公司,如果一个公司采用更为先进的数据分析手段,那它超越竞争对手的可能性将达到两倍。
数据分析具备巨大的商业价值。但在目前的数据处理框架中,OLTP和OLAP两类系统是割裂开的,主要是通过ETL把数据从交易型数据库导入到分析型数据库,而ETL的时延比较大,可以达到数十分钟到几小时,甚至是几天。上图右下角的图片显示,数据的商业价值会随着时间的流逝而下降。数据产生再经过ETL导入到数仓,在数仓里进行分析,然后做决策,执行相应的动作。在这时,数据的商业价值就会大打折扣。
因此最理想的情况是在数据产生后就能迅速对其进行分析。为了解决这个问题, HTAP系统应运而生,它的初衷就是要打破事务处理和分析处理的界限,使企业能够通过HTAP系统更好地发现市场反馈,获得更好的创新。这么先进的数据处理技术,为什么近年来才引起人们的关注呢?我个人认为,这主要得益于现代列存储技术的发展,HTAP系统的出现才成为了可能。
以前客户用SQL Server做查询分析处理,需要十多个小时以上。在这种技术能力下是无法达到实现HTAP系统要求的。后来SQL Server采用列存储技术,耗时十几个小时的分析可以降到几分钟,甚至可以在秒级时间内把结果运行出来。列存储技术及背后的向量查询引擎的发展,使得HTAP登上了历史舞台。
HTAP能让数据产生后马上就可以进入分析场景。但它面临最大的问题是如何把OLTP和OLAP两类工作负载更好放在一个系统上运行,毕竟这两类工作负载本质上是互斥的。交易型的事务是短事务,以写为主;分析型的事务是长事务,以读为主,经常需要做全表扫描,在扫描的基础上做统计、聚合等操作。这种情况下,OLAP的事务经常需要独占系统资源,使交易型的事务吞吐量下降。有研究表明,把OLTP和OLAP放在一个系统里调度,OLTP的吞吐量可能会下降到原本的1/3到1/5。所以如何让OLTP和OLAP在系统运行的过程中相互干扰最小,就成为HTAP系统设计的难题。
从过去十多年的发展来看,主要有两种实现HTAP的方案:第一种是改良的方式,在现有业务系统上延伸扩展,打造一个HTAP的系统来满足业务的需要,;第二种是从头开始设计一个具有颠覆性的系统. 当然第二种方式会产生更多有价值的技术,也会涉及到比较多技术难题,包含技术突破、业务适配等。
从现在来看很难说哪种更好,今天的分享我们也不回答哪一条路线更好,我们会在这两条路线上挑选典型的具有鲜明技术特色的系统,来和大家分享,同时会在最后给出最近十年来HTAP系统技术的演变过程和发展趋势。
2. HTAP系统的架构实践
2.1 HTAP系统的类别
总体来看,HTAP系统架构的实践可以分成两类:一类是改革,另一类是改良。前者采用One size fits all的策略,用一个大而全的系统同时满足OLTP和OLAP的需求,后者采用One size doesn’t fit all模型,将OLTP和OLAP两种系统组合起来,通过CDC的方式把OLTP上产生的数据以增量的方式导入到数仓进行分析。
第一类下又分为两个子类。第一个子类是单拷贝系统,在一个系统里用一种数据格式满足两种业务需求,通常是采用PAX存储。系统整体来看是采用行存储,但是当它把数据打包存储到某个页面时转换成列存储的形式。另一种是双拷贝系统,一个系统里同时存在行存储和列存储,行存储上的更新会定期导入到列存储里转换成列存储格式。在列存储上进行分析,行存储上执行更新。这在某种程度上降低了它们的竞争。第二类可以分为共享存储和独立存储两个子类。
下图右上角就是这四种类型的系统实现HTAP特征时的比较,可以从两个维度来刻画,一是数据新鲜度,二是工作负载干扰程度。
One size fits all策略把OLTP和OLAP两种工作负载放在一个系统上,如图1左上角,就干扰程度而言,OLTP和OLAP相互干扰最强,而单系统单拷贝方式尤为明显,其次就是单系统双拷贝的方式。这两种实现方式的缺点是OLTP/OLAP的干扰比较大会导致事务工作负载的吞吐严重下降,但优点是数据可见度高,因为不需要做数据的同步导入导出或数据转换。
One size doesn’t fit all 即松耦合模型也有两种类别,橙色椭圆代表共享存储下的松耦合系统,好像目前还没有有商业化的产品,只有IBM出了一个DEMO;另一种是采用类似proxy方式把TP与AP两种独立系统组合起来的松耦合系统。这两类系统中,OLTP和OLAP分别在两个独立的系统上进行,可以把干扰降到最小,但数据需要同步,交易型数据库更新的数据要通过CDC的方式同步到分析型系统上,数据的延迟会比较大。
列出了几个典型的HTAP工作负载对时延的需求。系统监控的延迟在20毫秒,在线游戏、个性化广告推荐、商品价格监控,则是在100-200之间。
2.2 单系统单拷贝之Hyper
接下来我们从四类系统实践中挑选部分代表性的系统来看HTAP技术如何具体实现。首先是Hyper。Hyper是很典型的系统,它原本是德国慕尼黑工业大学的 Thomas Neumann教授团队开发的原型产品,但是性能与创新性实在太好,后来被美国tableau公司收购,从学术型的产品变成了商业化的产品,不论从技术与经历都具有较高的代表性。
Hyper的查询执行模式很有考究。OLTP是串行执行,不需要加锁。这是因典型OLTP数据库把大部分时间都消耗在加锁、缓冲区管理、日志管理上,这些操作消耗了系统80%左右的资源。在Hyper中没有这些开销,事务串行执行,去除维护事务隔离性等开销。一个事务串行执行的耗时只有微秒级别,这个是可以接受的。VoltDB是类似的一个系统,事务执行同样不需要加锁,串行地执行(通过分片达到并行执行的效果)。OLAP则通过fork产生子进程在事务一致性的拷贝上运行查询。更新时再把相关的数据页拷贝出来,让交易型事务在不同的数据页上进行更新(Copy-on-Write)。
此外通过区分冷热数据的方式,把数据分成热数据、冷数据和温数据。上图右下角就是通过硬件的方式即MMU的内存管理单元去区分数据的访问热度。热数据放在正常页面即小页面上,冷数据是压缩存储,放在4M的大页面上。这种做法的好处是更新代价比较小,同时在做OLAP查询的时候,在大页面上会有比较好的性能。
Hyper的查询引擎也是相当优秀的,它利用向量化的查询引擎,用LLVM生成可执行的机器码。这个技术非常具有代表性,连著名的Spark也参考了它的代码生成技术。
2.3 单系统单拷贝之SAP HANA
HANA也是采用单系统单拷贝实现HTAP。它不太像一个数据库系统,反而像是一个支持多引擎多工作负载类型的统一数据管理平台。HANA系统的分层结构做得很好,总体来看可以分为编译层和执行层,上层又可以分为查询的解析,生成抽象语法树AST,再映射到计算图模型,接着对计算图模型进行物理优化。此后由分布式执行框架去构建实际的数据流,将具体的物理执行算子分发到特定的引擎执行。因为HANA支持多个工作引擎,比如数据仓的查询引擎、文本、图等,它向上提供了针对这些特定引擎的物理算子,比如关系操作算子、图计算的算子等。
执行引擎下又有统一的表模型。它向上提供一个统一的接口,类似于MySQL下的handler接口,下面的存储引擎负责实现具体的存储,上面的查询执行器只要实现handler接口就可以对接到系统里去。里面的存储分成三级:首先是L1-delta,对应的是热数据和无压缩的行存储;其次是L2-delta,对应的是轻量级压缩的列存储,比如字典压缩;最后是L3-main store,对应的是重度压缩列存储。更新发生在L1-delta,数据会定量地导入到Main store里。Main store是读友好的实现方式,满足AP类型的查询,而L1-delta是写优化的实现。通过这种方式来满足HTAP的需求。
它会定期地执行合并操作,把数据定期地合并到Main store里。在合并操作的过程中会生成Main2和Delta2,这时读操作就在Main1、Delta1和Delta2进行。拷贝完成以后Main1和Delta1最终会合并到Main2,最后切换过来,在Main2上进行读操作,写操作发生在Delta2。数据加锁的行为只是发生在缓冲区切换阶段。L1-delta采用redo日志保持持久性,Main store采用影子页技术减少日志的写入,保持一致性与持久性。
2.4 单系统双拷贝之SQL Server
SQL Server是一个双拷贝系统,把数据按行切分成group,定期转变成列存储。具体来说是,每一百万条数据做一个Group,进行切分,然后针对每列转换成列存储,叫做segment。每个列采用单独的压缩算法,比如有些适合用字典压缩,有些适合用数值压缩,因此不同的列采用不同的压缩算法。转换后的列存储附加额外字典表(如有),存储到Blob类型中。外面的directory追踪这些segment的存放位置。SQL Server也针对列存储提供了批量执行的算法,加速分析操作。
2.5 单系统双拷贝之Oracle
Oracle是另外一个采用双拷贝方式实现HTAP的系统,每个系统里针对有需要的表,会同时存在一份行存储和列存储,在列存储上做分析操作;在行存储上进行更新,定期同步到列存储里。系统可以灵活指定需要采用行存与列存的表,也可以系统运行时更改表特性。Oracle利用RAC集群进行横向拓展。
这里举两个例子,来介绍Oracle是如何利用列存储加速分期操作的。假如我们查找Outlet这个商店类型所有的销售额,首先扫描维表,根据“type把Outlet类型的商店ID拿到,生成一个map”,接着在事实表的对于外键列里把商品ID拿出来在这个map查找,如果找到就可以把Amount累加起来。它通过只扫描某些特定的列,生成相关的map,就可以把要访问的数据找出来,即把关联操作简化成表扫描操作,提升性能。
另外一个比较复杂的是要扫描两个维表,生成两个vectors,在里面再去事实表找相关的外键列,就可以直接定位到相关的vector,如果符合条件,就分类写到相应的临时表里。优点在于可以把表关联操作转换成表扫描操作,只需要访问查询中涉及到的列。
2.6 松耦合共享存储之Wildfire
目前还没有商业化的HTAP产品采用松耦合共享存储架构,但是IBM开发了一个原型,叫Wildfire。从技术细节来看,它的系统分成两类节点:一类是有状态的节点,一类是无状态的节点。有状态的节点处理事务型的请求,无状态的节点处理OLAP型的请求。OLAP型的请求可以容忍一定的延迟。
OLTP型的数据不会直接写入到共享文件系统里,会写入私有组成的一个集群,按照表分片的模式在这里进行数据的快速写入,再定期导入到共享文件系统里,然后供分析型查询去执行。分析型查询会自己去定制一个引擎,去对接spark executor。这个引擎是无状态的,可以去定制修改,在数据分析领域也是比较强悍的引擎,叫BLU,是IBM自家的分析执行引擎。总体上看,这个系统分成两个集群:一个是OLTP,一个是OLAP。
国内有部分产品的技术和该架构比较类似,利用spark的能力和生态去做分析型查询,比如利用spark的查询优化器和机器学习能力去构建OLAP能力,OLTP能力则自己构建,这样就把数据和这两套系统在某种程度上做了一定的融合,变成HTAP。
2.7 松耦合独立存储之F1 Lightning
松耦合独立系统近几年比较流行,有两个典型代表,一个是谷歌的F1 Lightning,另外一个是IBM的IDAA。我们先来介绍F1 Lightning。
F1 Lightning把系统分成三个模块:OLTP数据库、Lightning、查询执行引擎。Lightning通过Changepump捕获OLTP数据库的更新,以订阅的方式把数据分发到订阅者。Lightning内部还开发了一个适配器,将CDC的模式转换成内部统一的格式。内部有两级存储:内存存储和磁盘存储。内存存储是以行存储的模式存在,采用B-tree索引,在这里没有转换成列存储,只有在数据写入磁盘时才把行存储转换成列存储。上层查询通过快照的机制读取可见范围的相关数据。F1 Lightning将捕获的日志分成两层存储,为日志维护系统范围的检查点时间戳以及为适配不同数据库而设计的客户端接口很大程度上借鉴了Databus。这种实现方式带来的问题是查询延迟。从谷歌的测试来看,查询延迟在8-10分钟之间。因为采用CDC方式,要把OLTP数据转换成CDC内部统一的格式,再批量提交,因此延迟会比较大。
2.8 松耦合独立存储之IDAA
接下来介绍IBM的IDAA。最初IBM也开发了类似松耦合的HTAP架构。下图中左边是Db2,右边是他们的Warehouse,挂载到事务型引擎,事务型引擎将更新定期同步。但IBM系统设计者认为,CDC方案需要花费大量的时间和背景知识来维护额外的进程,且延迟比较大。基于这个原因,他们对此进行了改进,通过轻量级的集成同步方案规避上述问题,将延迟减少180倍。
CDC的方案需要在源端经历6个步骤:读取数据,解密;过滤无关的日志,按照LSN排序;之后还要对数据进行行列转换;把数据暂存起来,把数据转换成CDC统一内部的格式;再批量等待commit或Rollback,发送之前还要把Rollback数据去除。这种情况对源端的压力是比较大的,延迟也会比较大。
IDAA则建议把这几个步骤都挪到目的端执行。目的端能够原生地识别从Db2里传输过来的数据,当然这个是比较定制化的方案,通用性没那么好,但延迟可大幅降低,只有原来的1/180。现在延迟只有1-2秒左右就能读到最新的数据。
2.9 HTAP技术发展一览
这部分我们来对HTAP近10年来的发展脉络做梳理。图中的直线方向表示后者的技术借鉴前驱或者是从该产品演变过来的。
从图上可以看到,HTAP技术在2015、2016年之前主要是以One size fits all 策略为主,2015、2016年以后则以松偶合架构为主。这几年如F1 Lightning、IDAA等都是采用松偶合方式来实现HTAP,甚至SAP也是有采用松偶合的技术方案实现HTAP。从One size fits all 的演变趋势来看,大多是从双拷贝转换成单拷贝,从行存储转换成单一的列存储来应对OLTP/OLAP的请求。从原先在磁盘上用列存储,到内存里用行存储,再到现在统一改成列存储。这就是最近10年来HTAP技术的演变趋势。
3. 云原生对于HTAP系统的启示
这一部分我们来看看云原生架构对HTAP系统实现是否能带来帮助。
下面是三个值得思考的问题:首先是云原生架构即存算分离架构能否为HTAP的设计带来便利?其次是存算分离架构和弹性算力能否缓解资源竞争?因为这两条技术下的系统,在资源竞争和数据可见度上的表现各有优缺点,并不能说哪种产品的表现最优,都是在数据延迟和资源争用上做取舍。此外,云环境下丰富的计算资源池和存储资源池能否针对不同的计算、不同的业务特征进行划分从而降低用户的成本,这也是值得大家去思考的问题。
下图是我构想中的愿景图。总体来看,理想的情况是,在一个云原生架构里,Share storage和Query processing 分层,在查询入口处采用多租户设计,这里执行查询解析和查询的优化,然后把查询的执行推到查询执行层。
查询执行层分为有状态和无状态两种,分别处理与OLTP和OLAP相关的请求。共享存储层Share storage针对工作模式和工作负载采取不同的存储设计策略。在OLAP方面可以主打性价比,用对象存储和纠错编码模式降低成本。AWS Redshift团队的调研也表明,OLAP型的用户会更加关注成本,所以在Share storage里OLAP方面可以偏向性价比的权衡。而OLTP数据存储则采用高性能的存储,优化事务提交的关键路径,达到用户最优体验,比如说高性能Nvme,加速事务的提交速度,然后定期把数据从OLTP型存储导入到OLAP型的存储。
希望在不久的未来,可以看到相关厂商或数据库开发者实现这样的方案或技术特征。
4.总结
总体来看,HTAP已有的问题是OLTP和OLAP这两种互斥的属性,如何在一个系统里共存而干扰尽可能小,数据可见度延迟尽可能短。
目前存在两种架构实践:一种是One size fits all,用一个数据库系统来满足OLTP和OLAP多样化工作负载的需求;对系统相关的架构革新,针对两种工作负载的特性做更好的适配优化。另外一种是采用松偶合的方式,通过CDC把两类系统黏合起来,在上面架构一个类似于proxy的东西,为用户呈现出一个系统的表象,用户感知不到OLAP型系统的存在。这种方案能更好地降低两类工作负载对资源的竞争,但是由于需要跨系统进行数据同步因此延迟较大。而前一种方案虽然资源竞争比较大,但不需要对数据进行跨系统同步,因此延迟比较小,数据能见度、新鲜度高,可以满足紧急型任务的需求。
展望未来,我们认为云原生的弹性算力可能更适合HTAP系统,在云原生上会诞生更优秀的HTAP系统。