购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第1章
Chapter 1
Doris概述

Doris是由百度自研并开源的OLAP数据库,以易用的特点被业内熟知。Doris支持标准的SQL并且完全兼容MySQL协议,仅需亚秒级响应时间即可返回海量数据下的查询结果。

1.1 Doris的前世今生

Doris是一款为数据分析而生的数据库。从诞生之日起,Doris的每一次进步都是为了解决切实的OLAP业务痛点,每一次转变都是在应对不同的业务挑战。Doris的发展历程大致如图1-1所示。

图1-1 Doris的发展历程

1.1.1 Doris应需而生

在Doris诞生之前,百度和大多数互联网公司一样,使用MySQL的Sharding为OLAP报表业务提供支持。在早些年,百度的主要收入来源是广告,广告主需要通过报表查看广告的展现效果、点击量、收入等信息,并且根据不同维度分析制定后续广告的投放策略。随着百度本身流量的增加,广告流量也随之增加,MySQL的Sharding方案无法满足业务需求,主要痛点如下。

❑大规模数据导入会导致MySQL的读性能大幅降低,有时还会出现锁表现象,导致查询超时,尤其在频繁导入数据时,问题更为明显。

❑MySQL在数据量达千万级别时性能很差,只能从产品层面来限制用户的查询时间,抑制了用户需求,增加了很多后台取数的需求。

❑MySQL单表存储的数据有限,如果数据量过大,查询就会变慢。而且随着数据量的快速增长,Sharding方案维护成本飙升。

上述痛点也是目前大多数未引入Doris的企业所面临的,特别是在互联网行业,数据量大,且较少采用商业软件,主要以开源MySQL为核心构建报表查询系统,需要将在线分析处理结果进行多次聚合,才能满足报表的查询需求。

在2008年那个时间点,处理数据存储和计算的成熟开源产品很少,HBase的导入性能只有约2000条/s,不能满足业务快速增长需求。另外,业务还在不断增加,数据存储和数据分析的压力越来越大。于是,百度选择了自主研发之路,Doris由此诞生。

在Doris 1版本中,数据仍然通过用户ID进行哈希分布,将同一个用户ID的数据交由一台机器处理,这样大量的Join操作都可以在本地完成。Doris 1架构包含数据存储、元数据管理、数据导入和API网关4个部分,其中数据存储组件负责数据的存储和读写,元数据管理组件负责数据文件的目录管理和表信息管理,数据导入组件负责写入外部导入的数据,API网关负责接收、解析、规划查询请求。

相比于MySQL的Sharding方案,Doris主要在如下几个方面进行了改进。

❑Doris 1的数据模型将数据分为Key列、Value列。比如一条交易数据的Key列包括用户ID、时间、地域、来源等,Value列包括展现次数、点击次数、消费额等。在这样的数据模型下,所有Key列相同的行对应的Value列能够进行聚合,比如数据的时间维度最细粒度为小时,那么同一小时内多次导入的数据能够合并成一条记录,这样对于同样的查询来说,Doris 1需要扫描的数据条目相比MySQL会少很多。

❑Doris 1将MySQL逐条插入的方式改为批量更新,并且通过外围模块将同一批次数据进行排序以及预聚合。这样一个批次中相同Key列的数据能够被预先聚合,另外排序后的数据能起到聚集索引的作用,提升查询性能。

❑Doris 1提供了天表、月表这种类似物化视图的功能。比如用户想将数据按天进行汇聚展现,那么可以通过天表来实现。而相对于小时表,天表数据量会少很多,相应的查询性能也会提升几倍。

至此,Doris已经有了聚合模型、物化视图、批量读写3个基本特点了。Doris 2主要将Doris 1进行通用化改造,包括支持自定义表结构等,使Doris能够应用于其他产品,拓展了一些应用场景。

1.1.2 Doris架构重组

Doris变化和升级比较大的版本是Doris 3,这也是开发时间跨度比较长的一个版本,重大的改进主要有以下几点。

❑Doris 3在架构上引入了ZooKeeper来存储元数据,解耦组件,实现了系统无单点故障,提高了系统稳定性。在Doris 3的组件中,DT(Data Transfer)模块负责数据导入,DS(Data Searcher)模块负责数据查询,DM(Data Master)模块负责集群元数据管理。数据存储在分布式Key-Value引擎中。数据导入和数据查询时,Doris 3直接读取存储在ZooKeeper中的元数据,不再依赖元数据管理组件提供的服务。

❑Doris 3在数据分布方面引入了分区。数据会按照时间进行分区(比如天分区、月分区)。在同一个分区里,数据会根据用户ID进行分布。这样,同一个用户的数据会落在不同的分区,而在进行数据查询时,多台机器能同时处理一个用户的数据,实现了单用户数据分布式计算。

❑为了实现数据的多维分析,Doris 3采用了MySQL Storage Handler方案。通过这种方案,Doris 3伪装成一个MySQL的存储后端,类似于MyISAM、InnoDB。这样既能利用MySQL对SQL的支持,也能利用Doris 3对大量数据的快速处理;同时又引入了MySQL的BKA(Batched Key Access,批量索引访问)算法和MRR(Multi-Range Read,多范围读取优化)特性,尽量将计算下推给Doris 3来完成,从而减轻MySQL的计算压力,避免了单点故障。

