一、Doris演进史
Apache Doris —— 为分析而生
Doris发展历程: Doris发展比较重要的关键节点与事件
#2008 - Doris1 :「筑巢引凤」的重要基石
早年,百度最主要的收入来源是广告。广告主需要通过报表服务来查看广告的展现、点击、消费等信息,并且能够需要通过不同维度来获得广告的消费情况,用以指导后续的广告的投放策略。
在Doris1诞生之前,百度使用 MySQL Sharding 方式来为广告主提供广告报表支持。随着百度本身流量的增加,广告流量也随之增加,已有的 MySQL Sharding 方案变得不再能够满足业务的需求。主要体现在以下几个方面:
- 第一,大规模数据导入会导致 MySQL 的读性能大幅降低,甚至还有锁表情况,在密集导入数据的情况下尤为明显。同时在数据导入时,MySQL的查询性能大幅下降,导致页面打开很缓慢或者超时,用户体验很差;
- 第二,MySQL在大查询方面性能很差,因此只能从产品层面来限制用户的查询时间范围,用户体验很差;
- 第三,MySQL对数据量的支持是有限的。单表存储的数据有限,如果过大,查询就会变慢。对此的解决方案只有拆表、拆库、迁移数据。随着数据量的快速增长,已经无法维护。
当时数据存储和计算成熟的开源产品很少,Hbase的导入性能只有大约2000条/秒,不能满足业务每小时新增的要求。而业务还在不断增长,来自业务的压力越来越大。在这种情况下,Doris1诞生了,并且在2008年10月份跟随百度凤巢系统一起正式上线。
Doris1的主要架构如上图所示。数据仍然通过用户 ID 进行 Hash,将同一个用户ID的数据交由一台机器处理。其中:
- Hm-Storage负责数据的存储。
- ODP、OMG负责将业务数据导入到Hm-Storage中。
- AS负责解析、规划查询请求,并将查询请求发给Hm-Storage处理,并对Hm-Storage返回的数据进行一些业务相关的计算后将查询结果返回给用户。
相比于MySQL的方案,Doris1主要在如下几个方面进行了改进
- 首先,Doris1的数据模型将数据分为Key列,Value列。比如一条数据的Key列包括:用户ID、时间、地域、来源等等,value列包括:展现次数、点击次数、消费额等。这样的数据模型下,所有Key列相同的数据Value列能够进行聚合,比如数据的时间维度最细粒度为小时,那同一小时多次导入的数据是能够被合并成一条的。这样对于同样的查询来说,Doris1需要扫描的数据条目相比MySQL就会降低很多。
- 其次,Doris1将 MySQL 逐条插入改成了批量更新,并且在通过外围模块将同一批次数据进行排序以及预聚合。这样一个批次中相同Key的数据能够被预先聚合,另外排序后的数据能够在查询的时候起到聚集索引的作用,提升查询时候的性能.
- 最后,Doris1提供了天表、月表这种类似物化视图的功能。比如用户是想将数据按天进行汇聚展现,那么对于这种查询是可以通过天表来满足的。而天表相对于小时表数据量会小几倍,响应的查询性能也会提升几倍。
通过Doris1的工作,完全解决了MySQL Sharding遇到的问题。并于2008年10月于凤巢系统一起上线,完美的支撑了广告统计报表需求。
#2009 - Doris2:解「百度统计」燃眉之急
2008年的百度统计服务大约有50-60台MySQL,但是业务每天有3000万+条增量数据,由于MySQL的存储和查询性能无法满足需求,对存量数据的支撑已经到了极限,问题频出,万般无奈之下百度统计甚至关闭了新增用户的功能,以减少数据量的增加。
Doris1由于当时时间紧、任务重,所以设计、实现的时候只为了能够满足凤巢的业务需求,并没有兼顾其他的应用需求。由于Doris1方案对于凤巢成功的支持,百度统计同学开始基于Doris1打造Doris2系统,主要将Doris1进行通用化改造,包括支持自定义schema等,使Doris能够应用于其他产品。此外还进行一些优化以此来提升系统的查询、存储性能。
2009年Doris2研发完成后上线百度统计,并且成功支撑百度统计后续的快速增长,成功的助力百度统计成为当时国内规模最大,性能、功能最强的统计平台。由于在凤巢、百度统计上的成功,公司内部后续其他类似统计报表类的需求也都由Doris2进行支持,比如网盟、联盟等报表服务。
#2010 - Doris3: 让查询再快一点
百度在2009-2011年发展迅猛,营收每年近100%的速度增长,与之相伴的是广告数据量也随之大幅增长。随着业务数据量的不断增长,Doris2系统的问题也逐渐成为业务发展的瓶颈。
- 首先体现在Doris2无法满足业务的查询性能需求,主要是对于长时间跨度的查询请求、以及大客户的查询请求。这是因为Doris2通过规则将全部数据按照用户ID进行Sharding,这虽然能够将全部数据分散到多台机器上,但是对于单一用户的数据还是全部落在一台机器上。随着单用户数据量增多,一些查询请求无法快速计算得到结果。
- 其次,Doris2在日常运维方面基本上都需要停服后手动操作,比如 Schema Change、集群扩缩容等,一方面用户体验很差,一方面还会增加集群运维的成本。
- 最后,Doris2本身并不是高可用系统,机器故障等问题还是会影响服务的稳定性,并且需要人肉进行复杂的操作来恢复服务。
为了解决Doris2的问题,团队开始了Doris3的设计研发。Doris3的主要架构如下图所示,其中:
- DT(Data Transfer)负责数据导入
- DS(Data Seacher)模块负责数据查询
- DM(Data Master)模块负责集群元数据管理,数据则存储在Armor分布式Key-Value引擎中。
Doris3依赖ZooKeeper存储元数据,从而其他模块依赖ZooKeeper做到了无状态,进而整个系统能够做到无故障单点。
在数据分布方面Doris3引入了分区的概念。首先数据会按照时间进行分区(比如天分区、月分区);在同一个分区里,数据会根据用户ID再进行Sharding。这样同一个用户的数据会落在不同的分区上,而在查询时多台机器就能够同时处理一个用户的数据了,实现了单用户的分布式计算能力。但是可能还会存在一个分区内部单个用户数据量过大的情况。对于这种情况Doris3设计了后续表功能,会将单个分区内大用户的数据进行拆分,导入到多个分片中,这样能够保证每个分片内单个用户的数据总量最高是有限度的。
另外Doris3在日常运维Schema Change,以及扩容、缩容等方面都做了针对性设计,使其能够自动化进行,不依赖线上人工操作。
在当时,由于种种原因,Doris3最终确定使用了Armor来作为底层存储系统。Armor是一款分布式Key-Value系统,支持多副本强一致,且单表内全Key有序。选用Armor作为底层存储能够使Doris3只负责管理分片,而分片的副本,以及副本的一致性都由Armor来处理。并且,集群的扩、缩容等操作也只需要Armor感知即可,Doris3本身并不需要感知。当然除了这些好处外,这样的选型也有一些弊端。
由于Armor是一个通用的Key-Value系统,并不感知上层的业务数据,它并不支持Doris这种数据模型,既相同Key的数据,Value字段是可以进行聚合的。比如数据导入的批次是五分钟一批,但是数据时间粒度是小时,那么其实一个小时的数据可能是多次导入的,但是逻辑上是可以合并成一条数据的。所以为了实现这个功能,只能是Doris3自身实现了较为复杂的数据合并策略来完成相关数据的合并。
Doris3在2011年完成开发后逐渐替换Doris2所制成的业务,并且成功的解决了大客户查询的问题。而公司内部后续的新需求,也都由Doris3来承担支持。
#2012 - MySQL + Doris3: 百度的第一个OLAP平台
2012年随着Doris3逐步迁移Doris2的同时,大数据时代悄然到来。在公司内部,随着百度业务的发展,各个业务端需要更加灵活的方式来分析已有的数据。而此时的Doris3仍然只支持单表的统计分析查询,还不能够满足业务进行多维分析的需求。由于缺少通用的SQL支持,Doris3在面对更加灵活的多维分析场景时有点力不从心。当时,公司内只有Hive以及类似系统支持大数据量的SQL查询,但是他们均是面向解决离线分析场景,而在线多维分析领域缺少一款产品来满足业务方的需求。
所以,为了能够支持业务的多维分析需求,Doris3采用了 MySQL Storage Handler 的方式来进行扩展。通过此种方式,将 Doris3 伪装成一个MySQL的存储后端,类似于MyISAM、InnoDB一样。这样既能够利用上MySQL对于SQL的支持,也能利用上Doris3对于大数据量的支持。由于这里MySQL是计算单点,为了减轻MySQL的计算压力,Doris3应用了MySQL的BKA(Batched Key Access)以及MRR(Multi-Range Read)等机制尽量的将计算下推到Doris3来完成,从而减轻MySQL的计算压力。
通过 MySQL + Doris3 这个方案,百度Insight团队为PS、LBS、WISE等产品线提供了百度内部第一个OLAP分析服务平台。
#2012 - OLAP Engine:突破底层存储束缚
另一方面Doris3支持报表分析场景时,底层通用 Key-Value 存储引擎的弊端也逐渐显露。作为一个通用 Key-Value 存储引擎,在支持报表引擎方面暴露了一些问题。
- 第一,由于Key-Value系统读取只能够读取全Key,全Value,而报表分析系统中的大部分查询并不需要读取所有列,这样会带来不必要的IO开销;
- 第二,正如前文所说,由于引擎本身不感知业务模型,不能够再进行Merge的同时完成数据的合并,这需要Doris3借助复杂的作业管理在引擎外部完成Merge工作既不简洁,也不高效;
- 第三,为了保证业务的导入原子性,Doris3为每批次导入都赋值一个版本号,并记录在每条数据Key的最后部分。这样在查询的时候,需要对每条数据进行Key的解析,比较版本号,过滤掉不需要的版本。这样一方面需要读取无需读取的数据,一方面需要解析所有Key,从而带来不必要的CPU开销;
- 第四,Key-Value系统无法感知数据内容,只能使用通用压缩算法,进而导致数据的压缩效率不高。这样在查询、读取时都会带来较多的IO负载。
为了能够在底层存储引擎上有所突破,OLAP Engine项目启动了。这个项目的发起者是当时从Google来的高T,为百度带来了当时业界最领先的底层报表引擎技术。OLAP Engine最大的特点包括以下几点。
- 第一,引擎端原生就支持Schema,并且所有的列分为Key列,Value列。这样就能够跟上层的业务模型能够对应上,查询部分列时,无需加载全部列,减少不必要的IO开销。
- 第二,独特的数据模型。Value列支持聚合操作,包括SUM、MIN、MAX等。在Key列相同的情况下,Value列就能够按照聚合操作类型完成对应的聚合操作。而引擎本身导入方式类似于LSM Tree,这样在引擎后台进行Merge的同时,就能够将相同Key的数据中的Value字段按照对应的操作进行聚合。这样就无需外部再进行数据合并作业管理,将引擎层与业务层合并合二为一,省去不必要的IO、CPU开销。
- 第三,数据批量导入,原子生效。对于每个批次的导入,都会有个 Delta 文件对应,并且会有个版本号。在查询的时候只是在初始化的时候来确定读取哪个文件,这样就只会读取生效版本的数据,而不会读取没有生效版本的数据,更不会浪费CPU来进行版本号比较过滤。
- 第四,行列式存储。多行(比如1024行)数据存储在一个Block内,Block内相同列的数据一同压缩存放,这样可以根据数据特征利用不同的压缩算法(比如对于时间字段使用RLE等)大幅提高数据压缩效率。
即使分布式层没有采用复杂的分布式管理,只是使用类似 Doris2 的用户ID Sharding方式,OLAP Engine后续也成功的支持了凤巢,网盟等广告业务。这充分的能够体现OLAP Engine强大的报表分析能力。虽然OLAP Engine取得了成功,但是由于硬Sharding方案带来的不易运维、不易扩展等问题仍然存在。
#2013 - 用PALO:玩转OLAP
底层技术的发展会激发上层业务的需求,而上层业务的需求同时会为底层的技术带来新的挑战。随着第一款OLAP产品的问世,数据分析师们的建模就更加复杂,有时查询SQL会有上千行,人为阅读已经相当吃力。而MySQL + Doris3方案的弊端也就越发突显。因为分析SQL越来越复杂,大量的计算都需要在MySQL中完成,这样MySQL的计算能力就成为整个系统的性能瓶颈,突破这个性能瓶颈也就变得极为紧迫。
因此Doris亟需一款拥有分布式计算能力的查询引擎。幸运的是当时(2013年)各种SQL on Hadoop项目也正蓬勃发展,比如Impala,Tajo,Presto等等。在有限的时间内并不充分调研的情况下,团队选取了Impala作为了后续系统的分布式查询引擎。当时的选择Impala主要的原因是因为其性能较高,并且BE的C++语言跟我们已有系统的语言一致,未来可以省去一部分序列化开销。
由于MySQL + Doris3的方案制约了业务的使用,当时公司的另一个团队邀请了Oracle的Exadata进行POC,这给了Doris团队很大的压力。如果Doris想继续在OLAP领域继续发展,就需要快速的产出原型,并且性能上还要胜出Exadata。为了快速的验证方案的可行性,团队几个月内就把Impala与Doris3进行了集成,并用TPC-H进行了测试,结果是Impala + Doris3性能比Exadata更好。这次原型的成功为我们赢得了一次机会,能够让团队继续改造Doris3从而更好的支持OLAP场景。
新产品的名字命名为PALO,意为玩转OLAP。
PALO1除了增加分布式查询层之外,因为OLAP Engine在统计报表领域的成功,PALO1放弃了Doris3依赖的通用Key-Value系统,选择了OLAP Engine作为自己的单机引擎。因为没有了分布式Key-Value系统,那么PALO1自己完成数据分片管理、副本管理等工作。
PALO1的架构如下所示。
其中DM负责管理元数据、数据的分布、分片副本管理等内容,DM本身没有状态,元数据内容都存储在MySQL中。FE负责接收用户的查询请求,并且进行查询规划解析。BE是负责存储数据,以及进行具体的查询执行。
随着PALO1的正式上线,除了迁移所有Doris3已有的的业务外,也成功支持了当时百度内部大部分的OLAP分析场景。
#2015 - PALO 2:让架构再简单一点
如果说PALO1是为了解决性能问题,那么PALO2主要是为了在架构上进行优化。由于PALO1模块数目较多,并且外部依赖MySQL,这其实还是增加了运维的压力的。所以我们在PALO2项目中力求将系统的架构进行简化。经过简化后的系统架构如下图所示。
PALO2中我们只存在2种模块:FE、BE。
- FE一方面负责管理、存储元数据,另一方面FE还负责与用户交互,接受用户查询,对查询规划,监督查询执行,并将查询结果返回给用户。
- FE本身是有状态的,但是它内部通过BDB JE,能够将元数据进行多副本复制,从而能够保证服务的高可用。
- BE与PALO1功能一致,只是PALO2的BE包含了存储引擎,一方面减少了一个模块,并且在用户查询的时候少了一次数据的序列化、反序列化操作,节约CPU消耗。
通过PALO2的工作,系统架构本身变得相当简洁,并且不需要任何依赖。因为PALO2架构的简洁,我们后续也相对容易的基于PALO2提供了公有云服务以及私有化部署;另一方面,当PALO开源之后其他用户也能够用通过较低的门槛来搭建使用PALO 。在此之后PALO虽然经过几次改进,但是整体架构仍然保持PALO2的架构。
#2017 and Future:Apache Doris (incubating) ,是更广阔的世界
PALO2在百度内部基本服务了所有的统计报表、多维分析需求,我们相信它一定可以应用到其他公司,能够帮助更多的人更加高效、方便的支持类似的业务需求。因此,我们选择了开源,PALO于2017年正式在GitHub上开源,并且在2018年贡献给Apache社区,并将名字改为Apache Doris(incubating)进行正式孵化。贡献给Apache之后,Doris就不仅仅是百度的项目,而成为了Apache的项目。
随着开源,Doris已经在京东、美团、搜狐、小米等公司的生产环境中正式使用,也有越来越多的Contributor加入到Doris大家庭中。一路走来,Doris从未惧怕过挑战,也从未被困难击倒。时至今日,Doris已经站在了更高的舞台上,准备拥抱更多的机遇与挑战。
参考资料
一、Doris演进史的更多相关文章
- EMAS,一部淘宝十年移动互联网技术的演进史
导读 本文根据2018云栖大会深圳峰会·EMAS专场—移动互联的进化论,阿里巴巴高级技术专家泠茗< EMAS全景介绍>的演讲整理而成,文中就EMAS的起源史及EMAS的五大移动研发场景解决 ...
- 全局视角看技术-Java多线程演进史
作者:京东科技 文涛 全文较长共6468字,语言通俗易懂,是一篇具有大纲性质的关于多线程的梳理,作者从历史演进的角度讲了多线程相关知识体系,让你知其然知其所以然. 前言 2022年09月22日,JDK ...
- 苏宁易购Android架构演进史
互联网后端架构 https://mp.weixin.qq.com/s/5lDXjMh6ghQNi4E7qQIEEg 互联网后端架构 10月9日 摘要 移动青铜时代(2012-2014) 时代特点: 移 ...
- 《京东上千页面搭建基石——CMS前后端分离演进史》阅读笔记
一.背景 CMS即内容管理系统,目的是用于快速进行网站建设或者网页开发. 对于京东网站部门来说,CMS核心目的是用来快速开发和上线各种页面,诸如各种垂直频道页. 二.CMS核心目的 进行数据和模板的统 ...
- 转:微信Android客户端架构演进之路
转自: http://www.infoq.com/cn/articles/wechat-android-app-architecture 微信Android客户端架构演进之路 作者 赵原 发布于 20 ...
- Intel 处理器架构演进 转
Intel 处理器架构演进 Posted on 2018-02-13 | Edited on 2018-09-23 | In Computer Architecture | Comments: ...
- Web前端小白入门指迷
前注:这篇文章首发于我自己创办的服务于校园的技术分享 [西邮 Upper -- 004]Web前端小白入门指迷,写得很用心也就发在这里. 大前端之旅 大前端有很多种,Shell 前端,客户端前端,Ap ...
- 用Maven打包成EAR远程部署JBoss(二)——部署到远程JBoss
用Maven打包成EAR远程部署JBoss(一)讲了如何使用Maven打包,可是在文章的最后也留下了一个问题,那就是如何将包部署到远程的JBoss中呢?近期在对之前的学习进行总结,发现少了这样一篇重要 ...
- 深度学习实践系列(1)- 从零搭建notMNIST逻辑回归模型
MNIST 被喻为深度学习中的Hello World示例,由Yann LeCun等大神组织收集的一个手写数字的数据集,有60000个训练集和10000个验证集,是个非常适合初学者入门的训练集.这个网站 ...
- 百度APP移动端网络深度优化实践分享(二):网络连接优化篇
本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<二>连接优化>,感谢原作者的无私分享. 一.前言 在<百度APP移动端网 ...
随机推荐
- vivado的非嵌入ILA的使用
vivado非嵌入ILA的使用 1.实验原理 前面在vivado中联合vitis设计时接触过ila,那个时候采用的方法是直接调用IP核在原理图中连接.这个方法简单直接,可以将自己所需的测量信号转移到I ...
- linux 查看磁盘空间命令
一. df -h 显示当前磁盘挂载(包含剩余空间)情况 二. cd 到大容量的路径下使用命令 [root@hadoop102 /]# du -sh *|sort -n 三. 循环这个过程
- HashSet 存对象的时候是如何判断是不是同一个对象,其中含有重写equals方法和hashcode方法 后续
一开始,set集合里面只存进一个对象, 存第二个对象时候,由于重写了hashcode方法,只要价格都是10就返回1,所以hashcode一样的话,再去进行equals方法判断,此时这个也重写了,thi ...
- 21 JSONP
JSONP 为了解决浏览器跨域问题. jquery提供了jsonp请求. 在网页端如果见到了服务器返回的数据是: xxxxxxxxxxdjsfkldasjfkldasjklfjadsklfjasd ...
- 4 JavaScript数组和对象
4 数组和对象 在JS中创建数组非常简单. 直接[ ]即可. 也可以用正规军的new Array(). 不过效果都是一样的. var as = [11,22,33,44,55]; var bs = n ...
- #线段树合并、树上启发式合并#CF600E Lomsat gelral
题目 一棵树有\(n\)个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和 分析1 线段树合并,记录\(w,sum\)分别表示编号和以及颜色和,当颜色和相同时两个编号 ...
- 小师妹学JavaIO之:try with和它的底层原理
目录 简介 IO关闭的问题 使用try with resource try with resource的原理 自定义resource 总结 简介 小师妹是个java初学者,最近正在学习使用java I ...
- [IOI2000]邮局 题解
简要题意 线段上有 \(V\) 个村庄,现在要建 \(P\) 个邮局,使每个村庄到最近的邮局的距离之和最小. 50分做法 设\(dp[i][j]\) 表示第一个村庄到第 \(i\) 个村庄,建了 \( ...
- std::string 拼接字符串
#include <iostream> #include <string> #include <sstream> int main() { // 方法一:12345 ...
- MogDB/openGauss访问控制简介
MogDB/openGauss 访问控制简介 SQL 可以针对不同的数据库对象赋予不同的权限,这样就可以限制用户对数据的不必要访问,提高数据访问的安全性.常见的 SQL 权限如下: SELECT/UP ...