Druid - 核心原理、特点、以及架构详解
作者:hangge | 2024-11-12 08:49
1,什么是 Druid?
(1)Druid(文翻译为德鲁伊)是一个高性能的实时分析数据库,可以在复杂的海量数据下进行交互式实时数据分析,能够处理 TB 级别数据,以及响应在“毫秒”级。它主要是针对时间序列数据提供低延时的数据写入,以及快速交互式查询。
(2)Druid 是 MetaMarkets 公司于 2011 年推出的一款实时多维 OLAP 分析引擎,孵化于 Apache。Druid 在处理数据的实时性方面,比传统的离线 OLAP 引擎有了质的提升。
注意:阿里巴巴也创建过一个开源的数据库连接池项目 Druid,它本文介绍的实时 OLAP 分析引擎 Druid 没有任何关系。
2,Druid 的三个设计原则
(1)快速查询(Fast Query):其核心在于部分数据聚合(artial Aggregate)、数据内存化(In-emory)和索引(Index)。
- 对于数据分析场景,在大部分情况下,只需要关心按一定粒度聚合后的数据,而不是每一行原始数据的细节情况。数据聚合粒度可以是 1 分钟、10 分钟或 1 小时。部分数据聚合(artialAggregate)给 Druid 争取了很大的性能优化空间。
- 数据内存化也是提高查询速度的“杀手锏”。内存和硬盘的访问速度相差近百倍,但内存的大小是非常有限的,因此在内存使用方面要精细设计。例如:Druid 中使用了 Bitmap 和各种压缩技术。
- 另外,为了支持下钻某些维度,Druid 维护了一些倒排索引,这种方式可以加快 AND 和 OR 的计算效率。
(2)水平扩展(Horizontal Scalability):其核心在于分布式数据(Distributed Data)和并行化查询(Parallelizable Query)
- Druid 的查询性能在很大程度上依赖于内存的优化情况,数据可以被分布在多个节点的内存中,因此当数据增长时,可以通过增加机器的方式进行扩容。为了保持平衡,Druid 会按照时间范围对聚合数据进行分区处理。
- 对于高基数的维度,只按照时间拆分有时是不够的(因为 Druid 中的每个 Segment 不能超过 2000 万行),因此 Druid 还支持对 Segment 做进一步分区。
- 历史 Segment 数据可以被保存在深度存储系统中,可以是 HDFS、S3 之类的文件系统。如果某些节点出现故障,则可以借助 Zookeeper 协调其他节点来重新构造数据。
- Druid 中的查询模块能够感知和处理集群的状态变化,查询总是在有效的集群架构中进行的,集群上的查询可以进行灵活的水平扩展。
(3)实时分析(Realtime Analytics ):其核心在于“不可变的过去和只追加的未来”(Immutable Past,Append-Only Future )
- Druid 提供了包含基于时间维度数据的存储服务,并且任何一行数据都是历史真实发生的事件,因此在设计之初就约定“事件一旦进入系统就不能再改变”。
- 对于历史数据,Druid 以 Segment 数据文件的方式组织,并且将它们存储到深度存储系统中。在需要查询这些数据时,Druid 从深度存储系统中将它们加载到内存,供查询使用。
3,Druid 的优点
- 列式存储:使用面向列的存储格式,查询时只需要加载需要的列,这样可以显著提高查询性能。对于每一列都可以结合数据类型进行有针对性的优化,支持快速扫描和聚合。
- 可扩展的分布式架构:通常被部署在数十或者上百台服务器的集群中,可以提供每秒上百万条记录的接收速率,上亿条记录的存储,以及“亚秒”级别的查询延迟。
- 并行计算:可以在整个集群中并行处理查询。
- 实时和批量数据摄入:支持实时摄入数据或批量摄入数据,已被摄入的数据可以立即用于查询。
- 自修复、自平衡、易操作:集群扩展和缩小,只需要添加或者删除服务器,集群将在后台自动重新平衡,不需要任何停机时间。
- 云原生架构,高容错性:一旦数据进入 Druid,则副本就被安全地存储在深度存储系统中。即使某个 Druid 服务器出现故障,也可以从深度存储中恢复数据。对于仅影响少数 Druid 服务的有限故障,副本可确保在系统恢复时仍可以进行查询。
- 快速过滤的索引:使用压缩位图索引来创建索引,这些索引可以快速过滤和跨多个列搜索。
- 基于时间的分区:按照时间对数据进行分区,还可以根据其他字段进行分区。这意味着,基于时间的查询将仅访问指定的分区,这大大提高了基于时间分区数据的查询性能。
- 支持近似计算:提供了用于近似计数、近似排序之类的算法。这些算法提供了有限的内存使用,并且通常比精确计算快得多。对于准确度比速度更重要的场景,Druid 还提供了精确计数和精确排名。
- 自动实时聚合:支持在数据摄入阶段进行数据汇总,汇总时会预先聚合部分数据,这样可以节省大量成本,以及提高性能。
- 支持 SQL 查询:可以支持 SQL 实现数据分析。
4,Druid 的缺点
- SQL 支持有限:不支持部分 SQL 语句特性。
- 不支持 JOIN 操作:Druid SQL 原来是不支持 JOIN 操作的,从 0.18 版本开始正式支持,但也只支持 INNER、LEFT 和 CROSS 类型的 JOIN 操作。
- 不支持实时数据更新:支持实时数据插入,但是不支持实时更新,可以通过后台批处理作业间接实现更新操作。
- 分页功能需要借助于 LIMIT + OFFSET:无法通过 LIMIT 直接实现分页,需要结合 OFFSET 来实现。
- 无法查询原始明细数据:Durid 这种预聚合的方式可以显著减少数据的存储(理论上可以缩至 1/100)。但是也会带来副作用——会导致无法查询每条数据的原始明细,即数据聚合的粒度是能查询数据的最小粒度。
5,数据格式
(1)Druid 在数据存储时即可对数据进行聚合操作是其一个重要特点,该特点使得 Druid 不仅能够节省存储空间,而且能够显著提高聚合查询的效率。
(2)Druid 中的数据存储在 DataSource 中。DataSource 是一个逻辑概念,表示 Druid 的基本数据结构,可以将其理解为关系型数据库中的表。
(3)DataSource 包含时间、维度和指标这 3 列,具体见下面样例:
- Timesatmp(时间)列,表示每行数据的时间值,默认使用 UTC 时间格式,并且精确到毫秒级别。这一列是数据聚合与范围查询的重要维度。
- Dimensions(维度)列,用来标识数据行的各个类别信息。
- Metrics(指标)列,用于聚合计算的列。这些指标列通常是一些数字,对应的计算操作包括 Count、Sum 和 Mean 等。
Timestamp(间) | Dimensions(度) | Metrics(标) | |||
date | uid | name | age | clicks | coins |
2022-12-01T00:00:00Z | 1001 | Tom | 18 | 180 | 219 |
2022-12-01T00:00:00Z | 1002 | Jack | 20 | 201 | 392 |
2022-12-01T00:00:00Z | 1003 | Mick | 19 | 800 | 782 |
6,数据摄入方式
(1)Druid 支持两种类型的数据摄入方式:- 对于实时产生的数据,支持实时摄入。
- 对于历史数据,也可以实现批量摄入。
(2)这样可以保证在 Druid 中维护指定业务的所有数据,以及对于实时摄入的数据定时实现数据修正。之后通过 Druid 提供统一查询功能。
7,数据查询方式
(1)针对 Druid 的查询,官方提供了以下两种方式:
- Native Query:可以称之为本地查询。在指定查询条件时,需要组装 JSON 格式的查询条件,所以也可以称之为 JSON 查询方式。
{ "queryType": "scan", "dataSource": { "type": "union", "dataSources": ["table1", "table2", "table3"] }, "columns": ["column1", "column2"], "intervals": ["0000/3000"] }
- SQL 查询:Druid SQL 是 Druid 基于本地查询的替代品,主要是为了提供更加方便的查询功能,SQL 解析引擎使用的是 Apache Calcite 解析器。Druid SQL 会将 SQL 查询转换为原生的 Native Query(在转换 SQL 时的极少开销外,没有额外的性能损失)。
SELECT column1, column2 FROM( SELECT column1, column2 FROM table1 UNION ALL SELECT column1, column2 FROM table2 UNION ALL SELECT column1, column2 FROM table3 )
(2)Druid SQL 并非支持所有的 SQL 特性,下面这些是不支持的:
- 原生数据源(able、lookup、subquery)与系统表的 JOIN 操作。
- 左侧和右侧的表达式不相等的 JOIN 操作。
- 在 JOIN 操作的连接条件内包含常量值。
- 在 JOIN 操作的连接条件内包含多值维度。
- OVER 子句和分析型函数,例如:LAG 和 LEAD 等分析型函数。
- DDL 和 DML。
- 在系统表上使用 Druid 特性的函数。例如:TIME_PARSE 和 APPROX_QUANTILE_DS。
(3)目前 Druid SQL 也无法全部支持所有的 Native Query,主要支持以下:
- INLINE 数据源。
- 空间过滤器。
- 查询取消。
- 多值维度,仅在 Druid SQL 中部分实现。
8,架构分析
(1)Druid 的架构如下图所示,其中:
- Master Servers、Query Servers 和 Data Servers 属于 Druid 的服务;
- Coordinators、Overlords、Routers(optional)、Brokers、Middle Managers 和 Historicals 属于 Druid 的处理进程;
- Metadata Storage、Zookeeper 和 Deep Storage 属于 Druid 依赖的外部组件。
(2)Master Servers 说明:
- Coordinators 表示协调器,主要负责 Segment 的分发。例如只保存 30 天的数据,这个规则需要由 Coordinator 来定时执行。
- Overlords 主要负责处理摄入数据的任务,最终将任务提交到 MiddleManager。
(3)Query Servers 说明:
- Routesr 相当于多个 Broker 前面的路由,不是必需的。
- Brokers 主要负责处理外部请求,并对结果进行汇总。
(4)Data Servers 说明:
- MiddleManagers 可以被认为是任务调度进程,主要用来处理 Overlord 提交过来的任务。
- Historicals 用于将 Segment 存储到本地,相当于 Cache。相比于 Deep Storage,Historicals 会将 Segment 直接存储到本地磁盘,只有将 Segment 存储到本地才能被查询。Historicals 处理哪些 Segment 是由 Coordinators 指定的,但是 Historicals 并不会和 Coordinators 直接交互,而是通过 Zookeeper 来解耦。
(5)架构中涉及 3 种条行路线:
- Queries:Routers 将查询请求路由到 Brokers,Brokers 向 Middle Managers 和 Historicals 执行数据查询。在这里,Middle Managers 主要负责查询正在摄入的数据。例如,正在摄入 10:00~11:00 的数据,则需要查询 Middle Managers。对历史数据的查询是通过 Historicals 来实现的,然后这些数据被返回到 Brokers 进行汇总。这里需要注意的是,数据查询并不会直接落到 Deep Storage 上,即查询的数据一定是缓存到本地磁盘的。
- Metadata:Druid 的元数据主要存储到两个地方:一个是 Metadata Storage,这个一般是 MySQL,另一个是 Zookeeper。
- Data/Segments:这里包括两个部分:
- ① Middle Managers 的任务在结束时会将数据写入 Deep Storage,这个过程一般被称为 Segment Handoff。
- ② Historicals 定期去下载 Deep Storage 的 Segment 数据到本地。
附:对比 Druid、ClickHouse 和 Doris
Druid、ClickHouse 和 Doris 都适合应用于实时 OLAP 数据分析领域,不过它们各有特色,在技术选型时需要结合具体业务需求进行考虑。这 3 个实时 OLAP 引擎的对比如下。
- Druid:主要针对时间序列数据提供低延时的数据写入及快速交互式 SQL 查询,适合被应用在海量实时数据的交互式分析场景中。其可以基于时间维度对实时产生的明细数据自动实时聚合,提高查询效率。它有一个比较明显的缺点:对 SQL 支持有限。在 Druid 0.18 版本之前它是不支持 JOIN 操作的,从 0.18 版本开始它有限支持 JOIN 操作,目前主要支持 INNER JOIN、LEFT JOIN 和 CROSS JOIN。
- ClickHouse:可以提供海量实时数据的快速写入,以及基于 SQL 的快速实时查询,适合应用在海量实时数据的交互式分析场景中。其支持非标准 SQL,有限支持 JOIN 操作,目前在实时数据仓库领域应用得比较广泛。
- Doris:可以通过 SQL 实现实时数据分析,适合应用在海量实时数据的交互式分析场景中。其对 SQL 支持比较好,支持 JOIN 操作。Doris 和 ClickHouse 是比较类似的,但是 Doris 目前的成熟度暂时还不如 ClickHouse。
说明:
- 查询性能:这 3 个组件的查询性能都比较高。
- 高并发:Druid 和 Doris 可以支持高并发,ClickHouse 的并发能力有限。
- 实时数据摄入:因为都属于实时 OLAP 引擎,所以它们都支持实时数据摄入。
- 实时数据更新:Druid 是不支持实时更新的,只能通过后台批处理任务实现覆盖更新。ClickHouse 支持实时更新,只是功能比较弱。Doris 可以正常支持实时更新。
- 支持 JOIN 操作:在基于 SQL 实现统计分析时,是否支持 JOIN 操作是一个比较重要的指标。Druid 和 ClickHouse 都支持 JOIN 操作,但是支持度有限。Doris 可以正常支持 JOIN 操作。
- SQL 支持程度:Druid 对 SQL 的支持度是有限的,ClickHouse 支持非标准 SQL,Doris 支持标准 SQL(相对较好)。
- 成熟程度:Druid 和 ClickHouse 的成熟程度比较高,Doris 目前正处于快速发展阶段。
- 运维复杂度:ClickHouse 运维比较复杂,Druid 运维难度中等,Doris 运维最简单。
全部评论(0)