❑Doris 3重构了存储引擎。在多维报表分析场景中,原来底层通用Key-Value系统的弊端也逐渐显露。由于Key-Value系统只能够读取完整的Key和完整的Value,而报表分析系统中的大部分查询并不需要读取所有列,这样会带来不必要的I/O开销。同时,Key-Value系统无法感知数据内容,只能使用通用压缩算法,导致数据压缩效率低,读写性能差。为了在底层存储引擎上有所突破,百度启动了OLAP Engine项目,参考Google Mesa系统重构了存储引擎。

新的存储引擎主要有以下特点。

❑引擎端原生就支持Schema,并且所有的列分为Key列、Value列,能够和上层的业务模型相对应,查询部分列时,无须加载全部列,减少不必要的I/O开销。

❑独特的数据模型。Value列支持聚合操作,包括SUM、MIN、MAX等。在Key列相同的情况下,Value列能够按照聚合操作类型完成对应的聚合操作。而引擎存储数据类似于LSM树,这样在后台执行聚合操作时,就能够将相同Key的Value字段按照对应的聚合操作类型进行聚合,无须在外部增加数据合并作业。

❑数据批量导入,批量生效。每个批次的导入都会生成一个增量文件,并且会有一个版本号。在查询任务中,只需要在最开始的时候确定读取哪个文件,这样就会只读取生效版本的数据,不会读取没有生效版本的数据,更不会浪费CPU资源进行版本号的过滤。

❑行列式存储。多行数据存储在一个块(Block)内,块内相同列的数据一同压缩与存放,这样可以根据数据特征,利用不同的压缩算法(比如对时间字段使用RLE算法等)来提高数据压缩效率。

另外,Doris 3在日常运维,包括表结构变更、集群扩容、集群缩容等方面,都做了针对性的设计,实现了自动化操作。

1.1.3 Doris引擎升级

在完成存储引擎的重构以后,Doris的I/O瓶颈得到突破。随着Doris性能的提升,Doris的查询和应用场景也变得越来越复杂,原本采用的MySQL查询引擎逐渐出现瓶颈。

幸运的是,2013年各种SQL on Hadoop项目正蓬勃发展。经过对比,百度研发团队选取Impala作为后续系统的分布式查询引擎。当时选择Impala的主要原因是C++性能较高,并且跟Doris后端开发语言一致,可以省去一部分数据序列化开销。

基于Impala改进的新产品被命名为Palo。Palo除了增加分布式查询层之外,还将Doris 3的OLAP作为单机存储引擎。由于没有分布式Key-Value系统,最初的Palo版本需要自己完成数据分片管理、副本管理等工作。后来为了增加灵活性,MySQL替换ZooKeeper实现元数据存储,整体架构如图1-2所示。

图1-2 Palo 1.x整体架构

从图1-2可以看出,Palo 1.x的组件非常多,运维难度非常大。于是,在随后的Palo 2版本中,元数据管理、分片管理和元数据副本管理合并到Frontend(Doris前端模块,以下简称为FE)组件,OLAP存储引擎和查询引擎合并到Backend(Doris后端模块,以下简称为BE)组件,减少了Agent组件和uWSGI服务组件。FE负责接收用户的查询请求,对查询进行规划,监督查询执行情况,并将查询结果返给用户。BE负责数据的存储,维护多副本数据,以及具体的查询执行。简化后的Doris架构如图1-3所示。

图1-3 Palo 2系统架构

经过Palo 2版本的改进,Doris的架构变得相当简洁,并且不再需要任何外部依赖。在此之后,虽然Doris经过几次改进,但是整体架构仍然保持Palo 2的架构。Palo 2在2015年左右开始大面积推广使用。

1.1.4 Doris拥抱开源

到2017年,Palo 2在百度内部几乎满足了所有的报表统计、多维分析需求,获得了非常高的评价。为了帮助更多的企业能更加高效、方便地完成类似业务,百度选择开源这款产品。Palo于2017年正式在GitHub上开源,并且在2018年接入Apache社区,名字改为Apache Doris,正式成为Apache基金会的孵化项目。

自开源以来,Doris以上手简单、性能卓越、运维高效等特点被越来越多的用户认可和使用。经过三年多的孵化,Doris开发者数量增长了20倍,在GitHub上的Star数量翻了6倍,被百度、美团、京东、小米、网易等头部企业广泛使用,覆盖互联网、金融、电商、教育、文娱等多个行业。鉴于Apache Doris各项指标良好,项目趋于成熟,2022年6月16日,Apache Doris顺利通过ASF项目管理委员会的评估,晋升为Apache基金会顶级项目,也成了Apache基金会的第200个顶级项目。

1.2 Doris的特点

Doris是一款基于MPP技术的分析型数据库,能够在海量数据场景下提供毫秒级查询服务。Doris脱胎于Apache Impala和Google Mesa系统,并进行了大量改造和优化,最终形成了今天大家看到的架构简洁、性能卓越、功能丰富、简单易用的OLAP数据库。

1.2.1 极简架构

从设计上来说,Doris融合了Google Mesa的数据存储模型、Apache的ORCFile存储格式、Apache Impala的查询引擎和MySQL交互协议,是一个拥有先进技术和先进架构的领先设计产品,如图1-4所示。

图1-4 Doris技术分解

