前言
一年一度的数据库领域*会议VLDB 2019于美国当地时间8月26日-8月30日在洛杉矶召开。在本届大会上,阿里云数据库产品团队多篇论文入选Research Track和Industrial Track。
本文将对入围Industrial Track的论文《AnalyticDB: Realtime OLAP Database System at Alibaba
Cloud》进行深度解读。
1、背景
随着数据量的快速增长,越来越多的企业迎来业务数据化时代,数据成为了最重要的生产资料和业务升级依据。伴随着业务对海量数据实时分析的需求越来越多,数据分析技术这两年也迎来了一些新的挑战和变革:
1) 在线化和高可用、离线和在线的边界越来越模糊,一切数据皆服务化、一切分析皆在线化;
2) 高并发低延时,越来越多的数据系统直接服务终端客户,对系统的并发和处理延时提出了新的交互性挑战;
3) 混合负载,一套实时分析系统既要支持数据加工处理,又要支持高并发低延时的交互式查询;
4) 融合分析,随着对数据新的使用方式探索,需要解决结构化与非结构化数据融合场景下的数据检索和分析问题。
图1 阿里巴巴分析型数据库发展历史
阿里巴巴最初通过单节点Oracle进行准实时分析, 后来转到Oracle RAC。随着业务的飞速发展, 集中式的Shared Storage架构需要快速转向分布式,迁移到了Greenplum,但不到一年时间便遇到扩展性和并发的严重瓶颈。为了迎接更大数据集、更高并发、更高可用、更实时的数据应用发展趋势,从2011年开始,在线分析这个技术领域,阿里实时数据库坚定的走上了自研之路。大规模、海量数据实时分析型数据库系统——AnalyticDB,也是在这个时候诞生。
AnalyticDB是阿里巴巴自主研发、唯一经过超大规模、高并发以及核心业务验证的PB级实时分析型数据库。自2012年第一次在集团发布上线以来,至今已累计迭代发布近百个版本,支撑起集团内的电商、广告、菜鸟、文娱、飞猪等众多在线分析业务。AnalyticDB于2014年在阿里云开始正式对外输出,支撑行业既包括传统的大中型企业和*机构,也包括众多的互联网公司,覆盖外部十几个行业。AnalyticDB承接着阿里巴巴广告营销、商家数据服务、菜鸟物流、盒马新零售等众多核心业务的高并发分析处理,每年双十一上述众多实时分析业务高峰驱动着AnalyticDB不断的架构演进和技术创新。
图2 AnalyticDB设计挑战
2、挑战
已有的分析型数据库(以下简称OLAP)诸如Impala、Pinot、Druid等,总结了OLAP系统在设计的过程中应该解决的问题:低延迟、数据新鲜度、多样性、低成本、高扩展性、高可靠性。和这些已有的OLAP系统相比,AnalyticDB承载着更大的规模:2000+台物理机器、10PB+规模数据、百万张数据表以及万亿条数据行。因此,AnalyticDB在设计与实现的时候,不仅要解决已有OLAP系统要解决的问题,还要面临与解决三个更大的挑战:
1) 随着用户分析需求的急剧增加,用户的查询变得复杂且多样化:这些查询涵盖点查询、全表扫描、多表关联等,还会包含对任意列组合的筛选条件。如何在这种复杂分析场景下依然保证大部分甚至所有查询的低延迟,是一个非常大的挑战;
2) 如何在保证低延迟查询的情况下,仍然能处理每秒千万级别的写吞吐。传统的设计理念在同一条链路上同时处理读写请求,这会造成读写性能的互相严重影响。
3) 复杂分析场景下,会对行存、列存、关系型存储、复杂数据类型(JSON、vector、text)都有着强烈需求。如何设计一个对这些存储格式都很友好的存储层,也是一个业界难题。
接下来我们会介绍AnalyticDB的设计与实现关键点,探究AnanlyticDB是如何解决这些挑战的。
3、 AnalyticDB架构
AnalyticDB的整体架构如下图:
图3 AnalyticDB架构图
每个模块的具体描述如下:
- Coordinator(协调节点):协调节点负责接收JDBC/ODBC连接发过来的请求,并将请求分发给读节点或者写节点。
- Write Node(写节点):只处理写请求(如INSERT、DELETE、UPDATE)的节点。
- Read Node(读节点):只处理读请求(如SELECT)的节点。
- Pangu(盘古):高可靠分布式存储系统,是AnalyticDB依赖的基础模块。写节点会将写请求的数据刷到盘古上进行持久化。
- Fuxi(伏羲):资源管理与任务调度系统,是AnalyticDB依赖的基础模块。伏羲合理使用集群机器的空闲资源,以进行相关计算任务的异步调度执行。
3.1、表分区
为便于大规模分析处理,AnalyticDB对数据表进行分区。AnalyticDB数据表有两个分区级别:一级分区和二级分区。图4展示了创建带有一级分区、二级分区表的DDL语句:一级分区有50个分区,分区键为id列;二级分区有12个分区,分区键为dob列。数据行依据其包含的一级分区键的hash值,对应到不同的一级分区。通常,选择具有较高基数(cardinality)的列作为一级分区键,以保证数据行能均匀地分布到每个一级分区,最大化并行。用户还可以根据需要定义二级分区,以便进行数据的自动管理。二级分区拥有最大分区数,当二级分区的实际数目超过了这个最大分区数后,最老的二级分区会被自动删除。通常,选择时间列(天、周或月)作为二级分区列,这样,包含相同时间序列的数据行,会被划分到同一个二级分区中。
图4 创建带有分区的数据表的DDL
3.2、读写分离
传统OLAP系统在同一个链路上同时处理读写请求,因此,所有的并发读写请求都共享同一个资源池,也会互相影响。但是当读写并发同时非常大时,这种设计会由于过度的资源竞争而导致不好的性能。如图5所示,为了解决这个问题,同时确保读和写的高性能,AnalyticDB采用的架构为读写分离架构,即AnalyticDB有独立的读写节点各自处理读写请求,且写节点和读节点完全互相隔离。
图5 AnalyticDB读写分离
写节点:某个写节点会被选为主节点,其他写节点选为从节点,主节点和从节点之间通过ZooKeeper来进行通信。每个节点会独立负责某些一级分区的数据,主节点的任务就是决定每个节点负责哪些一级分区。协调节点会将写请求分发到对应的写节点上,写节点收到请求后,会将写SQL语句放到内存buffer中,这些buffer中的SQL语句称为log数据。写节点会将buffer中的log数据刷到盘古上,当刷盘古成功后,写节点会返回一个版本号(即LSN)给协调节点,表示写完成了。每个一级分区在其对应的写节点上,都会独立地对应一个版本号,每次写节点将某个一级分区的log数据刷到盘古后,都会增大这个版本号,并将最新版本号返回给协调节点。
当盘古上的log数据达到一定规模时,AnalyticDB会在伏羲上启动MapReduce任务,以将log数据转换成真实存储数据+索引。
读节点:每个读节点也独立负责某些一级分区的数据。在每个读节点初始化时,它会从盘古上读取最新版本数据(包括索引)。之后,基于这份数据,读节点会从写节点的内存buffer中将写请求log周期性地拉取过来,并在本地进行replay,replay之后的数据不会再存储到盘古中。读节点根据replay之后的数据,服务到来的读请求。
由于读节点需要去从写节点上拉取写请求数据,因此读节点为用户提供了两种可见性级别:实时(real-time)可见和延时(bounded-staleness)可见。实时可见允许读节点立即读到写节点写入的数据,延时可见允许读节点在一段时间后才读到写节点上写入的数据。AnalyticDB默认使用的可见性级别为延时可见。
当可见性级别选择为实时可见时,AnalyticDB采用了版本校验(version verification)机制来确保读写节点的同步。当用户执行完写请求后,他/她再发送一个查询请求到协调节点。协调节点会从对应写节点上获取最新的版本号(记为V1),连同查询请求一起下发给对应的读节点。读节点上在进行本地replay的时候,也会存有目前已经replay的版本号(记为V2)。读节点会比较V1和V2的大小:如果V1小于等于V2,那么读节点直接基于本地replay的数据执行查询;如果V1大于V2,那么读节点会从对应写节点上拉取直到V1的log数据,replay后再执行查询。需要强调的是,当可见性级别为延时可见时,协调节点在下发查询请求之前,不会实时地从对应写节点上获取最新版本号,而是使用缓存的之前写节点返回给自己的版本号(由于存在很多协调节点,所以某个协调节点上缓存的版本可能不是最新版本)。
3.3、可靠性与扩展性
- 可靠性。
AnalyticDB的读节点和写节点均具有高可靠性。对于写节点来说,当某个从节点失效的时候,主节点会将其负责的一级分区均匀地分配给其他正常的从节点。当主节点失效的时候,新的主节点会被重新选出来进行替代。对于读节点来说,用户可以指定读节点的副本个数(默认为2),不同副本会被放到不同的物理机上,以提供高可靠性。当某个读节点失效时,协调节点会将读请求自动下发到其他副本上。需要强调的是,某个写节点的失效,也不会影响读节点正常地拉取log数据和replay数据。因为log数据是持久化在盘古上的,在写节点失效地时候,读节点可以直接去盘古上拉取log数据。 - 扩展性。
AnalyticDB的读节点和写节点也具有高可扩展性。当一个新的写节点加入时,主节点会自动调整一级分区的分配,保证负载均衡。读节点的可扩展性也是由相同的机制保证的,只不过对于读节点,调整一级分区分配的是协调节点。
3.4、集群管理
AnalyticDB集群管理模式为多租户模式,即同一个集群上可以运行多个AnalyticDB实例。我们设计并实现了一个称为Gallardo的集群管理组件,Gallardo采用CGroup技术对不同AnalyticDB实例进行资源隔离(CPU核、内存、网络带宽),并负责实例的稳定性。当新的AnalyticDB实例创建的时候,Gallardo会为其分配相应的资源,在分配资源的时候,Gallardo会将协调节点、写节点、读节点以及读节点的不同副本放置在不同物理机上,以保证可靠性要求。
4、 AnalyticDB存储层设计
AnalyticDB存储围绕为“计算而生” 这一设计目标,主要解决海量数据的极速查询问题。本章将详细剖析存储和索引原理,并阐述设计背后的思考。
4.1、存储层架构
AnalyticDB存储层采用Lambda架构,读节点上的数据包括基线数据和增量数据两部分。如图6左边所示,基线数据是增量写入前的所有历史数据,包括索引和明细数据;增量数据没有索引,只有明细数据。明细数据来自于写入节点的日志数据,并按照行列混存的结构(见4.2节)保存在读节点的SSD磁盘上。
图6 Lamda架构和数据查询过程
4.1.1、写入和查询过程
AnalyticDB不仅支持实时INSERT,同时还支持UPDATE和DELETE。执行DELTE时,AnalyticDB没有采用立即物理删除,而是使用bitset来标记被删除的数据行,这些被标记删除的数据会在数据合并(4.1.2)时被真正物理删除。执行UPDATE时,UPDATE操作会被转换为DELETE + INSERT。COPY-ON-WRITE技术被用来支持MVCC多版本控制。当数据发生删除或更新时,AnalyticDB会产生一个新版本的bitset,并将被删除后更新的行的位设置1。此时正在运行的查询可以继续使用老版本的delete bitset,不会被写入阻塞。
算法1、2、3分别详细解释了INSERT、DELETE和QUERY执行的详细过程。
4.1.2、数据合并
由于没有全局索引,随着数据的不断实时写入,增量数据的查询性能会越来越慢。因此我们会在后台通过伏羲启动一个MapReduce 任务来合并基线数据和增量数据(同时去掉标记为删除的数据)形成一个新的基线数据,并基于新的基线数据构建全量索引。如图7所示,当数据合并任务开始时,首先会将增量数据引擎标记为immutable,并创建一个新的活跃增量数据引擎,该活跃增量数据引擎接受实时写入的数据。在合并任务执行过程中,所有查询和INSERT/DELETE都会执行在基线数据、immutable增量数据和活跃增量数据这三个数据引擎上。当合并任务完成后,老的基线数据和immutable增量数据会被替换为新的基线数据,新的查询将执行在新基线数据和活跃增量数据这两个数据引擎上。等所有老的查询都结束后,老基线数据和immutable增量数据会被删除。
图7 数据合并过程
4.2、存储数据结构
4.2.1、行列混存
在海量数据分析场景下,数据分析业务主要有以下三类workload:
1) OLAP场景下的大规模多维分析:海量数据的统计分析和多表关联,比较适合列存格式;
2) 高并发的点查:通常需要捞取出一整行的明细数据,比较适合行存。
3) 高写入吞吐:每秒千万的高吞吐实时写入,比较适合行存。
无论是行存和列存都无法同时满足以上需求,如何在一个系统中同时解决以上问题?如图8所示,AnalyticDB提出使用行列混存 结构,使用一套存储格式,兼具行存和列存之所长,同时满足以上三类workload。
图8 行列混存数据格式
对于一张表,每k行数据组成一个Row Group。在每个Row Group中,每列数据连续的存放在单独的block中,Row Group在磁盘上连续存放。每个block由于数据类型相同,可以支持高效的压缩。同时为了保证点查询的性能,我们会将所有的数据按照按指定列(聚集列)排序存放,好处是在按该列查询时显著减少磁盘随机IO次数。最后,与列存写入相比,行列混存可以把多个文件的写入被转化成单个文件的顺序写入,降低了IO的开销。
4.2.2、元数据
为了加速查询,AnalyticDB对每列构建一份元数据,并保存在一个叫detail_meta的单独文件中。detail_meta文件通常较小(小于1MB),首次查询时被加载在内存中。如图8左边所示,元数据主要包括4部分:
- Header。包括版本号,文件长度以及一些统计信息。
- 列统计信息。包括行数,NULL值数,cardinality,SUM,MAX和MIN 值。优化器根据这些信息来生成最佳执行计划。
- 字典。对于cardinality较少(小于1024)的列,AnalyticDB采用字典编码,数据文件里保存字典号码。字典保存在该字段中。
- 块地址信息。保存块号到数据文件起始地址和长度的映射关系。
4.2.3、非结构化数据存储
AnalyticDB不仅支持基本数据类型,也支持长文本、JSON和向量数组等非结构化数据类型。但是单行文本、JSON、向量数据的长度通常比基本数据类型的数据要长,单行长达数十KB甚至MB级别。基于固定行数的block设计会导致单个block过大,IO效率降低。因此针对非结构化数据类型,AnalyticDB采用了定长数据块技术,每个块大小相同。
如图9所示,每个Block由多个定长数据块(称作FBlock)组成,每个FBlock大小为32KB,单独存储为一个文件中。Block中保存的不再是原始数据内容,而是数据所在FBlock的块号和FBlock内的偏移位置。查询时,首先读取Block中的内容,得到FBlock的块号和块内偏移,然后再读取FBlock内容,得到非结构化数据内容。
图9 定长数据块格式
4.3、索引管理
索引是数据库系统里影响性能最关键组件,但是传统的索引并不能很好满足OLAP场景下任意ad-hoc查询的需求。例如B+Tree索引,在海量数据下存在中间节点过多,容易产生大量随机IO等问题;Druid可以对多列数据构建倒排索引,但是只能支持特定数据类型(字符串类型,不支持数值类型); 传统数据库构建索引占用大量资源,影响写入性能;现有的索引也不支持非结构化数据类型,例如JSON,长文本和向量。
因此,AnalyticDB设计和实现了一个新的索引引擎,在不影响写入性能的情况下,支持结构化和非结构化数据类型索引。它将构建过程从写入链路中移除,采用后台异步构建模式,支持对所有列构建索引,从而解决了OLAP任意查询的性能问题。
4.3.1、索引查询
AnalyticDB默认对所有列构建索引,并保存在一个单独的文件中。与传统的数据库不同,AnalyticDB索引中的key是列的值,value是该值出现的所有行号集合,并支持所有的条件同时走索引查询。如图10所示,该SQL是一个复杂查询包括结构化列条件和非结构化列条件。索引引擎会首先对每个条件走索引扫描,得到多个行号集合,然后将AND/OR 条件转换为行号集合的UNION/INTESECT操作。
图10 索引查询示例
AnalyticDB在索引引擎是实现上也做了大量的优化,包括:多路流式归并、索引选择CBO和索引结果缓存。
多路流式归并:传统数据库大多采用2路归并策略,在条件数特别多的场景下,会导致大量中间结果,计算效率很低。AanlyticDB采用K路流式归并算法,可以支持多个集合并行归并,避免产生大量中间结果集合,提升了整个归并的速度。
索引选择CBO:当where条件中包括多个条件,并不是所有的条件走索引扫描能取得最佳的性能。利用索引中的统计信息,提前估算出各个条件可能的选择率,对于选择率很高的条件走索引查询,其他条件直接在上层进行过滤操作。例如对于where id = 1 and 0 < x < 1000000的情况下,d = 1这个条件的选择率已经很高,则0索引结果缓存:在OLAP分析场景中,多个查询条件中,可能会出现部分条件固定不变或重复多次出现。针对这种场景AnalyticDB 实现了一个高效的无锁缓存,缓存的的key为等值或range条件,value为行号集合。这样在出现重复查询情况下,可以直接读取缓存,避免索引IO扫描开销。
4.3.2、索引构建
为了支持每秒千万的实时数据写入,避免同步构建索引影响实时写入的性能,AnalyticDB并没有采用同步构建索引的策略,而是采用异步后台进程构建索引的方式。索引引擎会根据时间或增量数据的大小来决定是否启动后台进程来构建索引。该后台进程读取Pangu上的历史全量数据和新写入的增量日志数据,完成数据合并形成新的全量数据,并对该全量数据重新构建索引。该过程通过伏羲的MapReduce任务执行,选择负载较低的机器执行,对用户完全透明。
5、 AnalyticDB计算层设计
5.1、优化器
AnalyticDB综合了CBO(基于代价估算)和RBO(基于规则)的优化模型来为实时在线分析提供最优的计划。优化规则的丰富程度是能否产生最优计划的一个重要指标。因为只有可选方案足够多时,才有可能选到最优的执行计划。AnalyticDB提供了丰富的关系代数转换规则,用来确保不会遗漏最优计划。
- 基础优化规则
1) 裁剪规则:列裁剪、分区裁剪、子查询裁剪
2) 下推/合并规则:谓词下推、函数下推、聚合下推、Limit下推
3) 去重规则:Project去重、Exchange去重、Sort去重
4) 常量折叠/谓词推导
- 探测优化规则
1) Joins:BroadcastHashJoin、RedistributedHashJoin、NestLoopIndexJoin
2) Aggregate:HashAggregate、SingleAggregate
3) JoinReorder
4) GroupBy下推、Exchange下推、Sort下推
- 高级优化规则
1) CTE
除了这些,我们创新性引入了两个关键功能:存储感知的优化和高效实时采样。
5.1.1、存储感知的优化
执行下推。执行下推是将SQL中可以依赖存储能力的关系代数计算进行提取,将查询计划等价转换为两部分,一部分在计算层执行,一部分下推给存储层执行。由于原有查询计划中并没有明确的界限来分隔两部分,因此需要依赖存储层本身的计算能力,通过关系代数的等价转换规则,将其分离。执行下推在很多分布式数据库中都有类似的实现,但下推算子的极致基本都是以单列条件的AND操作为主,其他算子如函数、JOIN等都在计算层实现。这主要是由于其并未实现存储层向上注册计算能力的逻辑,默认认为存储层最多只能做单列或者组合条件的过滤。
AnalyticDB引入了一种STARs模型作为执行下推的框架,通过将异构数据源的执行能力按照关系代数的维度进行抽象,将存储的能力特征化为其所能处理的关系代数的能力。在优化器完成初步的分布式执行计划后,利用动态规划的方式针对不同的数据源将适合下推给存储执行的关系代数算子进行封装,转化为对应的存储的API调用。
图11 STARs模型
于此同时,STARs框架还加入了代价的计算,也就是说并非简单的依赖存储的能力进行执行下推,而是在对其进行关系代数能力抽象的同时,对其执行的代价进行数值化。在进行动态规划时,将代价和执行能力同时作为参考因素,避免盲目的下推导致性能变差。
Join下推。数据重分布是分布式数据库执行计划区别于传统数据库执行计划的一个重要方面,主要是由于数据的物理分布特性与关系代数的逻辑语义不匹配导致的。比如SQL:SELECT t.tid, count(*) FROM t JOIN s in t.sid = s.sid GROUP BY t.tid。其中t表按照tid进行Hash分区,s表按照sid进行Hash分区。其逻辑执行计划如下:
基于规则的优化器。在生成Aggregation/Join的执行计划时,会发现其下游节点并不符合当前算子对数据物理分布特性的要求,因此会强制增加一个数据数据重分布的算子来保证其执行语义的正确,数据重分布带来的物理开销非常大,涉及到数据的序列化、反序列化、网络开销等等,因此避免多次数据重分布是对于分布式计算是一个非常重要的优化。AnalyticDB的优化器可以通过将所有可能的执行计划进行展开,并对其进行代价计算。正是使用了这种方式,AnalyticDB实现了在不同的数据规模时,生成对应其数据特征最优的执行计划。
图12 Aggregation/Join执行计划产生
基于索引的Join和聚合 。将Join变为查找现有索引,全索引的设计进一步消除了构建哈希开销。当调整Join的顺序时,如果大多数Join列是分区列且具有索引,优化器会避免使用BushyTree,而更倾向选择LeftDeepTree。采用LeftDeepTree,AnalyticDB能更好地利用现有索引。优化器更近一步下推了谓词和聚合。聚合函数,比如count和查询过滤可以直接基于索引计算。所有这些组合降低了查询延迟,同时提高集群利用率,从而使得AnalyticDB能轻松支持高并发。
图13 基于索引的Join优化
5.1.2、高效实时采样
统计信息是优化器在做基于代价查询优化所需的基本信息,通常包括有关表、列和索引等的统计信息。传统数据仓库仅收集有限的统计信息,例如列上典型的最常值(MFV)。商业数据库为用户提供了收集统计信息的工具,但这通常取决于DBA的经验,依赖DBA来决定收集哪些统计数据,并依赖于服务或工具供应商。
上述方法收集的统计数据通常都是静态的,它可能需要在一段时间后,或者当数据更改达到一定程度,来重新收集。但是,随着业务应用程序变得越来越复杂和动态,预定义的统计信息收集可能无法以更有针对性的方式帮助查询。例如,用户可以选择不同的聚合列和列数,其组合可能会有很大差异。但是,在查询生成之前很难预测这样的组合。因此,很难在统计收集时决定正确统计方案。但是,此类统计信息可帮助优化器做出正确决定。
我们设计了一个查询驱动的动态统计信息收集机制来解决此问题。守护程序动态监视传入的查询工作负载和特点以提取其查询模式,并基于查询模式,分析缺失和有益的统计数据。在此分析和预测之上,异步统计信息收集任务在后台执行。这项工作旨在减少收集不必要的统计数据,同时使大多数即将到来的查询受益。对于前面提到的聚合示例,收集多列统计信息通常很昂贵,尤其是当用户表有大量列的时候。根据我们的动态工作负载分析和预测,可以做到仅收集必要的多列统计信息,同时,优化器能够利用这些统计数据来估计聚合中不同选项的成本并做出正确的决策。
5.2、执行引擎
在优化器之下,AnalyticDB在MPP架构基础上,采用流水线执行的DAG架构,构建了一个适用于低延迟和高吞吐量工作负载的执行器。AnalyticDB的列式执行引擎能够充分利用底层的行列混合存储。与行式执行引擎相比,当前的向量化执行引擎更加缓存友好,能避免将不必要的数据加载到内存中。
图14 流水线模式执行引擎
与许多 OLAP 系统一样,AnalyticDB在运行时利用代码生成器(CodeGen) 来提高 CPU 密集型计算的性能。AnalyticDB的CodeGen基于 ANTLR ASM来动态生成表达式的代码树。同时此 CodeGen 引擎还将运行时因素纳入考虑,让AnalyticDB能在Task级别利用异构新硬件的能力。例如,如果集群中CPU支持 AVX-512指令集,我们通过生成字节码使用SIMD来提高性能。在此之外,通过整合内部数据表示形式,在存储层和执行引擎之间,AnalyticDB是能够直接对序列化二进制数据进行操作,而不是Java 对象。这有助于消除序列化和去序列化的开销,这在大数据量shuffle时可能会节约20%以上的时间。
6、 实验数据展示
6.1、实验准备
- 实验环境。实验运行在一个拥有八台机器的集群环境上,每天机器配置为Intel Xeon Platinum 8163 CPU (@2.50GHz)、300GB内存、3TB SSD和万兆网卡。集群上分配了一个拥有4个协调节点、4个写节点、32个读节点的AnalyticDB实例。
- 实验测试集。实验选择了两种测试集:一个是阿里巴巴集团内部产生的真实数据集,分别为1TB和10TB大小;一个是标准TPC-H测试集。
- 查询语句。针对真实数据集,选择了三种类型的查询语句,如表1所示,它们分别为全表扫描、点查询、多表关联查询。
表1 真实数据集上的三种类型查询
6.2、实验数据
真实数据集。图14展示了AnalyticDB在真实数据集上运行三种查询语句的性能。
图15 1TB和10TB数据量的查询延迟
如图所示,得益于全索引设计和行列混存存储模式,AnalyticDB均可以在秒级甚至更短的时间内完成三种类型的查询语句。对于任意一种查询,AnalyticDB均可以精准定位到对应的一级分区或二级分区,并进行索引筛选,从而避免无效、大量的全表数据扫描。也正是因为AnalyticDB能快速定位、扫描有效数据,1TB和10TB数据量的查询性能差别不大,也即AnalyticDB的性能受数据表大小影响有限。
图16 TPC-H性能
TPC-H数据集。图15展示了AnalyticDB在1TB TPC-H数据集下的性能,同时还和PrestoDB、Spark-SQL、Greenplum进行了对比。得益于流水线处理、全列索引、行列混存、运行时索引路径选择、K路归并、向量化执行引擎、CodeGen等优化机制,AnalyticDB获得了最优的TCP-H测试运行时间,并比第二好的Greenplum快了近2倍。