Apache Doris是一个开源的MPP(Massively Parallel Processing,大规模并行处理)架构的分析型数据库,以高性能、易用性、亚秒级查询响应著称,近年来在国内越来越火,百度、美团、小米、京东、字节跳动等很多大厂都在用,社区也非常活跃。
我最近在研究Doris的源码和架构,因为公司在做数据平台的技术选型,需要深入了解各个OLAP数据库的原理和优缺点。我读了Doris的官方文档、设计文档、部分源码,也做了一些性能测试,对它的底层机制有了比较深入的理解。
本文从整体架构、存储引擎、查询引擎、写入流程、查询优化、高可用、和其他OLAP数据库的对比等方面,深入剖析Doris的底层原理,聊聊它为什么能这么快,它的设计有什么巧妙之处,以及它的局限性在哪里。希望能给想了解Doris的朋友一些参考,也欢迎大家交流指正。
先说明一下:本文基于Doris 0.15版本(2021年的版本),后续版本可能有架构变化和功能增强,具体以最新版本为准。我不是Doris的核心开发者,只是一个研究者和使用者,理解可能有偏差,甚至有错误,仅供参考。
一、Doris是什么,解决什么问题
在深入原理之前,先简单介绍一下Doris是什么,它解决什么问题。
Doris最初是百度在2017年开源的一个分析型数据库,后来贡献给Apache基金会,成为Apache的顶级项目。它的定位是一个MPP架构的、支持实时分析的OLAP(Online Analytical Processing,联机分析处理)数据库,主要用于解决大数据量下的多维分析、实时报表、即席查询等场景。
简单来说,Doris解决的问题是:当你的数据量很大(几十亿到几百亿行),查询维度很多,要求查询响应快(亚秒级到秒级),同时还要支持实时数据写入和查询,传统的数据库(MySQL、PostgreSQL)扛不住,Hive、Spark SQL又太慢,这时候就需要Doris这样的OLAP数据库。
Doris的主要特点:
- MPP架构:数据分布式存储,查询并行执行,横向扩展能力强。
- 列式存储:按列存储数据,查询时只读取需要的列,IO效率高,压缩比高。
- 亚秒级查询:通过各种优化(预聚合、索引、向量化执行、CBO优化器等),大部分查询能在亚秒级返回结果。
- 实时写入:支持实时数据写入,写入后立即可查,延迟在秒级。
- 兼容MySQL协议:使用MySQL协议,用户可以用MySQL客户端、BI工具直接连接,学习成本低。
- 易运维:架构简单,只有两类进程(FE和BE),不依赖Hadoop、Spark等其他组件,部署和运维简单。
这些特点,让Doris在很多场景下都非常好用,尤其是企业级的数据分析、报表、看板等场景。下面我们就深入看看,Doris是怎么实现这些特点的。
二、整体架构:FE和BE两类进程
Doris的架构非常简洁,只有两类进程:FE(Frontend) 和 BE(Backend)。
- FE(Frontend):前端节点,负责接收用户请求、解析SQL、查询规划、元数据管理、调度等。FE是无状态的,可以水平扩展,通过一致性协议(BDB-JE)选主,保证元数据的一致性。
- BE(Backend):后端节点,负责数据存储、查询执行、数据写入等。BE是有状态的,存储实际的数据,数据在多个BE之间分布式存储,通过副本机制保证高可用。
就这么两类进程,没有其他依赖,部署非常简单,这也是Doris易用性的一个重要体现。对比一下,ClickHouse需要自己处理分布式表和副本,配置比较复杂;StarRocks(Doris的一个分支,后来独立发展)的架构和Doris类似,也是FE+BE。
下面分别看看FE和BE的职责。
FE的主要职责:
- 接收用户请求:FE兼容MySQL协议,用户用MySQL客户端连接FE,发送SQL语句。
- SQL解析和分析:FE解析SQL,进行语法分析、语义分析,生成逻辑执行计划。
- 查询优化:FE的CBO(Cost-Based Optimizer,基于代价的优化器)对逻辑执行计划进行优化,选择最优的执行计划,比如join顺序、join算法、是否使用预聚合表等。
- 查询调度:FE把优化后的执行计划拆分成多个Fragment,调度到对应的BE上执行,并协调各个BE之间的数据流转。
- 元数据管理:FE管理数据库、表、分区、副本等元数据,元数据存储在FE的内存中,通过BDB-JE持久化和同步。
- 数据导入调度:FE负责数据导入任务的调度,把导入任务分配给对应的BE。
FE是无状态的,可以部署多个,做负载均衡。多个FE之间通过BDB-JE(一个嵌入式的高可用key-value存储)同步元数据,选一个主FE,其他FE是从FE,主FE负责写操作,从FE负责读操作,主FE挂了会自动选新的主。
BE的主要职责:
- 数据存储:BE存储实际的数据,数据按表、分区、分桶(Tablet)分布式存储在多个BE上,每个Tablet有多个副本(默认3副本),分布在不同的BE上。
- 查询执行:BE接收FE调度的查询任务,执行具体的计算(扫描、过滤、聚合、join等),并把中间结果返回给FE或其他BE。
- 数据写入:BE接收数据写入请求,把数据写入内存中的MemTable,达到阈值后flush到磁盘,生成数据文件。
- 数据Compaction:BE后台定期做Compaction,把多个小的数据文件合并成大的,删除已标记删除的数据,优化查询性能。
- 副本管理:BE负责副本的创建、同步、修复,保证数据的一致性和高可用。
BE是有状态的,存储实际的数据,所以BE的扩展需要考虑数据的均衡。Doris支持在线扩容,新增BE后,会自动把部分数据迁移到新BE上,保持数据均衡。
整个架构的数据流是这样的:
- 写入:用户 → FE(接收、调度) → BE(写入、存储)
- 查询:用户 → FE(解析、优化、调度) → BE(并行执行、返回结果) → FE(汇总结果) → 用户
这个架构简洁高效,FE负责"大脑"的工作(解析、优化、调度),BE负责"体力"的工作(存储、计算),职责清晰,易于扩展和运维。
三、存储引擎:列式存储、分区、分桶、副本
Doris的存储引擎是它高性能的基础之一。Doris采用列式存储,支持分区和分桶,数据多副本存储,下面我们详细看看。
1. 数据模型:行存 vs 列存
Doris默认采用列式存储(也支持行存,但主要用列存)。列式存储是什么意思呢?就是把表的数据按列来存储,同一列的数据存在一起,而不是像行存那样把一行的所有列存在一起。
比如有一张用户表,有id、name、age、city四列:
- 行存:id1,name1,age1,city1, id2,name2,age2,city2, ...
- 列存:id1,id2,id3,... | name1,name2,name3,... | age1,age2,age3,... | city1,city2,city3,...
列式存储的好处:
- 查询效率高:分析查询通常只需要少数几列,列式存储只读取需要的列,不需要读取整行,IO量小。比如查询"SELECT city, COUNT(*) FROM user GROUP BY city",只需要读取city列,不需要读取id、name、age列,IO量减少75%。
- 压缩比高:同一列的数据类型相同,数据相似度高,压缩效果好。比如city列,很多重复的值,用字典编码或RLE编码,压缩比能达到5-10倍,甚至更高。压缩比高,存储成本低,而且查询时读取的数据量更小,更快。
- 向量化执行友好:列式存储的数据是连续的同类型数据,非常适合向量化执行(SIMD指令),一次处理一批数据,计算效率高。
当然,列式存储也有缺点,比如点查(根据主键查一行)效率不如行存,因为需要从多个列文件中拼接一行数据。但Doris的定位是OLAP分析,主要是批量查询和聚合,列式存储的优势远大于劣势。
2. 分区(Partition)
Doris支持分区,把一张大表按某个列(通常是时间列)分成多个分区,每个分区独立存储。比如按天分区,每天的数据一个分区。
分区的好处:
- 查询裁剪:查询时如果带分区列的过滤条件(比如WHERE dt = '2021-10-01'),只需要扫描对应的分区,不需要扫描全表,大大减少数据扫描量。
- 数据管理方便:可以按分区删除数据(比如删除30天前的分区),不需要逐行删除,效率高。也可以按分区导入数据,导入失败只影响当前分区。
- 冷热数据分离:可以把热数据(最近的)存在SSD,冷数据(历史的)存在HDD,降低存储成本。
Doris支持范围分区(Range Partition)和列表分区(List Partition),最常用的是按时间的范围分区。
3. 分桶(Bucket / Tablet)
分区之后,每个分区还可以进一步分桶,按某个列(通常是高基数字列,比如用户ID)哈希分成多个桶(Tablet),每个桶是数据分布和副本的基本单位。
比如一个分区有1000万行数据,分成10个桶,每个桶100万行,每个桶有3个副本,分布在不同的BE上。
分桶的好处:
- 并行查询:查询时,多个桶可以在多个BE上并行扫描和计算,提高查询并发度。
- 数据均衡:数据按哈希均匀分布在多个桶和多个BE上,避免数据倾斜。
- 高可用:每个桶有多个副本,分布在不同的BE上,某个BE挂了,其他BE上的副本还能提供服务。
分桶列的选择很重要,一般选择高基数、查询经常用做过滤或join的列,这样数据分布均匀,查询效率高。如果分桶列基数太低(比如性别,只有男和女两个值),会导致数据倾斜,某些桶数据量特别大,影响查询性能。
4. 副本(Replica)
Doris采用多副本机制保证高可用,默认每个Tablet有3个副本,分布在不同的BE上(也可以配置2副本或1副本,根据数据重要性和成本考虑)。
副本之间通过Paxos协议(Multi-Paxos)保证一致性,一个副本是主副本(Leader),其他是从副本(Follower)。写入数据时,先写主副本,主副本同步到从副本,多数副本写成功后,返回写入成功。查询时,可以读主副本,也可以读从副本(默认读主,也可以配置读从,提高读并发)。
多副本的好处:
- 高可用:某个BE挂了,其他BE上的副本还能提供服务,数据不丢失,服务不中断。
- 读扩展:可以读从副本,提高读并发能力。
- 数据安全:多副本存储,避免单点故障导致数据丢失。
当然,多副本也意味着存储成本翻倍(3副本就是3倍存储),但对于分析型数据库来说,数据安全和高可用更重要,存储成本相对可控(列式存储压缩比高,实际存储成本没有那么高)。
5. 数据文件结构:Segment、Column、Page
Doris的每个Tablet的数据,存储在磁盘上的多个Segment文件中。每个Segment文件包含多个列的数据,每列的数据又分成多个Page(数据页),Page是IO和压缩的基本单位。
具体结构:
- Tablet:一个分桶的数据,包含多个Segment文件。
- Segment:一次Compaction生成的数据文件,包含所有列的数据。
- Column:Segment中的一列数据,包含多个Page。
- Page:数据页,是IO和压缩的基本单位,每个Page存储一批数据,压缩后存储。
- Index:每列有索引(Zone Map索引、前缀索引等),用于快速定位数据。
这种分层的文件结构,既能高效存储大量数据,又能通过索引和分页快速查询需要的数据,是Doris高性能的基础之一。
四、写入流程:MemTable、Flush、Compaction
Doris支持实时写入,写入后立即可查,延迟在秒级。它的写入流程是怎样的呢?下面我们详细看看。
1. 写入方式
Doris支持多种写入方式:
- Stream Load:HTTP流式写入,适合实时数据导入,单条或小批量写入,延迟低。
- Broker Load:通过Broker从HDFS、S3等外部存储批量导入数据,适合大批量离线导入。
- Routine Load:例行导入,持续从Kafka消费数据导入,适合实时数据流。
- Insert Into:SQL方式插入,兼容MySQL的INSERT语法,适合小批量测试或简单导入。
- Spark Load / Flink Connector:通过Spark或Flink批量导入,适合大数据量的离线导入。
不同的写入方式适用于不同的场景,但底层的写入流程是类似的,都是把数据写入BE的内存,然后flush到磁盘。
2. 写入流程详解
以Stream Load为例,写入流程大致如下:
- 用户发送HTTP请求:用户把数据通过HTTP POST发送给FE(或BE),FE接收请求,做鉴权、解析、调度。
- FE调度写入:FE根据表的分区和分桶信息,计算每条数据应该写入哪个Tablet,然后把写入任务调度给对应Tablet的主副本所在的BE。
- BE写入MemTable:BE接收数据,把数据写入内存中的MemTable(内存表)。MemTable是一个内存中的数据结构,按主键排序(如果是主键模型),支持写入和查询。
- MemTable Flush:当MemTable的大小达到阈值(默认100MB),或者写入时间达到阈值,MemTable会被flush到磁盘,生成一个Segment文件。flush是异步的,不阻塞写入。
- 数据立即可查:数据写入MemTable后,就可以被查询到了(查询时同时扫描MemTable和磁盘上的Segment文件),所以写入后立即可查,延迟很低。
- Compaction:随着写入的进行,会生成很多小的Segment文件,BE后台会定期做Compaction,把多个小Segment合并成大Segment,同时删除已标记删除的数据,优化查询性能。
这个写入流程的设计,既保证了写入的低延迟(写入内存就返回),又保证了查询的实时性(内存数据立即可查),还通过Compaction保证了查询性能(合并小文件,减少文件数量)。
3. Compaction机制
Compaction是Doris写入流程中非常重要的一个环节,也是影响性能的关键因素。因为写入是小批量、持续的,会生成很多小的Segment文件,如果不合并,查询时需要扫描很多小文件,效率很低,而且文件太多也会影响文件系统的性能。
Doris的Compaction分为两种:
- Cumulative Compaction(累积Compaction):把多个小的Segment文件合并成一个中等大小的Segment,减少文件数量。这个过程比较频繁,合并的是最近写入的小文件。
- Base Compaction(基础Compaction):把累积Compaction生成的中等Segment和基础的大Segment合并,生成一个更大的基础Segment,同时删除已标记删除的数据(在更新和删除场景下,数据是标记删除的,Base Compaction时才真正删除)。这个过程比较耗时,频率较低。
Compaction是后台异步执行的,不影响前台的写入和查询,但会消耗CPU和IO资源。如果写入量很大,Compaction速度跟不上写入速度,就会导致小文件堆积,查询性能下降,这就是所谓的"Compaction压力"。
Doris有一些机制来缓解Compaction压力,比如动态调整Compaction的并发度和资源、限制写入速度、自动合并小文件等,但在超高写入量的场景下,Compaction仍然可能成为瓶颈,需要合理配置和优化。
4. 数据模型:明细、聚合、主键
Doris支持三种数据模型,不同的模型适用于不同的场景,写入和查询的行为也不一样:
- 明细模型(Duplicate Key Model):默认模型,数据是明细的,允许重复,不做预聚合。适用于原始数据存储、明细查询等场景。写入时直接追加,查询时扫描所有数据。
- 聚合模型(Aggregate Key Model):按维度列聚合,指标列按聚合函数(SUM、COUNT、MAX、MIN等)预聚合。适用于报表、看板等聚合查询场景。写入时,相同维度的数据会在MemTable和Compaction时聚合,查询时直接读预聚合的数据,速度快,但写入时需要聚合,开销大一些。
- 主键模型(Unique Key Model):按主键去重,相同主键的数据,新数据覆盖旧数据。适用于需要更新的场景,比如用户画像、订单状态更新等。写入时,相同主键的数据在Compaction时合并(旧数据标记删除,保留新数据),查询时只返回最新的数据。
不同的数据模型,写入流程和Compaction策略略有不同,但整体架构是一样的。选择合适的数据模型,对性能影响很大,需要根据业务场景选择。
五、查询引擎:MPP、向量化、CBO优化器
Doris的查询引擎是它高性能的另一个关键。Doris采用MPP架构,支持向量化执行,有CBO优化器,下面我们详细看看。
1. MPP执行模型
MPP(Massively Parallel Processing,大规模并行处理)是Doris查询执行的基本模型。简单来说,就是把一个查询拆分成多个子任务,分发到多个BE上并行执行,每个BE处理一部分数据,最后汇总结果。
Doris的查询执行流程:
- FE接收SQL,解析、优化,生成分布式执行计划。
- FE把执行计划拆分成多个Fragment(执行片段),每个Fragment负责一部分计算。
- FE把Fragment调度到对应的BE上执行,BE之间通过数据流式传输(Exchange)交换中间结果。
- 各个BE并行执行,最后把结果汇总到FE(或一个BE),返回给用户。
比如一个简单的聚合查询"SELECT city, COUNT(*) FROM user GROUP BY city":
- 假设有10个桶,分布在5个BE上。
- FE调度5个BE,每个BE扫描2个桶的数据,做局部聚合(每个BE内部按city分组计数)。
- 每个BE把局部聚合结果发送给一个汇总BE(或FE),汇总BE做最终聚合(把不同BE的相同city的count加起来)。
- 汇总结果返回给用户。
这样,原本需要一个节点处理1000万行数据,现在5个节点并行处理,每个节点处理200万行,查询速度理论上能提升5倍(实际受网络、聚合开销等影响,可能达不到线性扩展,但提升还是很明显的)。
MPP架构的好处是横向扩展能力强,数据量增大时,增加BE节点就能提升查询性能。而且MPP适合复杂的分析查询(多表join、多层聚合、子查询等),能充分利用集群的计算资源。
2. 向量化执行(Vectorized Execution)
向量化执行是Doris 0.15版本引入的重要特性(之前是火山模型,逐行处理),也是Doris性能提升的关键。
传统的火山模型(Volcano Model)是逐行处理的,每个算子一次处理一行数据,调用next()方法获取下一行。这种模型的优点是灵活,容易实现各种算子,但缺点是函数调用开销大,CPU缓存命中率低,无法利用SIMD指令,性能不高。
向量化执行是批量处理的,每个算子一次处理一批数据(比如1024行),数据按列存储在连续的内存中(Column),算子对整批数据做计算。这种模型的优点:
- 函数调用开销小:一次处理一批数据,函数调用次数大大减少。
- CPU缓存命中率高:同列数据连续存储,缓存友好。
- 支持SIMD指令:同类型数据连续存储,可以用SIMD指令(SSE、AVX等)一次处理多个数据,计算效率成倍提升。
- 分支预测友好:批量处理,分支预测更准确。
Doris的向量化执行是基于列式数据结构的,每个算子接收和输出的都是列式的批量数据(Block,包含多个Column),算子内部对整批数据做向量化计算。比如过滤算子,对整批数据的某一列做比较,生成一个过滤掩码,然后根据掩码过滤数据;聚合算子,对整批数据做分组聚合,用哈希表批量处理。
向量化执行的性能提升非常明显,尤其是在计算密集型的查询(聚合、join、表达式计算等)中,性能能提升几倍甚至十几倍。Doris 0.15版本默认开启向量化执行,也是这个版本性能大幅提升的主要原因。
3. CBO优化器
CBO(Cost-Based Optimizer,基于代价的优化器)是Doris查询优化的核心。CBO的作用是,对于一个SQL查询,生成多个可能的执行计划,估算每个计划的代价(CPU、IO、网络等),选择代价最低的执行计划。
Doris的CBO优化器是基于Apache Calcite开发的(后来做了很多定制和优化),支持:
- 逻辑优化:谓词下推、列裁剪、子查询改写、常量折叠、外连接转内连接等。
- 物理优化:join顺序选择(多表join时,选择最优的join顺序)、join算法选择(Hash Join、Nested Loop Join、Broadcast Join等)、聚合方式选择(本地聚合+全局聚合、两阶段聚合等)、是否使用预聚合表(物化视图)等。
- 代价估算:基于表的统计信息(行数、列的基数、数据分布、NULL值比例等),估算每个执行计划的代价,选择最优的。
CBO优化器对于复杂查询(多表join、子查询、复杂聚合)的性能影响很大,一个好的执行计划和一个差的执行计划,性能可能相差几十倍甚至上百倍。Doris的CBO优化器在不断完善,支持的优化规则越来越多,代价估算也越来越准确。
当然,CBO优化器的效果依赖于统计信息的准确性,如果统计信息不准确(比如数据更新后没有重新收集统计信息),优化器可能选错执行计划,导致查询性能下降。所以Doris支持自动和手动收集统计信息,保证统计信息的准确性。
4. 索引机制
Doris有多种索引,用于加速查询:
- 前缀索引(Prefix Index):Doris的表是按排序键排序存储的,排序键的前36个字节会建立前缀索引,查询时如果过滤条件包含排序键的前缀,可以用前缀索引快速定位数据,类似MySQL的聚簇索引。
- Zone Map索引:每列的每个Page(数据页)都有Zone Map索引,记录这个Page中该列的最大值、最小值、NULL值数量等。查询时,如果过滤条件和Zone Map不匹配(比如过滤条件是age > 30,但某个Page的max(age) = 25),可以直接跳过这个Page,不需要读取,大大减少IO量。
- Bloom Filter索引:可以给高基数的列建Bloom Filter索引,用于快速判断某个值是否存在,适合等值查询(比如WHERE id = 123)。Bloom Filter是一种概率数据结构,能快速判断"值一定不存在"或"可能存在",空间效率很高。
- Bitmap索引:可以给低基数的列建Bitmap索引,适合多条件组合查询(比如WHERE city = '北京' AND age > 30 AND gender = '男'),多个Bitmap可以快速做与或运算,定位符合条件的行。
- 倒排索引:新版本的Doris支持倒排索引,用于文本搜索和多条件组合查询,类似搜索引擎的倒排索引。
这些索引,配合列式存储和分区裁剪,能大大减少查询时的数据扫描量,提升查询性能。合理设计排序键和索引,是Doris性能优化的重要手段。
六、查询优化:预聚合、物化视图、Colocate Join
除了上面提到的存储引擎和查询引擎的优化,Doris还有一些高级的查询优化特性,进一步提升查询性能。
1. 预聚合(Aggregate Model)
前面提到,Doris的聚合模型(Aggregate Key Model)会在写入时就对数据做预聚合,相同维度的数据在MemTable和Compaction时就聚合好了。查询时,如果查询的维度和指标和预聚合的维度匹配,可以直接读取预聚合的数据,不需要再做聚合,查询速度非常快。
比如一张销售表,按日期、城市、商品维度预聚合了销售额和销量,查询"SELECT 日期, 城市, SUM(销售额) FROM 销售表 GROUP BY 日期, 城市"时,直接读取预聚合的数据,不需要扫描明细数据,性能提升明显。
预聚合的代价是写入时需要聚合,写入开销大一些,而且维度是固定的,如果查询的维度和预聚合的维度不匹配,就不能利用预聚合。所以聚合模型适合维度固定、查询模式固定的报表场景。
2. 物化视图(Materialized View)
物化视图是预聚合的升级版,它可以在一张基表上创建多个物化视图,每个物化视图可以有不同的维度、不同的聚合方式、不同的排序键,满足不同的查询模式。
比如一张用户行为明细表,可以创建:
- 物化视图1:按日期、用户ID聚合,查询用户日活。
- 物化视图2:按日期、页面聚合,查询页面访问量。
- 物化视图3:按日期、来源聚合,查询来源分布。
查询时,CBO优化器会自动判断查询是否匹配某个物化视图,如果匹配,就自动路由到物化视图查询,不需要用户手动指定,对用户透明。
物化视图的好处是灵活,可以针对不同的查询模式创建不同的物化视图,查询性能好。代价是占用更多存储空间(每个物化视图都存一份数据),写入时需要更新所有物化视图,写入开销大。所以物化视图适合查询模式多样、查询性能要求高、写入量不是特别大的场景。
3. Colocate Join(同组Join)
多表join是分析查询中常见的操作,也是性能瓶颈之一。传统的分布式join(Shuffle Join)需要把两张表的数据按join key哈希分发到所有节点,数据传输量大,网络开销大。
Doris支持Colocate Join(同组Join),把需要经常join的表,按相同的分桶列和分桶数,把对应的数据分桶存储在同一个BE组上。这样join的时候,对应桶的数据在同一个BE上,不需要跨节点传输数据,直接在本地join,大大减少网络开销,提升join性能。
比如订单表和用户表,都按userid分桶,分桶数相同,对应桶的数据存在同一个BE组上。查询"SELECT * FROM 订单 JOIN 用户 ON 订单.userid = 用户.user_id"时,每个BE只需要join本地的订单桶和用户桶,不需要数据传输,性能很好。
Colocate Join的限制是,同组的表必须有相同的分桶列、分桶数、副本数,而且对应桶的副本要在同一个BE组上,扩容和迁移时需要保持对应关系。所以Colocate Join适合那些经常join、join key固定的表,比如事实表和维度表的join。
4. Runtime Filter(运行时过滤)
Runtime Filter是一种动态的查询优化技术,在join查询中,先扫描小表,根据小表的join key生成一个过滤条件(Bloom Filter或Min/Max),然后在扫描大表的时候,用这个过滤条件提前过滤掉不匹配的数据,减少大表的扫描量和后续join的数据量。
比如"SELECT * FROM 大表 JOIN 小表 ON 大表.id = 小表.id",小表只有1000行,大表有1亿行。先扫描小表,得到1000个id,生成一个Bloom Filter,然后扫描大表的时候,用这个Bloom Filter过滤,只有id在Bloom Filter中的行才需要参与join,这样大表大部分数据都被过滤掉了,join的数据量大大减少,性能提升明显。
Doris支持Runtime Filter,在多表join查询中能自动应用,对大表join小表的场景性能提升很大。
七、高可用和运维
Doris的高可用和运维设计也比较完善,下面简单看看。
1. FE高可用
FE是无状态的,元数据存储在BDB-JE中,多FE部署时,通过BDB-JE的一致性协议同步元数据,选一个主FE,主FE挂了自动选新的主,服务不中断。FE的扩展很简单,新增FE节点,加入集群即可。
2. BE高可用
BE是有状态的,数据多副本存储(默认3副本),分布在不同的BE上。某个BE挂了,其他BE上的副本还能提供服务,数据不丢失。FE会检测到BE宕机,自动把宕机BE上的副本在其他BE上重建,恢复副本数,保证高可用。
BE的扩展也支持在线扩容,新增BE后,FE会自动调度,把部分数据迁移到新BE上,保持数据均衡,迁移过程不影响前台服务。
3. 易运维
Doris的运维比较简单:
- 架构简单:只有FE和BE两类进程,不依赖其他组件(Hadoop、ZooKeeper等),部署简单。
- 在线扩缩容:支持在线增加和减少FE、BE节点,不影响服务。
- 监控完善:提供丰富的监控指标(查询性能、写入性能、Compaction、副本状态等),可以对接Prometheus、Grafana等监控系统。
- Web管理界面:FE提供Web管理界面,可以查看集群状态、表信息、查询详情、进行管理操作等。
- 兼容性好:兼容MySQL协议,现有的MySQL客户端、BI工具、ETL工具都能直接用,不需要额外的适配器。
这些特性,让Doris的运维成本比较低,中小团队也能轻松运维。
八、Doris vs ClickHouse vs StarRocks
最后,简单对比一下Doris和其他常见的OLAP数据库,看看它们的区别和适用场景。
1. Doris vs ClickHouse
ClickHouse是俄罗斯Yandex开源的列式存储分析数据库,以极致的查询性能著称,单表查询性能非常强。
Doris和ClickHouse的区别:
- 架构:Doris是MPP架构,FE+BE,元数据和调度统一管理,分布式表和副本管理简单;ClickHouse是Shared Nothing架构,每个节点独立,分布式表需要自己配置,副本和分片管理相对复杂。
- 多表join:Doris的多表join性能较好,支持Colocate Join、Runtime Filter等优化,适合复杂的多表关联查询;ClickHouse的多表join性能相对较弱(虽然也在改进),更适合单表或简单join的场景。
- 实时写入:Doris支持实时写入,写入后立即可查,Compaction机制完善;ClickHouse的写入是批量的,小批量写入性能不好,建议大批量写入,实时写入需要额外处理。
- 易用性:Doris兼容MySQL协议,运维简单,易用性好;ClickHouse有自己的协议和客户端,虽然也兼容MySQL协议,但兼容性不如Doris,运维相对复杂。
- 性能:单表查询性能,ClickHouse通常更强(尤其是大宽表聚合);多表join和复杂查询,Doris可能更好。
- 社区和生态:ClickHouse社区更活跃,生态更丰富,全球用户更多;Doris在国内更火,国内大厂用得多,社区发展很快。
简单来说,如果你主要是单表或简单查询,追求极致性能,选ClickHouse;如果你需要复杂的多表join、实时写入、易用性好,选Doris。
2. Doris vs StarRocks
StarRocks是从Doris分叉出来的一个项目(最初叫DorisDB,后来改名为StarRocks),由一家商业公司主导开发,架构和Doris类似(FE+BE),但做了很多优化和改进。
Doris和StarRocks的区别:
- 主导方:Doris是Apache基金会项目,社区驱动,中立开放;StarRocks是商业公司主导,有企业版和社区版,商业支持更好。
- 性能:StarRocks在某些场景下性能更好,比如向量化执行更完善、CBO优化器更强、多表join性能更好,但Doris也在快速追赶,两者差距在缩小。
- 特性:StarRocks有一些Doris没有的特性,比如主键模型的更新性能更好、支持更丰富的索引、云原生版本等;Doris也有自己的特性,社区生态更开放。
- 稳定性:Doris发展时间更长,在很多大厂大规模使用,稳定性经过验证;StarRocks相对年轻,但发展很快,稳定性也在不断提升。
- 商业模式:Doris完全开源免费,没有商业版;StarRocks有社区版(免费)和企业版(收费),企业版有更多特性和支持。
简单来说,如果你想要完全开源、社区中立、经过大规模验证的,选Doris;如果你想要更好的商业支持、某些场景下更好的性能,选StarRocks。两者架构类似,迁移成本不高,可以根据实际需求选择。
九、写在最后
Doris是一个非常优秀的开源OLAP数据库,它的架构简洁高效,存储引擎和查询引擎都有很多优化,性能出色,易用性好,运维简单,非常适合企业级的数据分析场景。
深入研究Doris的原理之后,我对它的设计有了更深的理解,也学到了很多分布式数据库的设计思想。比如列式存储和MPP架构的结合、写入和查询的分离、Compaction机制、向量化执行、CBO优化器、预聚合和物化视图等,这些设计思想不仅适用于Doris,也适用于其他分布式系统。
当然,Doris也不是完美的,它也有一些局限性,比如超高写入量下Compaction可能成为瓶颈、超大规模集群下元数据管理可能有压力、某些复杂查询优化还不够完善等。但Doris社区发展很快,版本更新频繁,这些问题都在不断改进和解决。
总的来说,Doris是一个值得深入研究和使用的OLAP数据库,尤其是在国内的大数据分析场景下,Doris已经成为很多公司的首选。希望本文能帮助大家更好地理解Doris的底层原理,也欢迎大家一起交流讨论。
最后,用一句话结束本文:"理解原理,才能用好工具。"不管用什么数据库,深入理解它的底层原理,才能充分发挥它的性能,避免踩坑。希望大家都能找到适合自己业务场景的数据库,用好它,服务好业务。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录