在架构方面,Doris只有两类进程:一类是FE,可以理解为Doris的管理节点,主要负责用户请求的接入、查询计划的解析、元数据的存储和集群管理相关工作;另一类是BE,主要负责数据存储、查询计划执行。这两类进程都可以横向扩展。除此之外,Doris不依赖任何第三方系统(如HDFS、ZooKeeper等)。这种高度集成的架构极大地降低了运维成本。

FE节点包含Leader、Follower和Observer三种角色。默认一个集群只能有一个Leader,可以有多个Follower和Observer。其中,Leader和Follower组成一个Paxos选择组,如果Leader宕机,剩下的Follower会自动选出新的Leader,保证写入高可用。Observer同步Leader的数据,但是不参加选举。如果只部署一个FE,FE默认就是Leader。

FE节点主要包含存储管理模块、状态管理模块、协调模块、元数据模块和元数据缓存模块。存储管理模块负责管理所有的元数据信息,包括表信息、Tablet信息、Tablet的副本信息等,还负责管理用户的权限信息(即用户的认证信息和授权信息)和数据的导入任务等。状态管理模块负责管理所有BE进程的存活状态、查询负载等非持久化信息,并提供发布订阅接口。协调模块负责接收用户发来的请求,然后进行语句解析、生成执行规划,根据当前集群的状态,对执行规划进行调度。元数据模块负责对元数据的读写,只有Leader角色拥有此权限。元数据缓存模块负责同步元数据,以供语句解析、生成执行规划,主要是Follower和Observer角色拥有此权限。

BE节点可以无限扩展,并且所有BE节点的角色都是对等的。在集群足够大的情况下,部分BE节点下线不影响集群提供服务。BE节点主要由存储引擎和查询执行器组成。存储引擎负责管理节点本地的Tablet数据,发送或者接收数据并保存副本,定期合并、更新多个版本的数据,以减少存储占用。存储引擎还负责接收来自查询执行器的数据读取请求和批量数据导入请求。在MPP集群中执行查询时,会分解为一个树状的执行树,由Coordinator来协调执行,树的叶子节点也叫计划片断(PlanFragment),每个PlanFragment分配一个BE节点的查询执行器。

1.2.2 使用简单

Doris不仅架构简单,开发和使用也很简单。对于一款OLAP数据库来说,性能不是优劣评估的全部,易用性才是决定是否可持续使用的关键。Doris从设计之初就一直以易用性为出发点。

数据分析全周期一般包含数据建模、数据导入、用户上手分析、持续使用以及维护升级,无不体现Doris的易用性。

在数据建模方面,Doris支持Aggregate、Unique和Duplicate三种模型,可以满足OLAP领域的各种应用场景。同时,相对于MySQL,Doris只增加了一些分布式系统所具有的特性,比如分布键、分桶等。Doris建表语句如图1-5所示。

图1-5 Doris建表语句示意图

在数据导入方面,Doris提供多种数据导入方案(如图1-6所示),同时在数据导入过程中保证原子性。不论使用Broker Load进行批量导入,还是使用INSERT语句进行单条导入,都是一个完整的事务操作。导入事务可以保证一个批次内的数据原子性生效,不会出现部分数据写入的情况。

图1-6 Doris数据导入方案

同时,每一个导入作业都会生成一个Label,这个Label在数据库内用于唯一区分导入作业。Label用于保证对应的导入作业仅能成功导入一次。一个成功导入的Label再次调用时,会被拒绝并报错:Label already used。通过这个机制,数据消费侧可以实现At-Most-Once语义。如果结合上游系统的At-Least-Once语义,我们可以实现端到端数据导入的Exactly-Once语义。数据导入流程如图1-7所示。

图1-7 Doris数据导入流程

在SQL应用方面,Doris支持标准的SQL语言,在方言方面与MySQL兼容。不论简单的单表聚合、排序过滤,还是复杂的多表关联、子查询等,Doris都可以通过SQL语句轻松完成,极大地降低了用户的学习迁移和使用成本。高吞吐的即席查询和库内ETL也是Doris的强项。Doris还支持复杂的SQL语法,包括Grouping Set等高级语法功能,还支持通过UDF或UDAF自定义。在TB级别数据上,Doris可以代替部分Hive等离线系统功能,使得用户在一套数据库中满足所有需求。

在工具方面,Doris在FE节点实现了兼容MySQL协议,方便用户使用标准的MySQL客户端或各种语言的类库进行连接,对各种工具的支持都非常好。在数据库开发方面,Doris支持用户无缝使用DBeaver、DataGrip、Navicat等主流开发工具;在编程应用方面,Doris完全支持MySQL的JDBC和ODBC接口,支持C、Python、Java、Shell等开发语言;在BI应用方面,Doris支持帆软BI、观远BI、永洪BI、Tableau等各种敏捷BI软件;在ETL调度方面,Doris支持Kettle、DolphinScheduler等主流软件。

在集群可靠性方面,Doris使用“内存存储+检查点+镜像日志文件”模式,使用BTBJE(类似于Raft)协议实现元数据的高可用和高可靠。Doris内部自行管理数据的多副本并自动修复,保证数据的高可用、高可靠。在部分服务器宕机情况下,集群依然可以正常运行,数据也不会丢失。Doris部署无外部依赖,只需要部署BE和FE模块即可搭建集群。Doris支持在线更改表(加减列、创建Rollup),不会影响当前服务,不会阻塞读写等操作。

