hdfs元数据实时采集

一、背景及问题

0.Hdfs元数据管理

 

1.背景介绍

当前在数据资产管理平台上,需要展示每张hive表及分区的热力情况(文件数、存储量、更新时间等信息)。目前热力数据包含两部分内容:热力元数据和审计日志,其中审计日志可以直接消费kafka得到,而热力元数据暂时没有可以直接获取的地方,需要我们这边主动采集。

目前已经完成一版采集方案,为离线定时同步采集(T+1),因实时性不满足需求,所以需要再寻找更加实时的采集方案。

2.面临的问题与挑战

问题与挑战:

量大
(1) 集群的目录和文件数(节点)多,Top20的集群目录和文件数均上亿(大部分在1-3亿个目录和文件),对存储有很大的挑战

(2)EditLog量大,Top20集群的EditLog的qps均在100以上,部分集群的qps在1000以上,对处理有很大的挑战

顺序性依赖
EditLog的日志处理逻辑需要依赖顺序,即一条消息处理完成,才能处理下一条消息,因此无法并行处理EditLog,只能串行处理,这就导致无法通过增加机器或cpu来提高处理能力。

近实时
业务上需要近实时的收集数据,延迟不能太大,目前可接受分钟级别的延迟。

二、解决思路及技术选型

1.探索的方案及遇到的问题

方案

方案描述

方案优势

方案问题

备注

离线同步(T+1)通过每天拉取一次FsImage,解析FsImage,得到FsImage生成时的Hdfs集群情况信息方案实现简单,目前平台已实现并将结果写入到hive表中,我们只需直接读取hive表即可

1.方案延迟大,无法满足实时性需求

2.不同集群的FsImage生成时间差距较大

基于内存的实时同步方案(nna)
 
对于每个集群,部署一个特殊的NameNode节点,此节点不参与集群事务性的操作,只是进行元数据收集工作,元数据存储到内存中可以实现实时采集

1.消耗的内存资源非常大,每个Hdfs集群均需要部署一台分析机器,并且元数据均存储在内存中,对于Top20的集群,每台机器的内存均需要上百G。

2.后期很难维护,公司现有八十多个Hdfs集群,就意味着我们也要申请八十多个机器与其对应

因资源问题,此方案pass
基于ElasticSearch的实时同步方案(nna的改版)与nna类似,对于每个集群,同样部署一个特殊的NameNode节点,但是元数据不存储到内存中,而是存储到ES中可以节省内存资源

ES性能跟不上,消息处理不过来

因性能问题,此方案pass
基于RocksDB+ElasticSearch的实时同步方案不再每个集群部署一台机器,而是统一拉取FsImage和EditLog,并且将元数据存储到RocksDB中,来降低内存开销以及提高性能,最终将聚合的结果数据批量写入到ES

1.可以满足近实时采集需求

2.不需要消耗大量的内存

3.可以满足性能要求

方案难度较大,实施过程中遇到很多坑

2.技术选型

(1) 数据存储 -> RocksDB

Hdfs集群元数据量大(上亿数据),性能要求高(每秒顺序读写上千次),内存资源有限。基于以上要求,现有的大部分数据库都很难满足,尤其是有网络开销的数据库,很难达到性能要求,而内存资源又有限,因此最好的选择是基于磁盘的单机数据库。

RocksDB 是由 Facebook 基于 LevelDB 开发的一款提供键值存储与读写功能的 LSM-tree 架构引擎。用户写入的键值对会先写入磁盘上的 WAL (Write Ahead Log),然后再写入内存中的跳表(SkipList,这部分结构又被称作 MemTable)。LSM-tree 引擎由于将用户的随机修改(插入)转化为了对 WAL 文件的顺序写,因此具有比 B 树类存储引擎更高的写吞吐。

(2) 数据序列化 -> Protobuf

因Hdfs集群元数据量大,因此即使存储到磁盘上,存储开销依然很大,并且RocksDB为了提高写入和查询性能,会将部分数据放到内存中,因此单条数据越小,对内存和磁盘的开销越小,因此需要找一个在性能和序列化后数据量均较好的序列化方案。

Protobuf具备了优秀的序列化协议的所需的众多典型特征:

1、标准的IDL和IDL编译器,这使得其对工程师非常友好。

2、序列化数据非常简洁,紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。

3、解析速度非常快,比对应的XML快约20-100倍。

4、提供了非常友好的动态库,使用非常简介,反序列化只需要一行代码。

(3) 状态同步 -> Zookeeper

方案在实现过程中,会涉及到producer、consumer、loader三种角色,不同角色之间会有协同工作(例如loader下载完FsImage后,producer才能开始拉取EditLog等),如何协同不同角色?此处选择用Zookeeper的监听机制实现,当一个角色完成事件后,另一个角色可以立即监听到并做后续事情。

