Apache Druid 中文翻译 - 设计(Design)

设计

德鲁伊具有多进程、分布式的架构,并且在设计为云友好且易操作的系统。每种Druid进程类型(指Druid框架之中的各种组件)都可以独立配置和水平扩展,从而为您的集群提供最大的灵活性。这种设计还提高了容错能力:一个组件的故障不会立即影响其他组件。

进程和服务器

Druid有几种进程类型,简述如下:

  • Coordinator 协调器进程管理群集上的数据可用性。
  • Overlord 统治进程控制数据摄入工作负载的分配。
  • Broker 代理进程处理来自外部客户端的查询。
  • Router 路由进程是可选进程,可以将请求路由到代理,协调器和霸主。
  • Historical 历史进程存储可查询的数据。
  • MiddleManager 中间管理进程负责摄取数据。

Druid进程可以按照您喜欢的任何方式进行部署,但是为了便于部署,我们建议将其组织为三种服务器类型:主服务器,查询服务器和数据服务器。

  • Master:运行协调器和统治流程,管理数据可用性和接收。
  • Query:运行代理和可选的路由器进程,处理来自外部客户端的查询。
  • Data:运行Historical和MiddleManager进程,执行提取工作负载并存储所有可查询的数据。

有关进程和服务器架构的更多详细信息,请参阅Druid进程和服务器

外部依赖

除了内置的进程类型外,Druid还具有三个外部依赖项。这些外部依赖项能够利用现有的基础结构。

Deep storage 深度储存

每个Druid服务器均可访问共享文件存储。在集群部署中,这通常将是分布式对象存储(例如S3或HDFS)或通过网络挂载的文件系统。在单服务器部署中,这通常是本地磁盘。Druid使用deep storage来存储已摄取到系统中的所有数据。

Druid仅将deep storage用作数据的备份,并作为在Druid进程之间在后台传输数据的方式(注:多个Druid实例可以通过Deep Storage 共享数据)。处理查询请求的时候,历史进程不会从深度存储中读取数据,而是会在提供任何查询之前从其本地磁盘读取预摄入的segments 。这意味着Druid不需要在查询期间访问deep storage,从而帮助它提供最短的查询延迟。这也意味着在deep storage和 Historical进程中,必须有足够的磁盘空间用于加载你计划摄取的数据。

深度存储是Druid的弹性,容错设计的重要组成部分。即使每台数据服务器都丢失并重新配置,Druid也可以从深度存储中引导。

有关更多详细信息,请参阅深度存储页面。

Metadata storage 元数据存储

Metadata storage区包含各种共享的系统元数据,例如segment 可用性信息和task信息。在集群部署中,这通常将是传统的RDBMS,例如PostgreSQL或MySQL。在单服务器部署中,它通常将是本地存储的Apache Derby数据库。

有关更多详细信息,请参见“ 元数据存储”页面。

ZooKeeper

用于内部服务发现,协调和leader选举。

有关更多详细信息,请参见ZooKeeper页面。

Architecture diagram 架构图

下图显示了使用建议的Master / Query / Data服务器组织的查询和数据在整个体系结构中的流动方式:

 

存储设计

Datasources 数据源segments段

Druid数据存储在“Datasources”中,类似于传统RDBMS中的表。每个数据源都按时间分区,并且可以选择按其他属性进一步分区。每个时间范围都称为“chunk”(例如,如果您的数据源按天划分,则为一天)。在一个chunk内,数据被划分为一个或多个 segments。每个segments都是单个文件,通常包含多达几百万行的数据。由于segments是按时间块组织的,因此将细分视为生活在时间线上的以下内容有时会有所帮助:

 

 

一个Datasources 可能具有从几个段到数十万甚至数百万个segments。每个segments都是从在MiddleManager上创建开始的,在那个时候,segments是可变的且未提交的。为了生成紧凑且支持快速查询的数据文件,segments构建过程包括以下步骤,:

  • 转换为列格式
  • 使用位图索引编制索引
  • 使用各种算法进行压缩
    • 字符串列的ID存储最小化的字典编码
    • 位图索引的位图压缩
    • 所有列的类型感知压缩

segments会定期提交和发布。发布后,它们被写入Deep storage ,变得不可变,并从MiddleManagers迁移到Historical处理。有关该segment的Entry也将写入到元数据存储中。Entry是有关该segment的元数据的自描述位,包括诸如segment的模式,其大小以及其在Deep storage上的位置之类的信息。这些条目是Coordinator用来了解集群上应该有哪些数据的内容。

有关segments文件格式的详细信息,请参阅段文件

有关在Druid中对数据建模的详细信息,请参见模式设计

Indexing 索引 和handoff移交