在集群扩缩容方面,Doris基于分布式管理框架,自动管理数据副本的分布、修复和均衡。比如对于副本损坏情况,Doris会自动感知并进行修复。而对于节点扩缩容,Doris会自动进行数据分片均衡,整个过程完全不影响其他服务,无须运维人员进行任何额外的操作。

在集群升级方面,Doris只需要替换二进制程序,滚动重启集群即可。在设计上,Doris完全向前兼容,支持通过灰度发布方式进行新版本的验证和测试。而Doris本身的一些失败重试和故障路由功能也极大地降低了集群升级过程中发生的错误对业务的影响。

1.2.3 功能丰富

Doris提供了非常丰富的功能来应对不同的业务场景。下面重点介绍一些Doris特色功能。

首先是分区分桶裁剪功能。Doris支持两个层次的数据划分:第一层是分区(Partition),支持Range和List的划分方式;第二层是分桶(Bucket),将数据通过Hash值进行水平划分,数据分片(Tablet)在集群中被均匀打散。Doris数据分布示例如图1-8所示。

图1-8 Doris数据分布示例

利用分桶裁剪功能,Doris可以将查询固定到极少数分片上,从而有效降低单个查询对系统资源的消耗,提升集群整体的并发查询能力。在高并发查询场景中,Doris单节点可以支持每秒上千的查询请求。

其次是合理的缓存功能。Doris支持SQL级别和Partition级别的查询缓存,如图1-9所示。其中,SQL级别的缓存以SQL语句的Hash值为Key,直接缓存SQL查询结果,非常适合更新频率不高,但是查询非常频繁的场景。而Partition级别的缓存会智能地将SQL查询结果中不同分区的数据进行存储,之后的查询中可以利用已缓存分区的数据及新分区实时查询数据得到最终结果,从而降低重复数据查询,减少对系统资源的消耗。

图1-9 高并发查询的数据缓存示意图

再次是支持Bitmap数据类型,利用位图来存储整型数据,并且可以通过位图进行一些集合类操作。Bitmap可以应用于高基数精确去重场景。传统的实时计算去重算法需要在内存中构建Hash表,在基数非常高的情况下会占用大量内存。而使用Bitmap可以将数值类型转换成位图上的0和1,从而极大地降低内存开销,并且对于去重,只需要将多个位图求交集后计算1的个数,从而在有限的内存下快速进行高基数精确去重计算。在用户画像场景中,通过位图的集合运算,我们可以快速获取不同标签组合的人群包。同时,Doris内置了很多Bitmap相关的函数,以计算留存率等,比如通过intersect_count()函数可以方便地计算用户的留存率。

最后是物化视图。物化视图也是Doris的核心特点之一。物化视图是将预先计算(根据定义好的SELECT语句)好的数据集存储在一个对用户透明且有真实数据的视图表格中。物化视图主要是为了满足用户对原始明细数据任意维度分析,快速对固定维度进行分析、查询,在统一视角下对明细、聚合数据进行分析的需求。在Doris中,用户可以使用明细数据模型存储明细数据,之后在明细数据上,选择任意维度和指标创建聚合物化视图,如SUM、MIN、MAX、COUNT等。Doris会保证明细表和物化视图中的数据完全一致。如果导入或删除物理表中的数据,Doris会自动更新,保证原始表和物化视图中的数据一致。同时,物化视图对用户是透明的。Doris会自动根据查询语句,匹配到最合适的物化视图进行查询。通过物化视图功能,Doris支持在一张表中统一明细数据模型和聚合模型,以加速某些固定模式的查询。

Doris还支持基于主键的数据更新。通过Unique模型,用户可以对数据基于主键进行更新。在实现层面,Doris采用Merge-on-Read方式提供更新后的数据。此外,用户还可以使用REPLACE_IF_NOT_NULL聚合方式,实现部分列更新。基于Unique模型,Doris还支持通过Marked Delete和Sequence Column等功能,实现对上游交易数据库数据同步更新,并且保证事务的原子性以及数据同步的顺序性。

1.2.4 开源开放

Doris还有一个重要的特点,即完全开源开放。Doris作为Apache基金会的项目,遵守Apache License 2.0。Apache License 2.0作为主流的开源协议,被OSI认定为“受欢迎且被广泛使用的许可证”。

有关Apache License 2.0的具体内容,读者可以在Apache官网查阅,简单来说,包括分发完全自由、允许项目代码被修改、允许作为开源或商业化软件再次发布,一旦授权永久有效,修改代码或衍生代码中需要带有原来代码的协议、专利声明等。这是对任何商业公司和用户都极其友好的协议。

1.3 Doris核心设计

对于一个分析型数据库,最核心的3个组成部分是存储引擎、查询引擎和查询优化器,这也是Doris具有优越性能的关键。除此之外,向量化执行引擎的加入让CPU能力得到更充分的发挥,进一步提升了Doris查询性能。

1.3.1 存储引擎

和大多数分析型数据库一样,Doris也是以列存格式存储数据的。数据以列进行连续存储,因为类型相同,因此压缩率极高,节省了磁盘空间。Doris对不同的数据类型还提供了不同的编码方式,如INT类型数据存储会使用BitShuffle编码方式,而字符串类型数据存储会使用字典编码方式。更进一步,Doris还会自动根据列的值的分布情况来切换编码类型,比如对于字符串类型,如果列中的重复值比较多,则不再使用字典编码,而直接切换为Plain Text编码,以避免不必要的空间浪费。

