乐趣区

关于大数据:理-Druid-元数据之乱

vivo 互联网大数据团队 -Zheng Xiaofeng

一、背景

Druid 是一个专为大型数据集上的高性能切片和 OLAP 剖析而设计的数据存储系统。

因为 Druid 可能同时提供离线和实时数据的查问,因而 Druid 最罕用作为 GUI 剖析、业务监控、实时数仓的数据存储系统。

此外 Druid 领有一个多过程,分布式架构,每个 Druid 组件类型都能够独立配置和扩大,为集群提供最大的灵活性。

因为 Druid 架构设计和数据(离线,实时)的特殊性,导致 Druid 元数据管理逻辑比较复杂,次要体现在 Druid 具备泛滥的元数据存储介质以及泛滥不同类型组件之间元数据传输逻辑上。

本文的目标是通过梳理 Druid 元数据管理这个侧面从而进一步理解 Druid 外部的运行机制。

二、Druid 元数据相干概念

2.1 Segment

Segment 是 Druid 治理数据的最根本单元,一个 Datasource 蕴含多个 Segment,每个 Segment 保留着 Datasource 某个时间段的数据,这个特定时间段的数据组织形式是通过 Segment 的 payload(json)来定义的,payload 外部定义了某个 Segment 的维度,指标等信息。

同一个 Datasource 的不同 Segment 的 payload 信息(维度、指标)能够不雷同,Segment 信息次要蕴含上面几局部:

  • 【时间段(Interval)】:用于形容数据的开始工夫和完结工夫。
  • 【DataSource】: 用字符串示意,指定 segment 隶属于哪个 Datasource。
  • 【版本(Version)】:用一个工夫示意,时间段(Interval)雷同的 Segment,版本高的 Segment 数据可见,版本低的 Segment 会被删除掉。
  • 【Payload 信息】:次要蕴含了此 Segment 的维度和指标信息,以及 Segment 数据存在 DeepStorage 地位信息等等。

segment 次要组成部分

segment 外部数据样例

2.2 Datasource

Datasource 相当于关系型数据库的表,Datasource 的 Schema 是依据其可用的 Segment 动态变化的,如果某个 Datasource 没有可用的 Segment(used=1),在 druid-web 的 Datasource 列表界面和查问界面看不到这个 Datasource。

元数据库中 druid\_dataSource 表并没有保留 Schema 信息,只保留了该 Datasource 对应 实时工作生产数据的偏移量信息,都说 Druid 的 Datasource 相当于关系型数据库的表,然而 Druid 中表(Datasource)Schema 信息,并不是定义在 druid\_dataSource 元数据表里。

那么在 druid-web 页面上看到的 Datasource 的 Schema 信息是怎么来的呢?

其实它是实时依据该 Datasource 下所有 Segment 元数据信息合并而来,所以 DataSource 的 Schema 是实时变动的,

这样设计的益处是很好的适应了 Datasource 维度一直变动的需要在:

Schema 的合并过程 

2.3 Rule

Rule 定义了 Datasource 的 Segment 留存规定,次要分两大类:Load 和 Drop。

  • Load 示意 Segment 保留策略。
  • Drop 示意 Segment 删除策略。

Load/Drop 规定均有三个子类,别离是 Forever Load/Drop,Interval Load/Drop 以及 Period Load/Drop,一个 Datasource 蕴含 1 个或多个 Rule 规定,如果没有定义 Rule 规定就应用集群的 Default Rule 规定。

Datasource Rule 规定列表是有序的(自定义规定在后面,集群默认规定在前面),在运行 Run 规定时,会对该 Datasource 下所有可用的 Segment 信息,依照 Run 规定的先后顺序进行判断,只有 Segment 满足某个 Rule 规定,前面的规定 Rule 就不再运行(如图:Rule 解决逻辑案例)。Rule 规定次要蕴含上面几局部信息:

  • 【类型】:类型有删除规定和加载规定。
  • 【Tier 和正本信息】:如果是 Load 规定,须要定义在不同 Tier 的 Historical 机器正本数。
  • 【工夫信息】:删除或加载某个时间段的 Segment。

Rule 样例如下:

[
   {
   "period": "P7D",
   "includeFuture": true,
   "tieredReplicants": {
     "_default_tier": 1,
     "vStream":1
   },
   "type": "loadByPeriod"
 },
 {"type": "dropForever"}
 ]

Rule 解决逻辑案例

2.4 Task