三、方案介绍

1.整体设计

 

FsImage Loader:从Hdfs上Download FsImage到本地,解析后进行后续处理,写入RocksDB
EditLog Loader:每隔1分钟拉取一次EditLog,写入kafka
EditLog Consumer:消费kafka中的EditLog消息,写入RocksDB,及聚合后写入ES
状态同步:同步FsImage Loader、EditLog Loader、EditLog Consumer三者之间的协同
2.详细设计

总体流程如下

 

hdfs内部的元数据同步流程如下所示,以mkdir为例

 

从图中可以看到元数据会写一份到JournalNode,同时standy的节点可以访问这部分数据,拉取元数据主要就是根据这部分来进行的。

(1)FSImage加载及解析流程

先下载faimage文件,然后根据这个文件启动一个FsNameSystem,然后加载数据。原来的数据是加载到内存中的,是一个树,如下所示:

 

每个节点都会有个节点id,现在主要就是加载了file和Directory,其他的没怎么加载,暂时没有发现问题,后面发现问题了再改。为了少占用内存,我们是加载到rocksDb中的,其中最重要的就是treeIndex和detail两个库,里面节点的定位是以节点id来定位的。加载过程中会涉及到启停kafka的consumer和producer。这个过程会写es和rocksDb

(2)EditLog加载及解析流程

editlog加载是采用定期拉取远程Journal的editlog文件方式,一旦producer启动起来后,就会根据zk中的txid去Journal node拉取editlog文件,并将下一个txid写入到zk中,拉取的数据经过处理后会发送到kafka。

consumer端获取到数据后处理,这里获取的是op,op不会带节点的id,里面都是些绝对路径,所以就借用到了之前加载时库中的节点id,通过路径解析出id,然后进行目录的聚合等操作,然后再写会rocksDb,并写出到es。

(3)状态含义及管理

现在状态是各个角色分开管理的,loader、consumer、producer。主要是借用的zk的节点监控来回调触发各种操作的。虽然比之前用mysql来管理要方便了,但仍然感觉有些繁杂,后面可以优化成RPC,和选主一并实现。

(4)RocksDB及ES存储结构

RocksDB

RocksDb数据存储到本地磁盘中,通过不同的目录,存储不同的数据(此处称之为库),目前对于一个集群,会创建4个库:detail、treeIndex、txidIdempotent、middleData,每个库(目录)下存储RocksDB的数据文件,存储位置及结构如下:

 

detail库:key是节点id(Long类型,Hdfs集群生成),value是由type、fileSize等组成的信息,通过Protobuf序列化后存储。detail库中每条数据存储一个节点的明细信息,包括文件和目录。

treeIndex库:key是parentId|type|curPath,value是节点id,根据treeIndex库,可以快速的由Hdfs目录或文件全路径(path)找到对应的id。下面会详细介绍此索引。

txidIdempotent库:用于幂等判断,因为方案中使用了kafka,实现消息的ExactlyOnce比较难,因此此处加了幂等判断,即使用kafka AtLeastOnce。幂等库key是txid(hdfs集群生成),value为1,代表已处理过。

middleData库:为FsImage加载过程中存储临时数据使用,FsImage加载完成后删除。

ElasticSearch

ElasticSearch存储最终的结果数据,用于DAM后端查询使用。当前是每个集群创建一个索引,索引ID由hdfs的path拼接而成。FsImage和EditLog的数据,在处理完成后,经过聚合,得到对应Path的数据,先在内存中缓存一段时间(10s),然后批量写入到ElasticSearch中。

(5)树形结构索引

由上文可知,处理的数据均存储到detail库中,但detail库的key是id,而很多EditLog消息中并不会有id(例如OP_TIMES、RENAME等),这就需要根据Path能够快速获取到id,而treeIndex库就是为了此目的。

索引结构:

 

上图为索引结构示例图,索引数据最终以KV结构存储到rocksdb中,对于每条editlog中的path,通过索引可以查询到对应id,从而可以从detail库中取出具体数据进行操作。

(6)增量计算及数据聚合

1、新增文件op(op_close):detail插入一条,同时treeIndex也加入一条,同时对其所有的递归父目录,修改文件数和文件num

2、删除文件:和新增类似

3、rename文件或者目录:先删除再新增,是目录时需要递归调用

(7)数据序列化及反序列化

序列化和反序列化使用Protobuf,先写一个proto文件,然后安装一个proto程序生成一个java类,然后就可以调用里面得方法对java对象进行序列化和反序列化了
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值