从文件格式看,Doris支持的文件格式和Parquet比较类似。一个数据版本会被分割成最大空间为256MB的Segment,每个Segment对应一个物理文件。Segment通常分为Header、Data Region、Index Region、Footer几个部分。Data Region用于按列存储数据,每一列又被分为多个Page,而Page是Doris的最小数据存储单元,如图1-10所示。

图1-10 Doris文件格式

Index Region负责存储数据的索引。Doris提供了丰富的索引来帮助加速数据的读取和过滤。索引类型大体可分为智能索引和二级索引两种。其中,智能索引是在数据写入时自动生成的,无须用户干预,包括前缀稀疏索引、Min Max索引等。而二级索引是用户可以选择性地在某些列上添加的辅助索引,需要自主选择是否创建,比如Bloom Filter索引、Bitmap索引等。

前缀稀疏索引是建立在排序列结构上的一种索引。存储在文件中的数据是按照排序列有序存储的。基于排序列数据,Doris会每1024行创建一个稀疏索引项,如图1-11所示。索引的Key即当前1024行中第一行的前缀排序列的值。当用户的查询条件包含这些排序列时,Doris可以通过前缀稀疏索引快速定位到起始行。

Min Max索引是建立在Segment和Page上的索引。对于Page级别,Doris都会记录每一列中的最大值和最小值。同样,对于Segment级别,Doris也会记录每一列中的最大值和最小值,如图1-11所示。这样当进行等值或范围查询时,Doris可以通过Min Max索引快速过滤掉不需要读取的行。

图1-11 Doris前缀稀疏索引和Min Max索引示例

当对某一列创建Bloom Filter索引后,Doris会在Page级别创建该列的Bloom Filter数据结构。Bloom Filter索引使用固定空间的位图来快速判断一个值是否存在,非常适合高基数列上的等值查询。

Bitmap索引的Key值是实际的列值,Value值是Key在数据文件中的偏移量。通过Bitmap索引,Doris可以快速定位到列值对应的行号,并且快速取数。该索引比较适合基数较低的列上的等值查询。

除了存储方式和索引结构,Doris在读取逻辑上也有很多优化,比如延迟物化功能会先根据有索引的列,定位一个查询范围,然后根据有过滤条件的列进一步过滤以缩小查询范围,最后读取其他需要读取的列。这种方式可以减少不必要的数据读取,降低查询对I/O的资源消耗。

1.3.2 查询引擎

Doris的查询引擎是基于MPP框架的火山模型,是从早期的Apache Impala演化而来的。Doris会基于SQL语句先生成一个逻辑执行计划,然后根据数据的分布,形成一个物理执行计划。物理执行计划涉及多个Fragment,Fragment之间的数据传输则是由Exchange模块完成的。通过Exchange模块,Doris在执行查询任务时就有了数据重分布(Reshuffle)能力,让查询不再局限于数据存储节点,从而更好地利用多节点资源并行进行数据处理。基于MPP框架的查询引擎执行流程示意图如图1-12所示。

图1-12 基于MPP框架的查询引擎执行流程示意图

逻辑执行计划的Agg阶段分为先重分布数据再汇总两个步骤,这个过程和Hadoop类似,都是按照相同的Key进行数据重分布。

除了通过并行设计来提高查询效率外,Doris还对很多具体的查询算子进行了优化,比如图1-13中的聚合算子。

图1-13 聚合算子

在Doris中,聚合算子被拆分成两级聚合:第一级聚合是在数据所在节点,以减少第二级聚合的数据;而第二级聚合将Key相同的数据汇聚到同一个节点,进行最终的聚合。

在此基础上,Doris还实现了自适应聚合。首先我们要知道,聚合算子是一种阻塞型算子,需要等全部数据处理完后,才会将数据发送给上层节点。而自适应聚合是在第一级聚合中,如果发现聚合效果很差,即使聚合后也无法有效减少需要传输的数据,则会自动停止第一级聚合,转换为非阻塞的流式算子,直接将读取的数据发送到上层节点,从而避免不必要的阻塞等待。

针对Join算子,Doris也进行了大量优化,其中Runtime Filter是一种很重要的优化方式。在两个表的Join操作中,我们通常将右表称为BuildTable,将左表称为ProbeTable。在实现上,通常首先读取右表中的数据,在内存中构建一个HashTable,然后开始读取左表中的每一行数据,并在HashTable中进行连接匹配,返回符合连接条件的数据。通常来说,左表的数据量会大于右表的数据量。

Runtime Filter的设计思路是在右表内存中构建HashTable的同时,为连接列生成一个过滤器,之后把这个过滤器推给左表。这样,左表就可以利用过滤器对数据进行过滤,从而减少Probe节点需要传输和比对的数据。这种过滤器被称为Runtime Filter。针对不同的数据,Doris设计了不同类型的过滤器,例如In Predicate、Bloom Filter和Min Max。用户可以根据不同场景选择不同的过滤器。Runtime Filter实现逻辑示意图如图1-14所示。

Runtime Filter适用于大部分Join场景,包括节点的自动穿透,可将过滤器下推到最底层的扫描节点,例如分布式Shuffle Join中,可先将多个节点产生的过滤器进行合并,再下推到数据读取节点。

图1-14 Runtime Filter实现逻辑示意图