Task 次要用于数据的摄入(本文次要探讨实时摄入 kafka 数据的工作),在 Task 的运行过程中,它会依据数据工夫列产生一个或者多个 Segment,Task 分为实时和离线工作。

实时工作(kafka)是 Overload 过程依据 Supervisor 定义主动生成;
离线工作(类型:index\_hadoop,index\_parallel)则须要内部零碎通过拜访接口方式提交。

每个工作次要蕴含上面几局部信息:

  • 【dataSchema】:定义了该工作生成的 Segment 中有哪些维度(dimensionsSpec),指标(metricsSpec),工夫列(timestampSpec),Segment 粒度(segmentGranularity),数据聚合粒度(queryGranularity)。
  • 【tuningConfig】:工作在摄入数据过程中的优化参数(包含 Segment 生成策略,索引类型,数据抛弃策略等等),不同的工作类型有不同的参数设置。
  • 【ioConfig】:定义了数据输出的源头信息,不同的数据源配置项有所不同。
  • 【context】:对于工作全局性质的配置,如工作 Java 过程的 option 信息。
  • 【datasource】:示意该工作为那个 Datasource 结构 Segment。

实时工作生成 Segment 案例

2.5 Supervisor

Supervisor 用于治理实时工作,离线工作没有对应的 Supervisor,Supervisor 与 Datasource 是一对一的关系,在集群运行过程中 Supervisor 对象由 Overlord 过程创立,通过 Overlord 接口提交 Supervisor 信息后,会在元数据库(MySQL)中长久化,Supervisor 内容与 Task 类似,能够认为实时 Task 是由 Supervisor 克隆进去的。

三、Druid 整体架构

后面抽象地介绍了 Druid 元数据相干概念,为了深刻的理解 Druid 元数据,先从宏观的角度认识一下 Druid 的整体架构。

能够形象地把 Druid 集群类比为一家公司,以 Druid 不同组件类比这家公司中不同类型员工来介绍 Druid 集群,Druid 组件大体能够分为三类员工:领导层,车间员工和销售员工,如下图:

Druid 组件分类

  • 领导层: 领导依据内部市场需求(Overlord 接管内部摄入工作申请),而后把生产工作下发到对应的职业经理人(MiddleManager),职业经理人治理团队(MiddleManager 启动 Peon 过程),下发具体生产工作给不同类型的员工(Peon 过程)。
  • 车间员工:生产员工(Peon)负责生产产品(segment),仓库管理员(Coordinator)负责把生产进去的产品(segment)调配到仓库(Historical)中去。
  • 销售员工: 销售员(Broker)从生产员工(Peon)获取最新的产品(segment),从仓库中获取原来生产的产品(segment),而后把产品整顿打包(数据进一步合并聚合)之后交给顾客(查问用户)。

下面通过类比公司的形式,对 Druid 集群有了初步的整体印象。

上面具体介绍 Druid 集群架构,Druid 领有一个多过程,分布式架构,每个 Druid 组件类型都能够独立配置和扩大,为集群提供最大的灵活性。

一个组件的中断不会立刻影响其余组件。

上面咱们简要介绍 Druid 各个组件在集群中起到的作用。

Druid 架构

  • Overlord

    Overlord 负责接受任务、协调工作的调配、创立工作锁以及收集、返回工作运行状态给调用者。当集群中有多个 Overlord 时,则通过选举算法产生 Leader,其余 Follower 作为备份。

  • MiddleManager

    MiddleManager 负责接管 Overlord 调配的实时工作,同时创立新的过程用于启动 Peon 来执行实时工作,每一个 MiddleManager 能够运行多个 Peon 实例,每个实时 Peon 既提供实时数据查问也负责实时数据的摄入工作。

  • Coordinator

    Coordinator 次要负责 Druid 集群中 Segment 的治理与公布(次要是治理历史 Segment),包含加载新 Segment、抛弃不合乎规定的 Segment、治理 Segment 正本以及 Segment 负载平衡等。如果集群中存在多个 Coordinator Node,则通过选举算法产生 Leader,其余 Follower 作为备份。

  • Historical

    Historical 的职责是负责加载 Druid 中非实时窗口内且满足加载规定的所有历史数据的 Segment。每一个 Historical Node 只与 Zookeeper 放弃同步,会把加载实现的 Segment 同步到 Zookeeper。

  • Broker

    Broker Node 是整个集群查问的入口,Broker 实时同步 Zookeeper 上保留的集群内所有已公布的 Segment 的元信息,即每个 Segment 保留在哪些存储节点上,Broker 为 Zookeeper 中每个 dataSource 创立一个 timeline,timeline 依照工夫程序形容了每个 Segment 的寄存地位。

