Apache Druid设计原理
目录
一、架构
1、设计
Druid具有多进程,分布式架构,旨在实现云友好且易于操作。每种Druid进程类型都可以独立配置和扩展,从而为您的集群提供最大的灵活性。这种设计还提高了容错能力:一个组件的故障不会立即影响其他组件。
2、进程和服务器
Druid有几种进程类型,下面简要描述:
- Coordinator协调器进程管理群集上的数据可用性。
- Overlord领主进程控制数据提取工作负载的分配。
- Broker代理进程处理来自外部客户端的查询。
- Router路由器进程是可选进程,可以将请求路由到代理,协调器和领主。
- Historical历史进程存储可查询的数据。
- MiddleManager中间管理器进程负责摄取数据。
Druid进程可以按照您喜欢的任何方式进行部署,但是为了便于部署,我们建议将其组织为三种服务器类型:主服务器,查询服务器和数据服务器。
- 主机:运行协调器和领主进程,管理数据可用性和接收。
- 查询:运行代理和可选的路由器进程,处理来自外部客户端的查询。
- 数据:运行Historical和MiddleManager进程,执行提取工作负载并存储所有可查询的数据。
有关进程和服务器组织的更多详细信息,请参阅Druid进程和服务器。
3、外部依赖
除了内置的进程类型外,Druid还具有三个外部依赖项。这些旨在能够利用现有的现有基础结构。
4、深度储存
每个Druid服务器均可访问共享文件存储。在集群部署中,这通常将是分布式对象存储(例如S3或HDFS)或网络安装的文件系统。在单服务器部署中,这通常是本地磁盘。Druid使用深度存储来存储已提取到系统中的所有数据。
Druid仅将深度存储用作数据的备份,并作为在Druid进程之间在后台传输数据的方式。为了响应查询,历史进程不会从深度存储中读取数据,而是会在提供任何查询之前从其本地磁盘读取预提取的段。这意味着Druid不需要在查询期间访问深层存储,从而帮助它提供了最佳的查询延迟。这也意味着在深度存储和历史进程中,必须有足够的磁盘空间用于计划加载的数据。
深度存储是Druid的弹性,容错设计的重要组成部分。即使每台数据服务器都丢失并重新配置,Druid也可以从深度存储中引导。
有关更多详细信息,请参阅深度存储页面。
5、元数据存储
元数据存储区包含各种共享的系统元数据,例如段segment使用信息和任务task信息。在集群部署中,这通常将是传统的RDBMS,例如PostgreSQL或MySQL。在单服务器部署中,它通常将是本地存储的Apache Derby数据库。
有关更多详细信息,请参见“ 元数据存储”页面。
6、zookeeper
用于内部服务发现,协调和领导者选举。
有关更多详细信息,请参见ZooKeeper页面。
7、架构图
下图显示了使用建议的Master / Query / Data服务器组织的查询和数据在整个体系结构中的流动方式:
8、储物设计
Druid数据存储在“datasources”中,类似于传统RDBMS中的表。每个数据源都按时间分区,并且可以选择按其他属性进一步分区。每个时间范围都称为“块”chunk(例如,如果您的数据源按天划分,则为一天)。在一个块内,数据被划分为一个或多个 “段”segments。每个段都是单个文件,通常包含多达几百万行的数据。由于细分是按时间块组织的,因此将细分视为生活在时间线上的以下内容有时会有所帮助:
数据源可能具有从几个段到数十万甚至数百万个段的任何位置。每个段都是从在MiddleManager上创建开始的,并且那时是可变的且未提交的。段构建过程包括以下步骤,旨在生成紧凑且支持快速查询的数据文件:
- 转换为列格式
- 使用位图索引编制索引
- 使用各种算法进行压缩
- 字符串列的ID存储最小化的字典编码
- 位图索引的位图压缩
- 所有列的类型感知压缩
分段会定期提交和发布。此时,它们被写入深度存储,变得不可变,并从MiddleManagers迁移到Historical进程。有关该段的条目也将写入到元数据存储中。该条目是有关该段的元数据的自描述位,包括诸如段的模式,其大小以及其在深度存储上的位置之类的信息。这些条目是协调器用来了解集群上应该有哪些数据的内容。
有关段文件格式的详细信息,请参阅段文件。
有关在Druid中对数据建模的详细信息,请参见模式设计。
索引是创建新段的机制,移交是它们被发布并开始由历史进程提供服务的机制。该机制在索引端的工作方式如下:
- 一个索引任务开始运行,并建立一个新的细分市场。它必须在开始构建段之前确定该段的标识符。对于要追加的任务(例如Kafka任务,或追加模式中的索引任务),可以通过在Overlord上调用“分配” API来完成,以潜在地将新分区添加到现有的段集中。对于要覆盖的任务(例如Hadoop任务或不在附加模式下的索引任务),这可以通过锁定间隔并创建新的版本号和新的段集来完成。
- 如果索引任务是实时任务(例如Kafka任务),则此时可以立即查询该段。它可用,但尚未发布。
- 索引任务完成对段的数据读取后,会将其推入深度存储,然后通过将记录写入元数据存储来发布它。
- 如果索引任务是实时任务,则此时它等待“历史”进程加载该段。如果索引任务不是实时任务,它将立即退出。
在协调器/历史方面,像这样:
- 协调器定期(默认情况下,每1分钟)轮询元数据存储区以查找新发布的段。
- 当协调器找到已发布和使用但不可用的段时,它会选择一个“历史记录”进程来加载该段并指示“历史记录”这样做。
- 历史记录会加载细分并开始投放。
- 此时,如果索引任务正在等待切换,它将退出。
段均具有包含以下部分的四部分标识符:
- 数据源名称。
- 时间间隔(对于包含分段的时间块;这对应
segmentGranularity
于摄取时指定的时间间隔)。 - 版本号(通常为ISO8601时间戳,与首次启动段集的时间相对应)。
- 分区号(整数,在数据源+间隔+版本内是唯一的;可能不一定是连续的)。
例如,这是datasource clarity-cloud0
,time chunk 2018-05-21T16:00:00.000Z/2018-05-21T17:00:00.000Z
,version 2018-05-21T15:56:09.909Z
和分区号1中的段的标识符:
clarity-cloud0_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T15:56:09.909Z_1
分区号为0(块中的第一个分区)的段省略了分区号,如以下示例所示,它是与上一个分区在同一时间块中的段,但分区号为0而不是1:
clarity-cloud0_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T15:56:09.909Z
您可能想知道上一节中描述的“版本号”是做什么的。或者,您可能不是,在这种情况下对您有好处,您可以跳过本部分!
它在那里支持批处理模式覆盖。在Druid中,如果您要做的只是附加数据,那么每个时间块只有一个版本。但是,当您覆盖数据时,在幕后发生的事情是使用相同的数据源,相同的时间间隔,但版本号更高的方式创建了一组新的段。这向Druid系统的其余部分发出信号,表明应从集群中删除较旧的版本,而应使用新版本替换它。
对于用户而言,切换似乎是瞬间发生的,因为Druid通过首先加载新数据(但不允许对其进行查询)来处理此问题,然后在所有新数据加载完毕后,立即切换所有新查询以使用这些新查询。新细分。然后,它会在几分钟后删除旧段。
每个部分的生命周期都涉及以下三个主要领域:
- 元数据存储区:一旦构建完段,就将段元数据(小的JSON有效负载,通常不超过几个KB)存储在 元数据存储区中。将段的记录插入元数据存储的操作称为发布。这些元数据记录具有一个名为的布尔标志
used
,用于控制该段是否可查询。由实时任务创建的细分将在发布之前可用,因为它们仅在细分完成时才发布,并且不接受任何其他数据行。 - 深度存储:分段数据构建完成后,会将分段数据文件推送到深度存储。这在将元数据发布到元数据存储之前立即发生。
- 查询的可用性:段可用于在某些Druid数据服务器上进行查询,例如实时任务或历史进程。
您可以使用Druid SQL sys.segments
表检查当前活动段的状态 。它包括以下标志:
is_published
:如果分段元数据已发布到元数据存储中,used
则为true,并且为true。is_available
:如果该段当前可用于实时任务或历史流程查询,则为True。is_realtime
:如果分段仅在实时任务上可用,则为true 。对于使用实时提取的数据源,通常会先开始true
,然后false
随着段的发布和移交而变成。is_overshadowed
:如果该段已发布(used
设置为true)并且被其他已发布的段完全遮盖,则为true。通常,这是一个过渡状态,处于此状态的段很快就会将其used
标志自动设置为false。
9、查询处理
查询首先进入Broker,Broker将在其中识别哪些段具有与该查询有关的数据。段列表始终按时间修剪,也可以根据其他属性来修剪,这取决于数据源的分区方式。然后,代理将确定哪些历史记录和 MiddleManager正在为这些段提供服务,并将重写的子查询发送给每个流程。Historical / MiddleManager进程将接受查询,对其进行处理并返回结果。经纪人接收结果并将它们合并在一起以得到最终答案,并将其返回给原始呼叫者。
代理修剪是Druid限制每个查询必须扫描的数据量的一种重要方法,但这不是唯一的方法。对于比Broker可以使用的过滤器更细粒度的过滤器,每个段内的索引结构允许Druid在查看任何数据行之前先找出哪些行(如果有)与过滤器集匹配。一旦Druid知道哪些行与特定查询匹配,它就只会访问该查询所需的特定列。在这些列中,Druid可以在行与行之间跳过,从而避免读取与查询过滤器不匹配的数据。
因此,Druid使用三种不同的技术来最大化查询性能:
- 修剪为每个查询访问的细分。
- 在每个段中,使用索引来标识必须访问的行。
- 在每个段中,仅读取与特定查询相关的特定行和列。
有关Druid如何执行查询的更多详细信息,请参阅查询执行 文档。
二、Segments段
Apache Druid将其索引存储在段文件中,该段文件按时间进行分区。在基本设置中,将为每个时间间隔创建一个段文件,其中时间间隔可在的segmentGranularity
参数中 配置 granularitySpec
。为了使Druid在繁重的查询负载下正常运行,重要的是,段文件的大小应在建议的300MB-700MB范围内。如果你的段文件大于该范围,然后再考虑要么改变时间间隔的粒度或分区数据,并调整了targetPartitionSize
您的partitionsSpec
(一个很好的起点。此参数是500万行)。有关更多信息,请参见下面的分片部分和“ 批量提取”文档的“分区规范”部分。
1、段文件的核心数据结构
在这里,我们描述了段文件的内部结构,该结构基本上是柱状的:每列的数据都布置在单独的数据结构中。通过分别存储每列,Druid可以通过仅扫描查询实际需要的那些列来减少查询延迟。共有三种基本列类型:时间戳列,维度列和指标列,如下图所示:
timestamp和metric列很简单:在幕后每个都是由LZ4压缩的整数或浮点值的数组。一旦查询知道需要选择的行,它就简单地解压缩这些行,取出相关的行,然后应用所需的聚合运算符。与所有列一样,如果查询不需要一列,则该列的数据将被跳过。
维度列有所不同,因为它们支持筛选和分组操作,因此每个维度都需要以下三个数据结构:
- 将值(始终被视为字符串)映射到整数ID的字典,
- 使用1中的字典编码的列值列表,以及
- 对于列中的每个不同值,一个位图指示哪些行包含该值。
为什么要使用这三个数据结构?字典仅将字符串值映射为整数id,以便可以紧凑地表示(2)和(3)中的值。(3)中的位图-也称为反向索引,允许快速过滤操作(特别是,位图便于快速应用AND和OR运算符)。最后,group by和TopN 查询需要(2)中的值列表。换句话说,仅基于过滤器汇总指标的查询不需要触摸存储在(2)中的维度值列表。
为了具体了解这些数据结构,请考虑上面示例数据中的“ page”列。下图说明了表示该维度的三个数据结构。
1: Dictionary that encodes column values
{
"Justin Bieber": 0,
"Ke$ha": 1
}
2: Column data
[0,
0,
1,
1]
3: Bitmaps - one for each unique value of the column
value="Justin Bieber": [1,1,0,0]
value="Ke$ha": [0,0,1,1]
请注意,位图与前两个数据结构不同:前两个在数据大小上呈线性增长(在最坏的情况下),而位图部分的大小则是数据大小*列基数的乘积。压缩将在这里为我们提供帮助,因为我们知道对于“列数据”中的每一行,只有一个位图的条目为非零。这意味着高基数列将具有极为稀疏的可压缩高度位图。Druid使用特别适合位图的压缩算法(例如咆哮的位图压缩)来利用这一点。
2、多值列
如果数据源使用多值列,则段文件中的数据结构看起来会有所不同。让我们想象一下,在上面的示例中,第二行同时标记了“ Ke $ ha” 和 “ Justin Bieber”主题。在这种情况下,这三个数据结构现在看起来如下:
1: Dictionary that encodes column values
{
"Justin Bieber": 0,
"Ke$ha": 1
}
2: Column data
[0,
[0,1], <--Row value of multi-value column can have array of values
1,
1]
3: Bitmaps - one for each unique value
value="Justin Bieber": [1,1,0,0]
value="Ke$ha": [0,1,1,1]
^
|
|
Multi-value column has multiple non-zero entries
注意列数据和Ke $ ha位图中第二行的更改。如果一行的一个列有多个值,则其在“列数据”中的输入是一组值。此外, “列数据”中具有n 个值的行在位图中将具有n个非零值条目。
3、SQL兼容的空处理
默认情况下,Druid字符串维列使用值''
并且null
可以互换使用,而数值和度量列完全不能表示null
,而是将null强制为0
。但是,Druid还提供了与SQL兼容的空处理模式,必须通过在启用此操作,以在系统级别启用该模式druid.generic.useDefaultValueForNull
。此设置,当设置为false
,将允许Druid在摄取时间创建的段,其字符串列可以区分''
从null
,并且其可以表示数字列null
值的行代替的0
。
字符串维列在此模式下不包含任何其他列结构,而是仅保留该null
值的其他字典条目。但是,数值列将与附加位图一起存储在段中,该位图的设置位指示有null
价行。除了需要稍微增加段大小外,由于需要检查空位图,因此与SQL兼容的空处理也可能在查询时产生性能成本。该性能开销仅发生在实际上包含空值的列上。
4、命名约定
段标识符通常使用段数据源,间隔开始时间(ISO 8601格式),间隔结束时间(ISO 8601格式)和版本来构造。如果另外将数据分片超出时间范围,则段标识符还将包含分区号。
一个示例段标识符可以是:datasource_intervalStart_intervalEnd_version_partitionNum
5、段组件
在幕后,一个段由几个文件组成,下面列出。
-
version.bin
4个字节,以整数表示当前段版本。例如,对于v9段,版本为0x0、0x0、0x0、0x9
-
meta.smoosh
具有有关其他
smoosh
文件内容的元数据(文件名和偏移量)的文件 -
XXXXX.smoosh
这些文件中有一些是串联的二进制数据
这些
smoosh
文件代表在一起“ smooshed”的多个文件,以减少必须打开以容纳数据的文件描述符的数量。它们是最大2GB的文件(以匹配Java中内存映射的ByteBuffer的限制)。这些smoosh
文件包含数据中每个列的单独文件,以及index.drd
带有有关该段的额外元数据的文件。还有一个特殊的列,称为
__time
该段的时间列。希望随着代码的发展,这种特殊性将越来越小,但就目前而言,它就像我妈妈一直告诉我的那样特殊。
在代码库中,段具有内部格式版本。当前的句段格式版本为v9
。
6、列格式
每列存储为两部分:
- Jackson序列化的ColumnDescriptor
- 该列的其余二进制文件
ColumnDescriptor本质上是一个对象,它使我们能够使用Jackson的多态反序列化来添加新的有趣的序列化方法,而对代码的影响最小。它由一些有关该列的元数据组成(它是什么类型,它是多值的,等等),然后是可以反序列化其余二进制数的序列化/反序列化逻辑列表。
7、压缩
Druid 默认使用LZ4压缩字符串,长列,浮点和双列的值块,而字符串列和数字空值的位图使用Roaring压缩。我们建议您坚持使用这些默认值,除非对您自己的数据和查询模式进行的实验性验证表明非默认选项在您的特定情况下效果更好。例如,对于字符串列中的位图,在高基数列中使用Roaring和CONCISE之间的差异最为明显。在这种情况下,在匹配许多值的过滤器中,咆哮速度要快得多,但是在某些情况下,由于咆哮格式的开销,CONCISE的占用空间可能较小(但在匹配许多值时,速度仍然较慢)。当前,压缩是在段级别而不是单个列上配置的,有关更多详细信息,请参见IndexSpec。
8、分片数据以创建细分
对于同一数据源,在相同的时间间隔内可能存在多个段。这些段形成一个block
间隔。根据shardSpec
用于分片数据的类型,仅当a block
完成时,Druid查询才可能完成。也就是说,如果一个块由3个段组成,例如:
sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_0
sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_1
sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_2
在查询间隔2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z
完成之前,必须装入所有3个段。
该规则的例外是使用线性分片规范。线性分片规范不会强制“完整性”,即使分片未加载到系统中,查询也可以完成。例如,如果您的实时摄取创建了3个使用线性分片规范进行分片的段,并且系统中仅加载了两个段,则查询将仅返回这2个段的结果。
9、模式变更
Druid使用数据源,间隔,版本和分区号唯一地标识段。如果在一段时间内创建了多个段,则分区号仅在段ID中可见。例如,如果您有按小时细分,但一个小时内的数据量超过单个细分所能容纳的时间,则可以在同一小时内创建多个细分。这些段将共享相同的数据源,间隔和版本,但分区号线性增加。
foo_2015-01-01/2015-01-02_v1_0
foo_2015-01-01/2015-01-02_v1_1
foo_2015-01-01/2015-01-02_v1_2
另外,在上述中,实例中的片段dataSource = foo
,interval = 2015-01-01/2015-01-02
,version = v1
,和partitionNum = 0
。如果在以后的某个时间点,您使用新的架构重新索引数据,则新创建的段将具有更高的版本ID。
foo_2015-01-01/2015-01-02_v2_0
foo_2015-01-01/2015-01-02_v2_1
foo_2015-01-01/2015-01-02_v2_2
Druid批处理索引(基于Hadoop或基于IndexTask)可确保每个间隔的原子更新。在我们的示例中,在将所有v2
段2015-01-01/2015-01-02
都加载到Druid集群中之前,查询仅使用v1
段。一旦v2
加载了所有段并可以查询,所有查询将忽略v1
段并切换到这些v2
段。此后不久,v1
从集群中卸载了这些段。
请注意,跨越多个段间隔的更新仅是每个间隔内的原子。在整个更新过程中,它们不是原子的。例如,您具有如下段:
foo_2015-01-01/2015-01-02_v1_0
foo_2015-01-02/2015-01-03_v1_1
foo_2015-01-03/2015-01-04_v1_2
v2
分段一旦构建,就会被加载到集群中,并v1
在分段重叠的时间段内替换分段。在完全加载v2段之前,您的群集可能包含v1
和v2
段的混合。
foo_2015-01-01/2015-01-02_v1_0
foo_2015-01-02/2015-01-03_v2_1
foo_2015-01-03/2015-01-04_v1_2
在这种情况下,查询可能会混合使用v1
和v2
细分。
10、段之间的模式不同
同一数据源的Druid段可能具有不同的架构。如果一个段中存在一个字符串列(维),但另一个段中不存在,则涉及这两个段的查询仍然有效。缺少维的段查询将表现得好像维只有空值。同样,如果一个细分中有一个数字列(指标),而另一部分则没有,则对缺少指标的细分查询通常会“做正确的事”。此缺失度量标准上的聚合的行为就像该度量标准缺失一样。
三、进程和服务器
1、进程类型
Druid具有几种过程类型:
- 协调器Coordinator
- 领主Overlord
- 代理器Broker
- 历史结点Historical
- 中间层管理器MiddleManager和Peons
- 索引器(可选)
- 路由器(可选)
2、服务器类型
Druid进程可以按照您喜欢的任何方式进行部署,但是为了便于部署,我们建议将它们组织为三种服务器类型:
- master结点
- query结点
- data结点
本节描述了Druid进程以及建议的Master / Query / Data服务器组织,如上面的体系结构图所示。
- 主服务器
- 主服务器管理数据的接收和可用性:它负责启动新的接收作业,并协调下述“数据服务器”上数据的可用性。
在主服务器中,功能被划分为两个进程,即协调器和Overlord。
协调器进程监视数据服务器上的历史进程。他们负责将段分配给特定的服务器,并确保段在整个历史记录之间保持平衡。
Overlord进程监视数据服务器上的MiddleManager进程,并且是将数据摄入到Druid中的控制器。他们负责将提取任务分配给MiddleManager,并负责协调段发布。
在查询服务器中,功能在两个进程(代理程序和路由器)之间分配。
代理进程从外部客户端接收查询,并将这些查询转发到数据服务器。当Broker从这些子查询中接收到结果时,它们会合并这些结果并将其返回给调用方。最终用户通常查询代理,而不是直接查询数据服务器上的Historicals或MiddleManagers进程。
路由器进程是可选进程,可在Druid代理,Overlord和协调器之前提供统一的API网关。它们是可选的,因为您也可以直接联系Druid经纪人,Overlord和协调器。
路由器还运行Druid Console,这是用于数据源,段,任务,数据进程(历史记录和MiddleManager)以及协调器动态配置的管理UI。用户还可以在控制台中运行SQL和本机Druid查询。
-
数据服务器
-
数据服务器执行提取作业并存储可查询的数据。
在数据服务器内,功能分为两个进程,即Historical和MiddleManager。
-
历史过程是处理“历史”数据(包括系统中已经存在足以提交的流数据)的存储和查询的主力军。历史过程从深度存储中下载段,并响应有关这些段的查询。他们不接受写信。
-
MiddleManager进程处理将新数据提取到群集中的过程。他们负责从外部数据源读取并发布新的Druid段。
Peon进程是由MiddleManagers产生的任务执行引擎。每个Peon运行一个单独的JVM,并负责执行一个任务。Peons始终与生成它们的MiddleManager在同一主机上运行。
- 索引器过程(可选)
- 索引器进程是MiddleManager和Peons的替代方案。索引器不是将每个任务分派单独的JVM进程,而是将任务作为单个JVM进程中的单独线程运行。
与MiddleManager + Peon系统相比,Indexer的设计易于配置和部署,并且可以更好地实现跨任务的资源共享。索引器是一项较新的功能,由于其内存管理系统仍在开发中,因此目前被指定为 实验性。在未来的Druid版本中它将继续成熟。
通常,您将部署MiddleManagers或Indexer,但不能同时部署。
如上所述,可以根据主服务器/数据服务器/查询服务器的组织来部署Druid进程。该组织通常可以更好地利用大多数群集的硬件资源。
但是,对于超大型集群,可能需要拆分Druid进程,以使其在单独的服务器上运行,以避免资源争用。
本节介绍与过程共置有关的准则和配置参数。
在具有非常高的段数的集群中,将协调器和Overlord流程分开以为协调器的段平衡工作负载提供更多资源是有意义的。
通过设置druid.coordinator.asOverlord.enabled
属性,可以将协调程序和Overlord流程作为单个组合流程运行。
有关详细信息,请参见协调器配置:操作。
历史记录还受益于为内存映射段提供的空闲内存,这可能是分别部署历史记录和MiddleManager进程的另一个原因。
四、Deep storage深度储存
深度存储是存储段的地方。它是Apache Druid不提供的存储机制。这种深层的存储基础架构定义了数据的持久性级别,只要Druid进程可以看到该存储基础架构并进入存储在其上的段,那么无论丢失了多少Druid节点,您都不会丢失数据。如果段从该存储层消失,那么您将丢失这些段表示的所有数据。
1、本地挂载
本地挂载也可以用于存储段。这使您可以仅使用本地文件系统或可以在本地安装的任何其他文件(例如NFS,Ceph等)。这是默认的深度存储实现。
为了使用本地安装进行深度存储,您需要在通用配置中设置以下配置。
属性 | 可能的值 | 描述 | 默认 |
---|---|---|---|
druid.storage.type | 本地 | 必须设置。 | |
druid.storage.storageDirectory | 存储段的目录。 | 必须设置。 |
请注意,您通常应该将设置druid.storage.storageDirectory
为与druid.segmentCache.locations
和不同druid.segmentCache.infoDir
。
如果您在本地模式下使用Hadoop索引器,则只需为其提供一个本地文件作为您的输出目录即可使用。
2、兼容S3
3、HDFS
4、其他深层商店
有关其他深层存储,请参阅我们的扩展列表。
五、Metadata storage
元数据存储是Apache Druid的外部依赖项。Druid使用它来存储有关系统的各种元数据,而不是存储实际数据。有许多用于以下目的的表格。
Derby是Druid的默认元数据存储,但是,它不适合生产。 MySQL和PostgreSQL是更适合生产的元数据存储。
元数据存储存储整个元数据,这对于Druid集群正常工作至关重要。对于生产集群,请考虑使用MySQL或PostgreSQL而不是Derby。另外,强烈建议设置一个高可用性环境,因为如果丢失任何元数据,则无法还原。
1、使用Derby
将以下内容添加到您的Druid配置中。
druid.metadata.storage.type=derby
druid.metadata.storage.connector.connectURI=jdbc:derby://localhost:1527//opt/var/druid_state/derby;create=true
2、MySQL
3、PostgreSQL的
请参阅postgresql-metadata-storage。
4、添加自定义dbcp属性
注意:这些属性是不可设置通过druid.metadata.storage.connector.dbcp properties
:username
,password
,connectURI
,validationQuery
,testOnBorrow
。这些必须通过druid.metadata.storage.connector
属性设置。
支持的属性示例:
druid.metadata.storage.connector.dbcp.maxConnLifetimeMillis=1200000
druid.metadata.storage.connector.dbcp.defaultQueryTimeout=30000
有关完整列表,请参见BasicDataSource配置。
5、元数据存储表
-
段表
这是由druid.metadata.storage.tables.segments
酒店决定的。
该表存储有关系统中应该可用的段的元数据。(在文档的其他地方和整个项目中,这组段被称为“使用的段”。)协调器会轮询该表, 以确定系统中应可用于查询的段组。该表具有两个主要功能列,其他列用于索引目的。
该used
列中的值1 表示该段应由集群“使用”(即,段应被加载并可用于请求)。值0表示该段不应加载到集群中。我们将其作为从集群中卸载段而不实际删除其元数据的一种方法(如果出现问题,这将使回滚更为简单)。
该payload
列存储一个JSON Blob,该JSON具有该段的所有元数据(此负载中存储的某些数据与表中的某些列是多余的,这是有意的)。这看起来像
{
"dataSource":"wikipedia",
"interval":"2012-05-23T00:00:00.000Z/2012-05-24T00:00:00.000Z",
"version":"2012-05-24T00:10:00.046Z",
"loadSpec":{
"type":"s3_zip",
"bucket":"bucket_for_segment",
"key":"path/to/segment/on/s3"
},
"dimensions":"comma-delimited-list-of-dimension-names",
"metrics":"comma-delimited-list-of-metric-names",
"shardSpec":{"type":"none"},
"binaryVersion":9,
"size":size_of_segment,
"identifier":"wikipedia_2012-05-23T00:00:00.000Z_2012-05-24T00:00:00.000Z_2012-05-23T00:10:00.046Z"
}
请注意,此Blob的格式可能会并且会不时更改。
-
规则表用于存储有关段应降落在何处的各种规则。 在制定有关群集的分段(重新)分配决策时,协调器将使用这些规则。
-
config表用于存储运行时配置对象。我们还没有很多,我们不确定是否会继续使用这种机制,但这是在运行时更改整个集群中某些配置参数的方法的开端。
- 任务相关表
- 在管理任务时,Overlord和MiddleManager还创建和使用了许多表。
-
审核表用于存储配置更改的审核历史记录,例如,由协调器完成的规则更改以及其他配置更改。
##访问者:##
仅可通过以下方式访问元数据存储:
- 索引服务流程(如果有)
- 实时流程(如果有)
- 协调器流程
因此,您仅需要授予这些机器访问元数据存储的权限(例如,在AWS安全组中)。
六、ZooKeeper
Apache Druid使用Apache ZooKeeper(ZK)来管理当前集群状态。在ZK上发生的操作是
- Coordinator协调器领袖选举
- 细分历史记录中的 “发布”协议
- 协调器和历史记录之间的分段加载/删除协议
- Overlord领袖选举
- Overlord和MiddleManager任务管理
1、统筹领导人选举
我们使用Curator LeadershipLatch配方在路径上进行领导人选举
${druid.zk.paths.coordinatorPath}/_COORDINATOR
2、从“历史”和“实时”中细分“发布”协议
在announcementsPath
与servedSegmentsPath
被用于此目的。
所有“ 历史记录”进程都会在上发布自己announcementsPath
,具体来说,它们将在以下位置创建一个临时znode
${druid.zk.paths.announcementsPath}/${druid.host}
表示它们存在。他们还将随后在以下位置创建一个永久znode
${druid.zk.paths.servedSegmentsPath}/${druid.host}
并且当它们加载段时,它们将附加临时的znode,看起来像
${druid.zk.paths.servedSegmentsPath}/${druid.host}/_segment_identifier_
然后,诸如协调器和代理之类的流程可以观察这些路径,以查看当前哪些流程在为哪些细分市场提供服务。
3、协调器和历史记录之间的分段加载/删除协议
将loadQueuePath
用于此。
当协调器决定“ 历史”进程应加载或删除段时,它将一个临时znode写入以下内容:
${druid.zk.paths.loadQueuePath}/_host_of_historical_process/_segment_identifier
该znode将包含一个有效负载,该负载可指示Historical进程应如何处理给定的段。完成工作的“历史记录”过程后,它将删除znode以便向协调器表示已完成。