1.3.3 查询优化器

除了查询执行层面的优化,Doris在查询优化器方面也做了大量改进。Doris中的查询优化器能够同时进行基于规则和基于代价的查询优化。在基于规则的查询优化方面,Doris完成了包括但不限于以下方面的改进。

1)常量折叠。常量折叠可以预先对常量表达式进行计算,计算后的结果有助于规划器进行分区分桶裁剪,以及执行层利用索引进行数据过滤等。例如将where event_dt>=cast(add_months(now(),-1)as date)折叠成where event_dt>=2022-02-20。

2)子查询改写。将子查询改写为Join操作,从而利用Doris在Join算子上做的一系列优化来提升查询效率,例如将select*from tb1 where col1 in(select col2 from tb2)a改写成select tb1.*from tb1 inner join tb2 on tb1.col1=tb2.col2。

3)提取公共表达式。提取公共表达式可以将SQL中的一些析取范式转换成合取范式,而合取范式通常对执行引擎是比较友好的,可以将查询条件重组或者下推,减少数据扫描和读取的行数,例如将条件where(a>1 and b=2) or (a>1 and b=3) or (a>1 and b=4)转化成where a>1 and b in (2,3,4),明显后者的判断速度比前者的快很多。

4)智能预过滤。智能预过滤可以将SQL中的析取范式转换成合取范式并提炼出公共条件,以便预先过滤部分数据,从而减少数据处理量。

5)谓词下推。Doris中的谓词下推不仅可以穿透查询层,还能进一步下推到存储层,利用索引进行数据过滤,如图1-15所示。

图1-15 Doris中的谓词下推示意图

而在基于代价的查询优化方面,Doris主要针对Join算子进行了大量优化。

Join Reorder可以通过一些表的统计信息,自动调整Join顺序。而Join顺序的调整可有效减小Join操作中生成的中间数据集的大小,从而加速查询的执行,如图1-16所示。

图1-16 Join Reorder优化示意图

Colocation Join可以利用数据的分布情况,将原本需要去重后才能进行关联的数据,在本地完成关联,从而避免去重时大量的数据传输,如图1-17所示。

图1-17 Colocation Join优化示意图

Bucket Join是Colocation Join的通用版本。Colocation Join需要用户在建表时就指定表的分布,以保证需要关联查询的若干表有相同的数据分布。而Bucket Join会更智能地判断SQL中关联条件和数据分布之间的关系,将原本需要同时去重左右两张表中数据的操作,变成将右表数据重分布到左表所在节点,从而减少数据的移动,如图1-18所示。

图1-18 Bucket Join优化示意图

1.3.4 向量化执行引擎

传统的数据库都是典型的迭代模型,执行计划中的每个算子通过调用下一个算子的next()方法来获取数据,从最底层的数据块中一条一条读取数据,最终返给用户。它的问题在于每个Tuple都要调用一次函数,开销太大,而且因为CPU每次只处理一条数据,无法利用CPU技术升级带来的新特性,比如SIMD。向量化模型每次处理的是一批数据,这些数据会被保存在一种叫作向量的数据结构里,由于每次处理的是一批数据,因此可以在每个Batch内做各种优化。简单地说,向量化执行引擎=高效的向量数据结构(Vector)+批量化处理模型(nextBatch)+Batch内性能优化(例如SIMD等)。

原本向量化执行引擎只是一个概念,是ClickHouse将其变成了现实,并展示出强大性能。通过向量化执行引擎原理的介绍,我们可以看出,向量化执行引擎非常适合基于列存储的OLAP数据库,可以极大地提高并行查询效率。在ClickHouse之后,OLAP数据库配套向量化执行引擎几乎已经成为标配。目前,除了Doris以外,openGuass、Polar-x、TDSQL实现了部分或所有向量化执行引擎功能。

Doris是在0.15版本中引入向量化执行引擎的,并在1.0版本中逐渐成熟。根据Doris的演进计划,向量化执行引擎会逐步替换当前的行式SQL执行引擎,以充分释放CPU的计算能力,达到更强大的查询性能。

在绝大多数场景中,用户只需要将Session变量enable_vectorized_engine设置为true,FE节点在进行查询规划时就会默认将SQL算子与SQL表达式转换为向量化的执行计划,从而提升SQL执行性能。

1.4 Doris应用场景

我们从图1-19中可以大致了解Doris在整个大数据处理流程中的定位。上游各类数据如事务型数据库中的埋点数据、日志等,通过一些离线系统、消息系统等处理后,再导入Doris进行存储,而Doris可以直接对外提供在线报表、多维分析结果等查询和展示服务。同时,Doris也可以作为数据源,被Spark、Flink等系统访问,以实现联邦查询或多源数据处理。Doris本身的分布式查询框架也可以为一些外部系统如MySQL、ElasticSearch等提供SQL查询服务。

图1-19 Doris在大数据处理流程中的定位

Doris在企业大数据处理体系中有多种应用场景。

1.实时大屏

实时大屏一般通过简单直观的结果展示最关键的指标,汇总的数据量比较大,并且要求查询低时延。实时大屏最难的是数据高频写入和实时查询,而Doris对接Kafka和Flink,可以实现实时数据查询,完美满足海量数据秒级查询需求。例如,百度统计为网站站长提供流量分析、网站分析、受众分析等多种分析服务,服务网站数量超过450万,每天查询量达到1500万,QPS(Queries Per Second,每秒查询率)峰值超过1400,每日新增数据量超过2TB,数据导入频次为5min,平均查询时延30ms。