每个查问申请都会蕴含 dataSource 以及 interval 信息,Broker 依据这两项信息去查找 timeline 中所有满足条件的 Segment 所对应的存储节点,并将查问申请发往对应的节点。

四、Druid 元数据存储介质

Druid 依据本身不同的业务须要,把元数据存储在不同的存储介质中,为了晋升查问性能,同时也会将所有元数据信息缓存在内存中。把历史数据的元数据信息保留到元数据库(MySQL),以便集群重启时复原。

因为 Druid 领有一个多过程,分布式架构,须要应用 Zookeeper 进行元数据传输,服务发现,主从选举等性能,并且历史节点会把 Segment 元数据信息存储在本地文件。

那么历史节点(Historical)为什么会把该节点加载的 Segment 元数据信息缓存在本人节点的本地呢?

是因为在历史节点产生重启之后,读取 Segment 的元数据信息不必去 Mysql 等其余元数据存储介质进行跨节点读取而是本地读取,这样就极大地晋升了历史节点数据的复原效率。

上面别离介绍这些存储介质(内存、元数据库、Zookeeper、本地文件)里的数据和作用。

4.1 元数据库(MySQL)

MySQL 数据库次要用于长期长久化 Druid 元数据信息,比方 segment 局部元数据信息存在 druid\_segments 表中,历史的 Task 信息存在 druid\_tasks,Supervisor 信息存储在 druid_supervisors 等等。

Druid 局部服务过程在启动时会加载元数据库长久化的数据,如:Coordinator 过程会定时加载表 druid\_segments 中 used 字段等于 1 的 segment 列表,Overlord 启动时会主动加载 druid\_supervisors 表信息,以复原原来实时摄入工作等等。

MySQL 元数据库表

4.2  Zookeeper

Zookeeper 次要存储 Druid 集群运行过程中实时产生的元数据,Zookeeper 数据目录大略能够分为Master 节点高可用、数据摄入、数据查问 3 类目录

上面介绍 Druid 相干 Zookeeper 目录元数据内容。

 Zookeeper 元数据节点分类 

4.2.1  Master 节点高可用相干目录

${druid.zk.paths.base}/coordinator: coordinator 主从高可用目录,有多个长期有序节点 编号小的是 leader。

${druid.zk.paths.base}/overlord: overlord 主从高可用目录,有多个长期有序节点 编号小的是 leader。

4.2.2  数据查问相干目录

${druid.zk.paths.base}/announcements:只存储 historical,peon 过程的 host:port,没有 MiddleManager,broker,coodinator 等过程信息,用于查问相干节点服务发现。

${druid.zk.paths.base}/segments:以后集群中能被查问到的 segment 列表。目录构造:historical 或 peon 的 host:port/${segmentId},Broker 节点会实时同步这些 Segment 信息,作为数据查问的重要依据。

4.2.3  数据摄入相干目录

${druid.zk.paths.base}/loadQueue: Historical 须要加载和删除的 segment 信息列表(不止只有加载),Historical 过程会监听这个目录下本人须要解决的事件(加载或删除),事件实现之后会被动删除这个目录下的事件。

${druid.zk.paths.indexer.base}=${druid.zk.paths.base}/indexer:对于摄入工作数据的 base 目录。

${druid.zk.paths.indexer.base}/announcements:保留以后存活 MiddleManager 列表,留神 historical,peon 列表不在这里,这里只存储摄入相干的服务信息,用于数据摄入相干节点服务发现。

${druid.zk.paths.indexer.base}/tasks Overlord 调配的工作信息放在这个目录(MiddleManager 的 host:port/taskInfo),等工作在 MiddleManager 上运行起来了,工作节点信息将被删除。

${druid.zk.paths.indexer.base}/status:保留工作运行的状态信息,Overlord 通过监听这个目录获取工作的最新运行状态。

4.3 内存

Druid 为了晋升元数据拜访的效率会把元数据同步到内存,次要通过定时 SQL 查问拜访形式同步 MySQL 元数据或者应用 Apache Curator Recipes 实时同步 Zookeeper 上的元数据到内存如下图。

每个过程中的元数据不一样,上面一一介绍一下各个角色过程缓存了哪些数据。

Druid 过程元数据同步形式

4.3.1 Overlord

