分析hdfs文件变化及监控小文件

1、文档编写目的

目前各个企业都在利用Hadoop大数据平台,每天都会通过ETL产生大量的文件到hdfs上,如何有效的去监测数据的有效性,防止数据的无限增长导致物理资源跟不上节奏,我们必须控制成本,让有限的资源发挥大数据的极致功能。本文介绍如何去分析hdfs上的文件变化情况,以及老生常谈的小文件的监控情况的一种实现方式。

2、实现方式说明

本次分析方案有两种:1、利用hdfs的api文档,通过hdfs实例的listStatus方法递归出hdfs上所有的文件及目录的具体情况,包括path、ower、size等重要属性。然后将这些数据写到本地文件中,上传到hdfs上,然后在hive上建一个外表来映射这些数据,最后利用sql进行各种分析;2、第二种方式主要是在获取源数据时跟第一种不同,这次采用的是hdfs自带的分析fsimage文件的命令hdfs oiv -i + fsimage文件 -o +输出文件 -p Delimited,该命令将fsimage文件解析成可阅读的csv文件,后续操作跟第一种一样都是上传到hdfs建外表用sql来分析各种指标。下面主要讲一下第一种方式。

3、代码讲解

3.1 第一种用java代码通过hdfs的api文档获取完整数据

package com.mljr.hdfs;
import java.io.*;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class HdfsStatus {
    public static void main(String[] args) {
        FileSystem hdfs = null;
        try{
            Configuration config = new Configuration();
            config.set("fs.default.name", "nameservice1");
            hdfs = FileSystem.get(new URI("nameservice1"),//主节点ip或者hosts
                    config, "hdfs");
            Path path = new Path("/");//这里定义从hdfs的根节点开始计算
            String content_csv = "/tmp/content.csv";
            long startTime=System.currentTimeMillis();   //获取开始时间
            BufferedOutputStream out =new BufferedOutputStream(new FileOutputStream(new File(content_csv)));
            iteratorShowFiles(hdfs, path,out);
            out.close();
            long endTime=System.currentTimeMillis(); //获取结束时间
            long runTime = (endTime-startTime)/1000/60;
            System.out.println("程序运行时间: "+runTime+"min");

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(hdfs != null){
                try {
                    hdfs.closeAll();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     *
     * @param hdfs FileSystem 对象
     * @param path 文件路径
     */
    public static void iteratorShowFiles(FileSystem hdfs, Path path,BufferedOutputStream out){


        String line = System.getProperty("line.separator");
        try{
            if(hdfs == null || path == null){
                return;
            }
            //获取文件列表
            FileStatus[] files = hdfs.listStatus(path);
            //创建输出文件
            //展示文件信息
            for (int i = 0; i < files.length; i++) {
                try{
                    if(files[i].isDirectory()){
                        String text = (files[i].getPath().toString().replace("hdfs://nameservice1","")
                                + "," + files[i].getOwner()
                                + "," + "0"
                                + "," + "0"
                                + "," + files[i].getBlockSize()
                                + "," + files[i].getPermission()
                                + "," + files[i].getAccessTime()
                                + "," + files[i].getModificationTime()
                                + "," + files[i].getReplication()+line);
                        out.write(text.getBytes());
                        //递归调用
                        iteratorShowFiles(hdfs, files[i].getPath(),out);
                    }else if(files[i].isFile()){
                        String text=files[i].getPath().toString().replace("hdfs://nameservice1","")
                                + "," + files[i].getOwner()
                                + "," + "1"
                                + "," + files[i].getLen()
                                + "," + files[i].getBlockSize()
                                + "," + files[i].getPermission()
                                + "," + files[i].getAccessTime()
                                + "," + files[i].getModificationTime()
                                + "," + files[i].getReplication()+line;
                        out.write(text.getBytes());
                    }
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

接下来就是将本地的文件上传到hdfs上,然后建hive外表

#!/bin/bash
source /etc/profile
cd /home/dmp/hdfs

#生成hdfs目录文件和节点信息
java -cp ./HdfsStatus-1.0-SNAPSHOT.jar com.mljr.hdfs.HdfsStatus
#将文件上传到hdfs(hdfs目录需要提前创建好)
hadoop fs -rm -r /tmp/dfs/content/content.csv /tmp/dfs/nodes/nodes.csv
hadoop fs -put /tmp/content.csv /tmp/dfs/content

接下来就是sql分析了

#建外表
CREATE EXTERNAL TABLE `default.hdfs_info`(
  `path` string, 
  `owner` string, 
  `is_dir` string, 
  `filesize` string, 
  `blocksize` string, 
  `permisson` string, 
  `acctime` string, 
  `modificatetime` string, 
  `replication` string)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' 
WITH SERDEPROPERTIES ( 
  'field.delim'=',', 
  'serialization.format'=',') 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  'hdfs://nameservice1/tmp/dfs/content'

#sql分析一级目录大小
select joinedpath, sumsize
from 
(
select joinedpath,round(sum(filesize)/1024/1024/1024,2) as sumsize
from
(select concat('/',split(path,'\/')[1]) as joinedpath,accTime,filesize,owner 
from default.hdfs_info
)t
group by joinedpath
)h
order by sumsize desc

#sql分析二级目录大小
select joinedpath, sumsize
from 
(
select joinedpath,round(sum(filesize)/1024/1024/1024,2) as sumsize
from
(select concat('/',split(path,'\/')[1],'/',split(path,'\/')[2]) as joinedpath,accTime,filesize,owner 
from default.hdfs_info
)t
group by joinedpath
)h
order by sumsize desc 
###后面的各级目录方式类似,就不再详述了,下面说下各级目录小文件统计的sql

#三级目录下小于100k文件数量的统计
 SELECT concat('/',split(path,'\/')[1],'/',split(path,'\/')[2],'/',split(path,'\/')[3]) as path ,count(*) as small_file_num
  FROM 
  (SELECT relative_size,path 
  FROM 
  (SELECT (case filesize < 100*1024 WHEN true THEN 'small' ELSE 'large' end) 
  AS 
  relative_size, path 
  FROM default.hdfs_info WHERE is_dir='1') tmp 
  WHERE 
  relative_size='small') tmp2 
  group by concat('/',split(path,'\/')[1],'/',split(path,'\/')[2],'/',split(path,'\/')[3]) 
  order by small_file_num desc;
  
###其他各级目录小文件数量的统计,方法类似,下面说下hive某个库下面表大小以及修改时间的统计
SELECT joinedpath,
       from_unixtime(ceil(acctime/1000),'yyyy-MM-dd HH:mm:ss') AS acctime,
       from_unixtime(ceil(modificatetime/1000),'yyyy-MM-dd HH:mm:ss') AS modificatetime,
       sumsize
FROM
  (SELECT joinedpath,
          min(accTime) AS acctime,
          max(modificatetime) AS modificatetime,
          round(sum(filesize)/1024/1024/1024,2) AS sumsize
   FROM
     (SELECT concat('/',split(path,'\/')[1],'/',split(path,'\/')[2],'/',split(path,'\/')[3],'/',split(path,'\/')[4],'/',split(path,'\/')[5]) AS joinedpath,
             accTime,
             modificatetime,
             filesize,
             OWNER
      FROM default.hdfs_info
      WHERE concat('/',split(path,'\/')[1],'/',split(path,'\/')[2],'/',split(path,'\/')[3],'/',split(path,'\/')[4])='/user/hive/warehouse/default.db')t
   WHERE joinedpath != 'null'
   GROUP BY joinedpath)h
ORDER BY sumsize DESC

其实hdfs上可用来分析的太多了,我这边只是抛砖引玉给出了一些基本的sql分析,还有一些其他的后面补充吧。

3.2 还是简单介绍下第二种方式吧,其主要的实现手段就是shell脚本,贴一下主要的代码

prepare_operation()
{
    # get parameters
    t_save_fsimage_path=$1
    # delete history fsimage
    fsimage_tmp_file=`find ${t_save_fsimage_path} -name "fsimage*"`
    if [ ! -z "${fsimage_tmp_file}" ]
    then
        for file in ${fsimage_tmp_file}
        do
            rm -f ${file}
        done
    fi
    # 使用set -e时,如果命令返回结果不为0就报错,即无法再使用$?获取命令结果,可用||或!处理     
    
}
get_hdfs_fsimage()
{
    # 获取传入参数   
    t_save_fsimage_path=$1
    # 从namenode上下载fsimage               
    hdfs dfsadmin -fetchImage ${t_save_fsimage_path}
    # 获取下载的fsimage具体文件路径               
    t_fsimage_file=`ls ${t_save_fsimage_path}/fsimage*`
    # 处理fsimage为可读的csv格式文件             
    hdfs oiv -i ${t_fsimage_file} -o ${t_save_fsimage_path}/fsimage.csv -p Delimited
    # 删除fsimage.csv的首行数据          
    sed -i -e "1d" ${t_save_fsimage_path}/fsimage.csv
    # 创建数据目录      
    hadoop fs -test -e ${t_save_fsimage_path}/fsimage || hdfs dfs -mkdir -p ${t_save_fsimage_path}/fsimage
    # 拷贝fsimage.csv到指定的路径          
    hdfs dfs -copyFromLocal -f ${t_save_fsimage_path}/fsimage.csv ${t_save_fsimage_path}/fsimage/
}

main()
{
    # 开始时间           
    begin_time=`date +%s`   
    # 定义本地和HDFS的临时目录路径        
    t_save_fsimage_path=/tmp/dfs 
    # 创建临时目录,删除历史数据等操作                
    prepare_operation ${t_save_fsimage_path} 
    # 获取HDFS的FSImage         
    hdfs_fsimage_update_time=`date "+%Y-%m-%d %H:%M:%S"`
    get_hdfs_fsimage ${t_save_fsimage_path}
    # 结束时间        
    end_time=`date +%s`
    # 耗时(秒数)     
    result_time=$((end_time-begin_time))
    echo "******************************************************************"
    echo "The script has taken ${result_time} seconds..."
    echo "Result Table: default.hdfs_meta"
    echo "HDFS FSImage update-time before: ${hdfs_fsimage_update_time}"
    echo "******************************************************************"
}
#执行主方法             
main "$@"

在获取到源数据之后的操作就跟之前的一样了。

4、总结

其实基于hdfs上的文件以及目录的分析还有很多工作要做,比如分析hdfs各级目录每天的增量变化情况,得出集群主要的增长数据来自哪个地方;分析hdfs上文件的生命周期,一个文件在hdfs上很久都没变动了是否代表这个数据就没价值了;另外hive表实质上也是hdfs上的文件,通过分析hdfs上文件包含的小文件可以知道哪些hive表没有正常使用参数产生了大量的小文件等等。合理的利用hdfs存储空间可是能帮公司节约很大的成本哦。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容