2.固定报表

一般的报表类数据分析的查询模式比较固定,而且后台执行的SQL语句往往都是确定的。针对此类应用场景,以前的方案是使用MySQL数据库存储结果数据,用户需要将数据仓库中的数据高度汇总并写入MySQL。而Doris可以基于明细数据或者轻度汇总数据直接进行查询,可以大大减轻数据开发压力。Doris固定报表查询的时延一般在秒级以下,并且Doris通过MySQL交互协议支持各种报表工具,可以极大地提高报表开发效率。同时,Doris对多表关联的支持,也可以满足基于星型模型或星座模型的多维报表查询需求。

以百度为例,百度广告的用户后台管理系统支持一系列统计分析,例如流量分析、来源分析、访问分析、转化分析、访客分析、优化分析等,不仅包括区间统计,还包括各类筛选、同/环比、趋势分析,如图1-20所示。

图1-20 Doris固定报表案例

3.自助分析

自助分析也叫多维分析,是指IT人员根据业务需求预先加工好维度数据和事实数据,供业务人员按照自由组合维度进行数据分析的一种新型数据分析方式。相较于传统的固定报表,自助分析使用更为灵活,支持任意粒度的组合查询数据,以便查找异常数据和分析变动原因。

针对自助分析需求,我们一般基于轻度汇总数据或者明细数据构建星座模型,Doris支持丰富的Join操作和高效的Join查询,非常适合用于多维分析中的多表关联场景。Doris基于行列混合存储数据,针对多维分析场景只需要读取部分列进行计算,可以极大地减少磁盘的I/O操作。Doris支持丰富的索引来对数据进行预过滤,减少参与计算的数据量,也可以带来查询性能的大幅提升。在自助分析场景中,Doris是目前最契合用户需求的数据库产品。图1-21是百度分析基于Doris提供的自助分析案例截图。

图1-21 Doris自助分析案例

4.用户画像

用户画像在精准用户推荐、对客营销、运营分析等方面有着非常重要的作用,已经成为互联网企业的标配。Doris支持Bitmap去重,在用户圈选方面有巨大优势,可以更优雅、快速地筛选用户。Doris在用户画像构建方面提供了非常丰富的函数,支持对非Bitmap表和Bitmap表做交集、并集、补集运算。

5.多源联邦查询

Doris支持无缝对接多种数据来源,包括JDBC、Hive、对象存储S3/OSS和数据湖Iceberg Hudi等。大型企业需要构建统一的查询入口,实现实时数据和离线数据的联邦分析,满足数据分析人员多元化的查询需求。Doris已经成为这方面的最优选择之一。

通过Multi-Catalog功能,Doris提供了快速接入外部数据源进行访问的能力。用户可以通过CREATE CATALOG命令连接到外部数据源,Doris会自动映射外部数据源的库、表信息。之后,用户可以像访问普通表一样,对这些外部数据源中的数据进行访问,避免了对每张表手动建立外表映射的复杂操作。

同时,结合自身的高性能执行引擎和查询优化器,Doris实现了在数据湖上极速、易用的分析体验,较Presto、Trino有3~5倍的性能提升。

6.实时数据仓库

将Doris作为实时数据仓库的底座,是目前最常见的应用场景。业界主要利用Canal解析MySQL的binlog日志,利用Flume采集Web日志,最终写入Kafka并进行削峰填谷,提供稳定的流数据。Kafka数据可以直接通过Routine Load进入Doris,也可以经由Flink加工处理后写入Doris,然后通过视图或者物化视图进行数据处理,由前端应用直接查询实时数据。

这种方案的数据链路短、实时性更高,同时开发和运维成本低。

7.流批一体数据仓库

Doris不仅支持秒级查询,对库内ETL场景的支持也非常好。不少企业直接基于Doris构建企业级离线数据仓库。例如特步基于Doris打造了流批一体的数据仓库,既配有零售模型,又配有库存模型。系统架构如图1-22所示。

图1-22 特步基于Doris的流批一体数据仓库系统架构

Doris在架构中的工作任务如下。

❑基于DataX的全量或者增量数据加载入库;

❑基于Kafka的实时流式数据加载入库;

❑基于视图的数据增量和全量加工;

❑基于DELETE、INSERT INTO语句的批处理、微批处理;

❑针对不同业务场景实现差异化的实时性要求。

这样做的好处是:

❑开发简单,运维简单;

❑一套代码实现全量和增量逻辑,简化工作流;

❑在确保数据准确性的前提下满足数据实时性要求;

❑避免了数据跨库移动和多份存储,节省了硬件开销。

1.5 Doris的竞争对手

虽然Doris很优秀,但它也不是所向披靡的,毕竟开源比较晚。下面针对其他更早开源的竞品进行对比。

1.5.1 Doris的“前浪”——Greenplum

Greenplum数据库非常适合中小企业。Greenplum数据库于2015年开源,生态圈非常丰富,支持通过PXF读取Hadoop集群中的数据,支持通过GPSS组件连接Spark完成数据的快速读取和写入,支持通过GPText分析文本数据,支持集成开源的MADlib基础机器学习算法、支持集成PostGIS插件分析时空数据。Greenplum数据库具有非常丰富的ETL功能,例如支持GPLoad快速导入数据,支持丰富的函数和自定义函数,支持通过函数完成数据处理,支持行存储和列存储,支持宽表模型和星型模型。Greenplum数据库几乎完全兼容PostgreSQL数据库语法,不仅开发简单,入门门槛低,并且与外围的BI工具和ETL工具兼容得非常好。

