分享嘉宾:陈明雨 Apache Doris PMC 成员
编辑整理:刘闰丰 酷开科技
出品平台:DataFunTalk
导读:本次分享的主题是Apache Doris极速1.0版本的解析与未来的规划。
今天的分享主要围绕下面几方面内容展开:
-
Apach Doris特性一览
-
Apache Doris1.0版本解析
-
Apache Doris未来规划
-
Apache Doris开源社区
首先介绍下Apache Doris,下图为Apache Doris官网上的一张图,可以看出Doris在整个大数据设计中的地位。
从上游TP系统数据、业务埋点数据、web端日志等通过批处理/流处理系统,经过加工之后存储到doris中,doris可以直接对外提供查询服务,包括实时大屏、多维报表、流量统计、自助查询、用户画像等。
同时doris作为带有存储的数据服务,可以通过doris提供的connector(spark, flink等)读取doris中数据和其他大数据系统中的数据进行联邦查询,来避免数据孤岛的情况。
并且,doris自身也有完备的MPP查询层,我们可以通过外表的方式来对其他的数据源进行数据分析,比如可以通过ODBC协议来连接支持所有ODBC的数据库mysql, oracle, sql Server等,同时也对ES进行了深度的集成,为ES提供了一个分布式的sql查询层,可以解决ES在sql查询方面的不足,也可以利用ES在全文检索、索引上的一种高效的处理性能。
这就是目前Doris在整个大数据处理中的定位。
接下来从doris的功能出发,介绍一下为什么选择doris以及doris有那些特性。
01
Apache Doris 特性一览
这里我们将对doris的6个功能逐一进行介绍。
1. 极简架构
极简架构,是我们在设计之初为用户提供的一个优势和便利。从上图可以看出Doris的架构是非常简单的。
整个架构主要有两类角色:FE(Frontend)和BE(Backend)。其中,FE主要负责元数据的存储,用户请求的接入,以及查询计划的解析。BE则主要负责查询计划的执行,数据的存储。
除了这两个进程之外,Doris不再依赖任何第三方服务。也就是说我们的部署和运维相对而言都是比较简单的。同时在上层也提供了mysql协议的兼容。支持标准的sql语法方便用户零成本地接入到系统中来。
2.高效自运维
上图可以看到,用户的数据可以通过用户建表时定义的分区分桶来进行数据切片,最终用户的数据是以分片的形式存储在整个分布式集群中的各个节点中,并且节点之间的分片可以自动均衡和修复,比如当前有3个BE节点,当我们增加第四个BE节点的时候,系统会自动按分片粒度将数据均匀分布在新增服务器上来保证多个服务器的负载是接近的和相同的,同时在出现某一个数据的副本损坏的时候,doris内部也会在短时间内将副本补齐到一个健康状态来保证副本的可用性和可靠性。整个过程不需要人工介入,同时整个过程也不会影响到用户的查询,导入等功能。
所以在容灾和自运维方面相对便利。
3. 高并发场景支持
我们知道一个olap领域的数据库更偏向于高吞吐、复杂场景下的快速处理,doris除了这种复杂的AD-hoc查询,同时也能提供高并发的点查,在单机情况下可以支持1000QPS的请求支持,并且是可横向扩展的。主要得益于2个方面:
①分区裁剪
我们可以通过查询优化器,以及数据的分区分桶的形式,将查询条件定位到具体分区及分桶中,这样一个查询所占用的资源将足够小,因为最终只是定位到某一台机器的某一个片上,这样整个集群支持高并发的请求就可以变多。
②数据Cache
在doris中分为不同种类的数据Cache,比如最基础的sql Cache,相同的sql查询可以直接从cache中获取计算好的查询结果。第二种是类似于partition cache的方式,智能的根据查询条件去cache中获取历史分区数据的结果,这也是doris在高并发查询场景下的一些特色。
4. MPP执行引擎
Doris中是有一个完备的MPP执行引擎的。从上图可以看到,我们的查询优化器对一个sql生成树状的逻辑计划以后,根据数据分布及优化规则拆分为物理执行计划。首先,一个物理执行计划可以在集群中多个BE节点同时执行,同时在一个BE内部我们还会把查询计划的分片进一步拆分成多个instance,以保证在单个节点内部再一次进行并行,达到单机多核CPU的特性。
同时我们也引用到了Exchange节点,该节点使得MPP的完备性得到了提升,保证数据通过shuffle的方式重新分布在更多的节点上,而不仅仅只局限于数据存储所在的计算资源。
5.明细与聚合数据
doris本身是支持预聚合的语义的。如果同学们使用过kylin、druid就可以知道它们会将数据预先计算好。在doris中,用户可以将明细的数据直接存储在base表,doris支持在明细表上选择任意维度列和指标列生成物化视图,也就是上图中的上卷表,并且doris可以保证生成的所有的物化视图和明细表中的数据是强一致的,也就是说通过导入的事务性保证夺标之前数据一致性,这样用户也不需要担心基础表数据更新,上卷表还没更新导致数据查询的不一致性,这在doris中是不存在的。
对于用户来说,所有查询都是针对base表而言,查询优化器会智能地分析查询模式自动匹配到对应的物化视图上来进行数据的返回,一方面通过数据一致性,其次是查询自动路由,来保证在一个系统中进行明细数据和聚合数据的统一处理和分析,也降低了运维压力,比如是否感知物化视图、表更新等问题。
6. 便携数据接入
从图中可以看出doris支持了非常多的数据导入方式,用户数据不管是在对象存储、kafka存储,还是流批处理系统中,我们都可以通过一个sql命令将数据灌入倒doris中,比如我们支持kafka不丢不重进行一键订阅,同时也提供了flink/spark connector可以接入更丰富的数据源写入到doris中,也可以通过broker load进行大批量的一次性的数据导入,也可以通过stream load进行近似流式的导入。
以上通过简单的6点介绍,概括了doris的一些特性,接下来我们进入今天的重点,对doris在前段时间刚刚通过的1.0版本的解析。
02
Apache Doris1.0版本解析
1.0版本也是Doris进入孵化器以来第一个1位大版本。在该版本中,主要提供三大特点:极速、稳定、多源。
1. 极速:向量化引擎
第一点就是极速,其本质就是我们在1.0版本中引进了全新的向量化引擎。向量化引擎在业界是有很多应用的,包括前几年比较火的clickhouse系统,是把整个向量化技术带到了工业生产领域,让大家在实际生产环境中去应用向量化技术来得到收益,所以我们在设计向量化引擎中也借鉴了非常多的clickhouse的优秀设计,来保证我们的查询性能得到质的提升。向量化引擎我们从5部分来为大家进行介绍。
①列式内存布局
可以看到在原来的行存计算模型中,数据在内存中的表现方式是行的表现方式,在整个向量化执行引擎中,把它改成了列的表现方式,后面我们会讲到它的优势。
②向量化计算框架
同时在列式的表现方式中,我们还改进了向量化计算框架,可以看到当我们计算a, b两列中b列的abs函数时会在原有的block基础上,增加一个结果列,将b列的填充结果增加到结果列中,然后再进行b列的结果删除。
③Cache亲和度
列式的布局和向量化计算框架,提供了以下好处,第一个是cache亲和度,在行存计算模式中,当我们计算某一列时,需要将整行加载到内存中,并且通过offset和偏移去找到对应的列,然后再将那一列的值取出进行计算,这样可以看出,当我们加载整行数据时,其实只用到了某一列,这对cache亲和度是不好的,也就是一个cache行中有效占比比较低,而在列式布局中,是一次性加载这一列,这样cache亲和度有明显提升。我们可以在一个cache行中去缓存更多有效数据,如果熟悉系统的同学我们应该知道不同的cache级别访存的延迟会有指数级的提升,所以通过cache亲和度提升了系统的计算效率。
④虚函数调用
在行存储中,计算某一列值需要进行大量的switch case或者虚函数的调用,我们需要在动态运行时去判断该列的类型,根据不同的类型去调用具体实函数的实现。所以虚函数是需要在运行时才能知道类型的,编译器无法在编译期间静态获取一些信息进行优化,还有就是虚函数无法做内联,那么编译器就会在编译期间丢失很多有用信息无法做进一步优化,我们在做虚函数调用的时候还会涉及很多查表的功能,查虚函数表会严重打破多级流水,整个CPU的分支命中率和预测率都会下降,从而影响执行效率。
所以在向量化引擎中,我们引用了大量的静态模板,使大量虚函数的调用变成了在编译期间静态的类型转换,这样可以保证我们的优化器在编译期间直接编译出最优代码,其次可以在运行期间减少分支错误的预测等问题,使效率得到进一步提升。
⑤SIMD指令
SIMD指令集分为两个部分,第一部分是C++的编译器,可以自动的实现一些SIMD指令集的优化,比如在上图例子中:for循环设计两个数组的计算,该两个数组在内存中是连续存储的,第二没有两个变量之间的数值依赖,其次for计算相对简单,那么编译器则会自动的做一些多指令集的优化,可以看到在自动做了SIMD指令集优化后可以得到3倍的一个性能提升。我们不需要显式地调用SIMD指令集,而是通过改变一些编程习惯等让编译器自动帮我们做性能的提升。
第二点是手动的SIMD指令集。在一些稍微复杂的函数下,编译器没有足够的智能帮我们做指令集优化的时候,我们可以手动的调用SIMD指令来进行优化,比如上图,两个字符串比较函数中,可以通过手写SIMD指令集的方式来做改进。
以上就是对向量化引擎简单得介绍,如果大家感兴趣可以去下图中的链接进行进一步的学习。
在最后 ,我们看一下使用向量化引擎的效果,其实在1.0版本中我们的向量化引擎还处于一个实验阶段,它是一个完备的功能集,但还有很多优化点在持续的改进,在当前版本中,首先我们可以看到在SSD的Benchmark上,也就是在多表关联的查询场景下,大部分查询都有着一倍以上的性能提升。
如果在单表查询场景下,比如日志分析场景,或者大宽表分析场景下,可以看到我们向量化引擎的提升是非常明显的,可以达到10倍的性能提升。所以在生产环境上,平均的观感体验可以达到3-5倍的提升。当然这并不是我们最终的数据,我们也会在后续的1.1和1.2版本中进行持续向量化优化,在后续版本中会带来更多的向量化提升。这就是1.0版本在极速方面做的工作。
2. 稳定:内存可控
doris本身是一个MPP架构的执行引擎,整个执行过程是完全依赖于内存的,无论导入还是查询,数据的cache都是占用内存开销的,所以它本质是对内存使用比较高的一个系统,在之前版本使用过的同学可能会发现在一些高负载场景下经常会出现Out Of Memory的错误导致进程挂掉继而重启,所以内存控制在之前版本是做的不好的。
在1.0版本我们把它作为专项进行了改造,我们的目标是对OOM Say No!通过一系列技术方案来保证整个进程是在内存控制范围内的,避免出现OOM的现象。
我们通过mem Tracker树形结构来充分控制整个系统各个模块,各个任务之间内存的调用关系和控制,比如在进程级别会有一个总的Process Tracker,在Process Tracker下会挂分模块的Tracker,例如针对查询的Query Tracker,针对导入的Load Tracker,任务的Task Tracker和 Cache Tracker。如图中的Query Trackers,我们还会针对每一个查询上的计划分片,或者说执行分片上进一步增加细粒度的tracker,比如可以增加聚合算子的内存占用,扫描算子的内存占用,这样可以精细化到每个查询中的算子使用,保证查询内存可用。
其次每个mem tracker是挂在总的process tracker下的,所以所有的查询也会受到总的内存的控制,具体实现细节实际是在把每个mem tracker在线程执行的时候挂载到Thread Local 的变量上,这样一个线程内所有系统申请都会通过Thread Local进行捕获,同时我们调用TcMallocHook的方式来去把所有new, delete这些申请给hook起来,不会遗失任何一个实际内存使用,这样带来的好处:
①可观测局部内存占用。在新的版本中可以精确的定位到内存具体是被查询,还是导入还是被cache占用,通过可观测性来快速定位问题。
②严格监控实际内存申请,杜绝内存超限。通过hook方式严格将内存进行限制,并且不会有超限的问题。
3. 多源:大数据生态
第三部分是大数据生态,在1.0版本我们也开放了对hive外表,iceberg外表的的查询功能,本质是想解决用户引用doris的时候,但是数据已经在其他存储系统中存放,这样在之前版本中如果想使用外部数据源,则需要导入doris才可以使用,数据迁移成本高。
① 解决数据不迁移过程中能够加速原有系统的查询加速能力。
② 逐步形成完善的湖仓一体生态。在1.0版本中我们已经支持了hive 和 iceberg的数据源,那么针对iceberg也支持自动同步hive外表,hive表不需要一张张做映射建立,而只需要提供hive metedata地址则可以实现自动同步,我们在后续版本也会逐步完善doris的生态。
这就是doris在1.0大版本中整个的更新,整个1.0版本相对于0.15版本社区有116位贡献者,贡献了大概663个commit,除了上述3点介绍的功能之外,还有非常多的功能和性能提升,欢迎大家前往doris官网探索。
03
Apache Doris 未来规划
接下来简单介绍下Apache Doris在未来的规划。
1. 湖仓一体
虽然目前已经引入了hive 和 iceberg 的功能支持,但是相对来说功能较简单,在接下来的工作中,会引入Multi-Catalog机制,该机制类似于presto这种sql加速系统,可以将多源的数据统一到catalog抽象,可以保证更方便接入外部数据源。现在也是在联系社区同学开发hudi的支持。同时也会陆续引入merge on read的支持,目前可以理解为都是在读取copy on read的表,通过引入merge on read可以读取到数据湖实时更新的数据。
2. 存算分离
存算分离本质上引用的是越来越多的上云的需求。我们可以通过低价的S3的存储,达到低成本的收益,其次根据云上的弹性扩容提供无状态的计算节点的横向扩容,通过低成本+弹性来满足存算分离的需求。我们也可以根据共享数据来保证多个业务共享,但是通过独立的计算资源来计算。
3. 实时写入
这点来源的需求是我们看到在日志场景或者是lot场景等场景,多个数据源进行并发的向同一个系统写入,doris现在还是一个近似微批的系统,需要去限制批次时间,这样用户体验就会相对较差,在后续会尝试支持高并发实时写入场景。还有在面临一些订单场景,我们可以UNIQUE key主键模型来近似达到数据更新的效果。unique key是一个merge on read 的模型,写入速度较快,但是查询效率会差一些,后续我们会支持copy on write 的数据更新,来吸收一定的写的吞吐来提高查询的能力。
4. 稳定性与可观测性
我们会继续提升系统稳定性,在高负载情况下保证系统平稳运行。同时提高系统可观测性,包括引入tracing来快速的定位系统问题,这就是doris至少在今年需要做的一些事情。
04
Apache Doris 开源社区
最后介绍一下我们的开源社区现状,这是在4月份的一个截图, 目前contributors人数已经突破300,月活跃贡献者人数已达70人,目前毕业已经在推进了,到最后阶段,如果一切顺利在下个月则可以完成一系列的毕业流程,希望可以给用户提供一个很好的体验。
我们希望我们的社区不光是冷冰冰的代码,也是数据库爱好者、OLAP爱好者、开发者共同的社区,这是github网址和2022年的Roadmap地址,感兴趣可以关注我们的Apache Doris微信公众号,帮助大家更好地去了解和使用doris。
今天的分享就到这里,谢谢大家。
在文末分享、点赞、在看,给个3连击呗~
01/分享嘉宾
陈明雨
Apache Doris PMC 成员
陈明雨,Apache Doris PMC 成员。前百度资深研发工程师。8年分布式系统研发经验,一直专注于分布式可扩展分析型数据库领域。
本文转载自 陈明雨 DataFunTalk,原文链接:https://mp.weixin.qq.com/s/s8aYQbidIcp347maTEadNg。