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)里的元数据别离通过实时同步和定时拉取的形式同步到过程的内存中,以进步拜访效率。
- 保留在各个组件过程中内存的元数据才是以后集群中最新最全的元数据。