正是因为Greenplum数据库对外开源,大量国产数据库在Greenplum的基础上进行优化后对外发布使用,因此Greenplum数据库还是MPP框架数据库领域的“带头大哥”。Greenplum安装非常简单、便捷,无任何外部依赖,可以单机部署,也可以多服务器集群部署。Greenplum具有比Oracle更领先的架构优势和性能优势,很适合单表百万级别到十亿级别的数据仓库和数据中台构建项目。

Greenplum的优点很多,但是相对于新兴的ClickHouse和Doris数据库,Greenplum在性能上有一定的劣势。首先,Greenplum默认还是行数据库,这导致不能完全发挥列存储优势,也没有实现向量化执行引擎。其次,Greenplum数据库只能选择所有节点平均分布数据或者所有节点都保留一份完整的数据副本,数据存储方式和ClickHouse比较类似,但是没有Doris灵活。最后,Greenplum查询特别依赖底层存储设备,在数据存储格式优化和索引方面不及ClickHouse和Doris数据库。基于以上原因,Greenplum数据库在千万级以上数据量的查询场景下,性能会逊色很多。

相比于Doris的高内存和多节点要求,Greenplum对内存要求非常低,并且单台服务器上可以部署多个节点。对于硬件资源投入比较少的企业,Greenplum仍是最好的选择。更多关于Greenplum构建数据仓库、数据中台的内容,读者可以阅读本人的上一部作品《高效使用Greenplum:入门、进阶和数据中台》。

1.5.2 Doris的“表哥”——Kylin

Kylin是一个开源的、分布式的分析型数据仓库,提供基于Hadoop、Spark的SQL查询接口及多维分析(OLAP)能力,以支持对超大规模数据的查询。Kylin架构如图1-23所示。

根据官方介绍,Kylin仅需三步即可实现对超大规模数据的亚秒级查询。

1)定义一个星型模型或雪花形模型的数据集。

2)在定义的数据集上构建Cube。

3)使用标准SQL语法通过ODBC、JDBC或RESTful接口查询Cube数据。

图1-23 Kylin架构

但是当ClickHouse横空出世后,Kylin这种架构劣势就显现出来了。诚如官方介绍,Kylin实现亚秒级查询需要三步,而Doris或者ClickHouse只需要一步——完成数据同步。而且,Kylin构建时间较长,为了提高命中率,还需要占用更多的存储空间,这些对于开发者来说都是不太友好的。

当然,Kylin作为一款和Hive大数据集群深度集成的查询引擎,功能成熟稳定,仍然有一定的应用市场。最新版的Kyligence已经支持通过微批处理方式集成准实时数据,实现近实时查询。

1.5.3 Doris的“知音”——ClickHouse

ClickHouse作为一款开源的列存储数据库管理系统,支持线性扩展,具有安装简单、高可靠性、高性能等特点。ClickHouse也正是以“快”闻名于世。在标准查询场景中,ClickHouse平均比Vertica快5倍,比Greenplum快10倍,比Hive快279倍,比MySQL快800倍,并且可处理的数据级别已达到10亿级别。

ClickHouse和Doris一样,诞生于搜索引擎公司,主要用于数据查询场景,但是ClickHouse关注点在用户点击流,而Doris关注点在OLAP业务。二者都支持单机部署和MPP框架的分布式部署。ClickHouse从底层存储到上层查询引擎都做了大量优化,在查询性能方面做到了极致,尤其是在首次实现向量化执行引擎后,成为很多后来者的“学习榜样”。

ClickHouse的优点很多,缺点也不少。首先,ClickHouse的集群模式不灵活,数据必须平均分布,并发支持度也不太高。其次,ClickHouse的表引擎太多,但是大部分表引擎缺陷太明显,不能满足实际应用场景。最后,ClickHouse的语法不太友好,无论建表语句,还是删除数据,在具体项目实战中都会出现很多问题。

1.5.4 Doris的“伤痕”——StarRocks

谈到Doris,不得不谈鼎石科技的StarRocks(早期叫作DorisDB)。鼎石科技成立之初,我就关注到了这家公司。从鼎石科技公众号发布《给ClickHouse用户的一封信》开始,Doris和StarRocks的裂痕就逐渐公开了。根据我简单的了解,StarRocks在开源Doris的基础上进行了一系列优化,率先完成了向量化执行引擎的开发,成为一款商业化的数据库产品,并对外提供社区版。

作为一个Oracle、DB2、Hana和Greenplum数据库的资深用户,我对数据库厂商提供商业化服务举双手赞成。StarRocks选用Elastic License 2.0开源协议,更大的可能是为了在云原生时代求得一席生存之地。希望StarRocks在后期的发展中可以利用自己的优势反哺Doris开源社区,一起做大、做强Doris生态。 xCgVPi8pi2fMNv3fFoWAZP6ZvdLtKiLiHeDbU04t34+X7gc+sgKjsq0Ar9yUQfPu

点击中间区域
呼出菜单
上一章
目录
下一章
×