实时同步 Zookeeper 目录(${druid.zk.paths.indexer.base}/announcements)下的数据,应用变量 RemoteTaskRunner::zkWorkers(类型:Map)存储,每 ZkWorker 对应一个 MM 过程,在 ZkW orker 对象中会实时同步 Zookeeper 目录(${druid.zk.paths.indexer.base}/status/${mm_host:port})工作信息,应用 RemoteTaskRunner::runningTasks 变量存储。

默认每分钟同步数据库中 druid_tasks active = 1 的数据,应用变量 TaskQueue::tasks(类型:List)存储,在同步时会把内存中的 Task 列表与最新元数据里的 Task 列表进行比拟,失去新增的 task 列表和删除的 task 列表,把新增的 Task 加到内存变量 TaskQueue::tasks,清理掉将要被删除的 task

4.3.2 Coordinator

默认每 1 分钟同步元数据库中 druid_segemtns 中列 used= 1 的 segment 列表到变量 SQLMetadataSegmentManager::dataSourcesSnapshot。

默认每 1 分钟同步元数据库 druid_rules 表信息到 SQLMetadataRuleManager::rules 变量

应用 CoordinatorServerView 类(前面会介绍)实时同步 ${druid.zk.paths.base}/announcements,${druid.zk.paths.base}/segments 的数据,用于与元数据库中的 segment 比照,用来判断哪些 segment 应该加载或删除。

4.3.3 Historical

会实时同步 ${druid.zk.paths.base}/loadQueue/${historical_host:port} 下的数据, 进行 segment 的加载与删除操作, 操作实现之后会被动删除对应的节点。

Historical 通过上报 segment 信息到 ${druid.zk.paths.base}/segments 来裸露 segment。

4.3.4 MiddleManager

会实时同步 ${druid.zk.paths.indexer.base}/tasks/${mm_host:port}的数据,进行工作(peon)过程的启动,启动实现之后会被动删除对应的节点。

MiddleManager 上报 segment 信息到 ${druid.zk.paths.base}/segments 来裸露 segment。

4.3.5 Broker

应用 BrokerServerView 类实时同步 ${druid.zk.paths.base}/announcements,${druid.zk.paths.base}/segments 的数据,构建出整个零碎的时间轴对象(BrokerServerView::timelines)作为数据查问的根本根据。同步过程中类的依赖关系如下图。

上层的类对象应用监听下层类对象的形式感知 sement 的增删改,并做相应的逻辑解决,会同时监听 ${druid.zk.paths.base}/announcements 和 ${druid.zk.paths.base}/segments 的数据的数据变动,通过回调监听器的形式告诉到上层类对象。

zk 中 segment 同步到 Druid 过程过程中对象之间的监听关系

4.4 本地文件

本地文件的元数据次要用于复原单个节点时读取并加载。

例如:Historical 节点第一个数据目录下的 info\_dir 目录(如:/data1/druid/segment-cache/info\_dir),保留了该节点加载的所有 segment 信息,在 Historical 过程重启时会读取该目录下的 segment 元数据信息,判断本地是否有该 segment 的数据,如果没有就去深度存储系统(hdfs)下载,数据下载实现后会上报 segment 信息到 Zookeeper(门路:${druid.zk.paths.base}/segments)。

五、Druid 元数据相干业务逻辑

因为 Druid 组件类型比拟多,业务逻辑比较复杂,从整体到部分形式,从宏观到细节,循序渐进地理解 Druid 的业务逻辑,以便理解 Druid 元数据在业务逻辑中施展的作用。

5.1 Druid 元数据整体业务逻辑

后面从整体理解了 Druid 集群各个组件的协作关系,上面别离从摄入工作治理、数据摄入、数据查问三个方面的业务逻辑来梳理元数据在 Druid 集群所起的作用。

5.1.1 摄入工作治理

摄入数据之前须要用户提交摄入工作,Overlord 依据工作的配置会相应命令 MiddlerManager 启动该工作的相干过程(peon 过程)用于摄入数据,具体流程如下图中数据序号程序执行。

工作提交与治理

上面别离依照上图中数字序号程序介绍 Druid 外部对于工作治理的业务逻辑:

① Overlord 过程收到工作提交申请之后,会把工作信息写入 druid_tasks 表,此时字段 active 等于 1。
② Overlord 分配任务给特定的 MiddleManager 节点,并把 task 信息写入 Zookeeper 目录(${druid.zk.paths.indexer.base}/tasks)下。
③ MiddleManager 过程监听以后节点在 Zookeeper 目录(${ruid.zk.paths.indexer.base}/task)须要启动的 task 信息。
④ MiddleManager 会以 fork 的形式启动 Peon 过程(task)此时 Peon 过程开始摄入数据,并把工作 Running 状态写入 Zookeeper 目录(${ruid.zk.paths.indexer.base}/status)。
⑤ Overlord 会实时监听 Zookeeper 目录(${ruid.zk.paths.indexer.base}/status)获取工作运行最新状态。
⑥ 工作实现后 Overlord 会把 task 状态信息更新到数据库表 druid_tasks,此时字段 active=0。

