最近项目用到了Doris,其实之前也用过Doris ,这次总结一下Doris基础。参考资料为官方文档:https://doris.apache.org/zh-CN/docs/3.0/gettingStarted/what-is-apache-doris,官方文档写的比较明了,而且是中文的。
1.简介
1.1历史
Apache Doris 最初是百度广告报表业务的 Palo 项目。2017 年正式对外开源,2018 年 7 月由百度捐赠给 Apache 基金会进行孵化。在 Apache 导师的指导下,由孵化器项目管理委员会成员进行孵化和运营。2022 年 6 月,Apache Doris 成功从 Apache 孵化器毕业,正式成为 Apache 顶级项目(Top-Level Project,TLP)。目前,Apache Doris 社区已经聚集了来自不同行业数百家企业的 600 余位贡献者,并且每月活跃贡献者人数超过 120 位。
1.2架构
Apache Doris 是一款基于 MPP 架构的高性能、实时分析型数据库。它以高效、简单和统一的特性著称,能够在亚秒级的时间内返回海量数据的查询结果。Doris 既能支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。MPP(Massively Parallel Processing)是一种通过多个处理器或节点并行处理数据的大规模并行处理架构。
1.3使用场景
基于这些优势,Apache Doris 非常适合用于报表分析、即席查询、统一数仓构建、数据湖联邦查询加速等场景。用户可以基于 Doris 构建大屏看板、用户行为分析、AB 实验平台、日志检索分析、用户画像分析、订单分析等应用。
2.整体架构
Apache Doris 采用 MySQL 协议,高度兼容 MySQL 语法,支持标准 SQL。用户可以通过各类客户端工具访问 Apache Doris,并支持与 BI 工具无缝集成。Doris 的整体架构由两类进程组成:Frontend (FE) 和 Backend (BE)。其中 FE 主要负责用户请求的接入、查询解析规划、元数据的管理、节点管理相关工作;BE 主要负责数据存储、查询计划的执行。在部署 Apache Doris 时,可以根据硬件环境与业务需求选择存算一体架构或存算分离架构。
2.1存算一体架构
FE 和 BE 进程都可以横向扩展。单个集群可以支持数百台机器和数十 PB 的存储容量。FE 和 BE 进程通过一致性协议来保证服务的高可用性和数据的高可靠性。存算一体架构高度集成,大幅降低了分布式系统的运维成本。
优点
部署简易:Apache Doris 不需要依赖类似外部共享文件系统或者对象存储,仅依赖物理服务器部署 FE 和 BE 两个进程即可完成集群的搭建,可以从一个节点扩展到数百个节点,同时也增强了系统的稳定性。
性能优异:Apache Doris 执行计算时,计算节点可直接访问本地存储数据,充分利用机器的 IO、减少不必要的网络开销、获得更极致的查询性能。
使用场景
简单使用/快速试用 Doris,或在开发和测试环境中使用;
不具备可靠的共享存储,如 HDFS、Ceph、对象存储等;
业务线独立维护 Apache Doris,无专职 DBA 来维护 Doris 集群;
不需极致弹性扩缩容,不需 K8s 容器化,不需运行在公有云或者私有云上。
2.2存算分离架构
从 3.0 版本开始,可以选择存算分离部署架构。Apache Doris 存算分离版使用统一的共享存储层作为数据存储空间。存储和计算分离后,用户可以独立扩展存储容量和计算资源,从而实现最佳性能和成本效益。存算分离架构分为以下三层:
元数据层: 负责请求规划、查询解析以及元数据的存储和管理。
计算层: 由多个计算组组成。每个计算组可以作为一个独立的租户承担业务计算。每个计算组包含多个无状态的 BE 节点,可以随时弹性伸缩 BE 节点。
存储层: 可以使用 S3、HDFS、OSS、COS、OBS、Minio、Ceph 等共享存储来存放 Doris 的数据文件,包括 Segment 文件和反向索引文件等。
优点
弹性的计算资源:不同时间点使用不同规模的计算资源服务业务请求,按需使用计算资源,节约成本。
负载(完全)隔离:不同业务之间可在共享数据的基础上隔离计算资源,兼具稳定性和高效率。
低存储成本:可以使用更低成本的对象存储,HDFS 等低成本存储。
适用场景
已在使用公有云服务
具备可靠的高性能共享存储系统,比如 HDFS、Ceph、对象存储等
多个业务使用共享同一份数据, 并且有隔离计算的需求
需要极致的弹性扩缩容,需要 K8S 容器化,需要运行在私有云上
有专职团队维护整个公司的数据仓库平台
如果共享存储的吞吐或者延迟等性能比较差,对于存算分离架构Doris有比较大的性能影响。
3.数据表设计——数据模型
Doris支持3种数据模型。主键模型(Unique Key Model)、明细模型(Duplicate Key Model)、
聚合模型(Aggregate Key Model)。因为数据模型在建表时就需要确定,且无法修改。针对业务场景,所以选择一个合适的数据模型非常重要。
3.1主键模型
特点
每一行的 Key 值唯一。Doris存储层对每个 key 只保留最新写入的数据,插入或更新数据时,新数据会覆盖具有相同 Key 的旧数据,确保数据记录为最新。与其他数据模型相比,主键模型适用于数据的更新场景,在插入过程中进行主键级别的更新覆盖。
使用场景
适合高频更新且有唯一键约束的场景。
高频数据更新:适用于上游 OLTP 数据库中的维度表,实时同步更新记录,并高效执行 UPSERT 操作;
数据高效去重:如广告投放和客户关系管理系统中,使用主键模型可以基于用户 ID 高效去重;
需要部分列更新:如画像标签场景需要变更频繁改动的动态标签,消费订单场景需要改变交易的状态。通过主键模型部分列更新能力可以完成某几列的变更操作。
实现方式
主键模型有两种实现方式。
写时合并(merge-on-write):自 1.2 版本起,Doris 默认使用写时合并模式,数据在写入时立即合并相同 Key 的记录,确保存储的始终是最新数据。写时合并兼顾查询和写入性能,避免多个版本的数据合并,并支持谓词下推到存储层。大多数场景推荐使用此模式;自 Doris 2.1 版本以后,默认开启写时合并:
读时合并(merge-on-read):在 1.2 版本前,Doris 中的主键模型默认使用读时合并模式,数据在写入时并不进行合并,以增量的方式被追加存储,在 Doris 内保留多个版本。查询或 Compaction 时,会对数据进行相同 Key 的版本合并,确保结果正确。读时合并适合写多读少的场景,在查询是需要进行多个版本合并,谓词无法下推,可能会影响到查询速度。
主键模型更新有两种语义。
整行更新:Unique Key 模型默认的更新语义为整行UPSERT,即 UPDATE OR INSERT,该行数据的 Key 如果存在,则进行更新,如果不存在,则进行新数据插入。在整行 UPSERT 语义下,即使用户使用 Insert Into 指定部分列进行写入,Doris 也会在 Planner 中将未提供的列使用 NULL 值或者默认值进行填充。
部分列更新:如果用户希望更新部分字段,需要使用写时合并实现,并通过特定的参数来开启部分列更新的支持。请查阅文档部分列更新。
数据插入与存储
建表,插入4条数据,再插入2条数据,相同的Key会覆盖。
CREATE TABLE IF NOT EXISTS example_tbl_unique( user_id LARGEINT NOT NULL, username VARCHAR(50) NOT NULL, city VARCHAR(20), age SMALLINT, sex TINYINT)UNIQUE KEY(user_id, username)DISTRIBUTED BY HASH(user_id) BUCKETS 10PROPERTIES ( "enable_unique_key_merge_on_write" = "false");-- insert into raw dataINSERT INTO example_tbl_unique VALUES(101, 'Tom', 'BJ', 26, 1),(102, 'Jason', 'BJ', 27, 1),(103, 'Juice', 'SH', 20, 2),(104, 'Olivia', 'SZ', 22, 2);-- insert into data to update by keyINSERT INTO example_tbl_unique VALUES(101, 'Tom', 'BJ', 27, 1),(102, 'Jason', 'SH', 28, 1);-- check updated dataSELECT * FROM example_tbl_unique;+---------+----------+------+------+------+| user_id | username | city | age | sex |+---------+----------+------+------+------+| 101 | Tom | BJ | 27 | 1 || 102 | Jason | SH | 28 | 1 || 104 | Olivia | SZ | 22 | 2 || 103 | Juice | SH | 20 | 2 |+---------+----------+------+------+------+
注意事项
Unique 表的实现方式只能在建表时确定,无法通过 schema change 进行修改。不只是Unique ,其他两种也是。
在整行 UPSERT 语义下,即使用户使用 insert into 指定部分列进行写入,Doris 也会在 Planner 中将未提供的列使用 NULL 值或者默认值进行填充;
部分列更新。如果用户希望更新部分字段,需要使用写时合并实现,并通过特定的参数来开启部分列更新的支持。请查阅文档部分列更新获取相关使用建议;
使用 Unique 表时,为了保证数据的唯一性,分区键必须包含在 Key 列内。
最后这条我解释一下:如果分区键不包含在Key中,那么相同的Key可能会被分到不同的分区中,这样只能保证一个分区内Key唯一,不能保证Key的全局唯一,会出现不同分区有相同Key的情况;如果分区键包含在Key中,那么相同的Key肯定会被分到同一个分区中,这样能保证一个分区内Key唯一,不同分区的Key肯定不同,这样就保证了Key的全局唯一。
3.2明细模型
允许指定的 Key 列重复,Doirs 存储层保留所有写入的数据,适用于必须保留所有原始数据记录的情况。
特点
保留原始数据:明细模型保留了全量的原始数据,适合于存储与查询原始数据。对于需要进行详细数据分析的应用场景,建议使用明细模型,以避免数据丢失的风险;
不去重也不聚合:不管key是不是相同,都是插入新数据,不会覆盖和聚合。
灵活的数据查询:明细模型保留了全量的原始数据,可以从完整数据中提取细节,基于全量数据做任意维度的聚合操作,从而进行元数数据的审计及细粒度的分析。
使用场景
适合一些全量原始数据,只会追加而不会发生变更的场景。
日志存储:用于存储各类的程序操作日志,如访问日志、错误日志等。每一条数据都需要被详细记录,方便后续的审计与分析;
用户行为数据:在分析用户行为时,如点击数据、用户访问轨迹等,需要保留用户的详细行为,方便后续构建用户画像及对行为路径进行详细分析;
交易数据:在某些存储交易行为或订单数据时,交易结束时一般不会发生数据变更。明细模型适合保留这一类交易信息,不遗漏任意一笔记录,方便对交易进行精确的对账。
数据插入和存储
明细模型中 Key 列指做为排序。
3.3聚合模型
Key值唯一,可根据 Key 列聚合数据,Doris 存储层保留聚合后的数据,从而可以减少存储空间和提升查询性能;通常用于需要汇总或聚合信息(如总数或平均值)的情况。聚合模型只存储聚合后的数据,节省存储空间并加速查询。比如插入了3个子订单,插入后可根据订单号聚合为1条,汇总订单金额。
使用场景
适合不需要查询原始明细数据,只需要明细数据汇总后的信息。
明细数据进行汇总:用于电商平台的月销售业绩、金融风控的客户交易总额、广告投放的点击量等业务场景中,进行多维度汇总;
不需要查询原始明细数据:如驾驶舱报表、用户交易行为分析等,原始数据存储在数据湖中,仅需存储汇总后的数据。
原理
每一次数据导入会在聚合模型内形成一个版本,在 Compaction 阶段进行版本合并,在查询时会按照主键进行数据聚合:
数据导入阶段:数据按批次导入,每批次生成一个版本,并对相同聚合键的数据进行初步聚合(如求和、计数);
后台文件合并阶段(Compaction):多个版本文件会定期合并,减少冗余并优化存储;
查询阶段:查询时,系统会聚合同一聚合键的数据,确保查询结果准确。