索引 是创建新段的机制,移交是它们被发布并开始由Historical进程提供服务的机制。该机制在索引端的工作方式如下:

  1. 一个索引任务开始运行,并建立一个新的segments。它必须在开始构建段之前确定该段的标识符。对于要追加的任务(例如Kafka任务,或追加(append)模式中的索引任务),可以通过在Overlord上调用“allocate” API来完成,以潜在地将新分区(partition )添加到现有的段集中。对于要覆盖(overwriting )的任务(例如Hadoop任务或不在追加模式下的索引任务),这可以通过锁定间隔(注:覆盖的时间段)并创建新的版本号和新的段集来完成。
  2. 如果索引任务是实时任务(例如Kafka任务),则此时可以立即查询该段。它可用,但尚未发布。
  3. 索引任务完成对段的数据读取后,会将其推入深度存储,然后通过将记录写入元数据存储来发布它。
  4. 如果索引任务是实时任务,则此时它等待“Historical”进程加载该段。如果索引任务不是实时任务,它将立即退出。

在Coordinator / Historical端,看起来像这样:

  1. Coordinator 定期(默认情况下,每1分钟)轮询元数据存储区以查找新发布的段。
  2. 当Coordinator 找到已发布和使用但不可用的段时,它会选择一个“Historical”进程并指示“Historical”来加载该段
  3. 历史记录会加载段并开始处理。
  4. 此时,如果索引任务正在等待切换,它将退出。

Segment identifiers 段标识符

段均具有包含以下部分的四部分标识符:

  • 数据源名称。
  • 时间间隔(对于包含分段的时间块;这对应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中,如果您要做的只是追加(append)数据,那么每个时间块只有一个版本。但是,当您覆盖数据时,在幕后发生的事情是使用相同的数据源,相同的时间间隔,但版本号更高的方式创建了一组新的段。这向Druid系统的其余部分发出信号,表明应从集群中删除较旧的版本,而应使用新版本替换它。

对于用户而言,切换似乎是瞬间发生的,因为Druid通过首先加载新数据(但不允许对其进行查询)来处理此问题,然后在所有新数据加载完毕后,立即切换所有新查询以使用这些新段。然后,它会在几分钟后删除旧段。

段生命周期

每个部分的生命周期都涉及以下三个主要领域:

  1. 元数据存储区:一旦构建完段,就将段元数据(一个小的JSON有效负载(payload ),通常不超过几个KB)存储在 元数据存储区中。将段的记录插入元数据存储的操作称为发布。这些元数据记录具有一个名为used的布尔标志,用于控制该段是否可查询。由实时任务创建的段将在发布之前可用(available),因为它们仅在段完成时才发布,并且不接受任何其他数据行。
  2. 深度存储:分段数据构建完成后,会将分段数据文件推送到深度存储。这在将元数据发布到元数据存储之前立即发生。
  3. 查询的可用性:段可用于在某些Druid数据服务器上进行查询,例如实时任务或历史进程。

您可以使用Druid SQL sys.segments检查当前活动段的状态 。它包括以下标志:

  • is_published:如果段元数据已发布到存储的元数据中,used为true。
  • is_available:如果该段当前可用于实时任务或历史流程查询,则为True。
  • is_realtime:如果分段在实时任务上可用,则为true 。对于使用实时提取的数据源,通常会先开始true,然后false随着段的发布和移交而变成。
  • is_overshadowed:如果该段已发布(used设置为true)并且被其他已发布的段完全遮盖,则为true。通常,这是一个过渡状态,处于此状态的段很快就会将其used标志自动设置为false。

查询处理

查询首先进入Broker,Broker将在其中识别哪些段具有与该查询有关的数据。段列表始终按时间筛选,也可以根据其他属性来筛选,这取决于数据源的分区方式。然后,Broker将确定哪些Historical 和 MiddleManager为这些段提供服务,并将重写的子查询发送给每个进程。Historical / MiddleManager进程将接受查询,对其进行处理并返回结果。Broker接收结果并将它们合并在一起以得到最终响应,并将其返回给原始调用方。

Broker筛选是Druid限制每个查询必须扫描的数据量的一种重要方法,但这不是唯一的方法。对于比Broker可以使用的过滤器更细粒度的过滤器,每个段内的索引结构允许Druid在查看任何数据行之前先找出哪些行(如果有)与过滤器集匹配。一旦Druid知道哪些行与特定查询匹配,它就只会访问该查询所需的特定列。在这些列中,Druid可以在行与行之间跳过,从而避免读取与查询过滤器不匹配的数据。

因此,Druid使用三种不同的技术来最大化查询性能:

  • 筛选每个查询所需要访问的段。
  • 在每个段中,使用索引来标识必须访问的行。
  • 在每个段中,仅读取与特定查询相关的特定行和列。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章