5.1.2 数据摄入逻辑

Druid 数据摄入逻辑

上面别离依照上图中数字序号程序介绍 Druid 外部对于数据摄入的业务逻辑:

① Peon 过程在本地生产 segment 之后,会上传 segment 数据到深度存储 Hdfs。
② 插入一条 segment 元数据信息到元数据 druid_segments 表中,包含 segment 数据 hdfs 地址,Interval 信息,留神此时 used 字段为 1。
③ Coordinator 过程定时拉取 druid_segments 表中 used 为 1 的数据。
④ Coordinator 过程把 segment 调配信息写入 Zookeeper 目录:${druid.zk.paths.base}/loadQueue。
⑤ HIstorical 过程监听以后节点在 Zookeeper 目录(${druid.zk.paths.base}/loadQueue)获取须要加载的 segment 信息。
⑥ 从 Hdfs 下载 segment 数据,加载 segment。
⑦把已加载的 segment 的元数据信息同步到 Zookeeper 目录(${druid.zk.paths.base}/segments)。

5.1.3 数据查问逻辑

数据查问次要波及到 Peon、Historical,Broker 三个角色,Broker 会依据 client 的查问申请中蕴含的 dataSource 和 interval 信息,筛选出须要查问的 segment,而后 Broker 作为客户端从 Peon 获取实时数据,从 Historical 获取历史数据,再依据查问要求,将两局部数据进一步聚合,如下图:

Druid 数据查问逻辑

5.2 Druid 元数据具体业务逻辑

有了后面对 Druid 集群整体意识之后,上面更为粗疏的探讨 Druid 元数据在各个组件之间施展的作用。

如下图虚线箭头示意元数据的传输,上面依照图中数字序号介绍每个虚线箭头两端组件与元数据存储介质(MySQL、Zookeeper)之间的元数据,每条具体从组件对元数据存储介质蕴含读和写两方面来介绍,如下:

Druid 元数据业务逻辑

① :启动工作时写入 task 信息,提交实时工作时写入 supervisor 信息。读:broker 调用 overlord 接口时会查问不同状态下的 task 信息,过程重启时复原 supervisor 信息。
② :分配任务到 MiddleManager 时,写入工作信息。读:同步正在运行工作的状态信息。
③ :写入以后节点工作状态信息到 Zookeeper,读:读取带启动或终止工作信息。
④ :工作启动后上报实时 segment 信息。
⑤ :coordinator 定时读取字段 used= 1 的 segment 列表信息。
 写:coordinator 调配的 segment 信息,读:已调配的 segment 列表信息。
⑦ :已加载实现的 segment 信息,读:须要加载的 segment 信息。
⑧ :加载实现的 segment 信息,作为数据查问的根据。

六、总结

后面以整体到部分、形象到细节的形式从四个方面(Druid 元数据基本概念、Druid 整体架构、Druid 元数据存储介质 Druid 元数据相干业务逻辑)介绍了 Druid 元数据在 Druid 集群中表演的角色。

Druid 领有一个多过程,分布式架构,每个组件只关注本人的业务逻辑和元数据,通过 RPC 通信或 Zookeeper 进行组件之间的解耦,每个 Druid 组件类型都能够独立配置和扩大,极大提供集群的灵活性,以至于一个组件的中断不会立刻影响其余组件,上面对 Druid 元数据做一个总结:

  • Druid 元数据存储介质有内存、元数据库(MySQL)、Zookeeper、本地文件。
  • 元数据库(MySQL)和本地的元数据起到备份、长久化的作用。Zookeeper 次要起到元数据传输桥梁,实时保留元数据的作用,同时把元数据同步到内存,极大晋升了 Druid 数据查问和数据摄入的性能,而本地文件的元数据次要用于复原单个节点时疾速读取并加载到内存。
  • 在 Druid 组件过程中会把 Zookeeper 和元数据库(MySQL)里的元数据别离通过实时同步和定时拉取的形式同步到过程的内存中,以进步拜访效率。
  • 保留在各个组件过程中内存的元数据才是以后集群中最新最全的元数据。
退出移动版