一、概述

  1.HDFS中的角色

    Block数据:

      HDFS中的文件在物理上是分块存储(block),块的大小可以通过配置参数( dfs.blocksize)来规定,默认大小在hadoop2.x版本中是128M,之前的版本中是64M 

  1. 基本存储单位,一般大小为64M(配置大的块主要是因为:1)减少搜寻时间,一般硬盘传输速率比寻道时间要快,大的块可以减少寻道时间;2)减少管理块的数据开销,每个块都需要在NameNode上有对应的记录;3)对数据块进行读写,减少建立网络的连接成本)

  2. 一个大文件会被拆分成一个个的块,然后存储于不同的机器。如果一个文件少于Block大小,那么实际占用的空间为其文件的大小

  3. 基本的读写S#x5355;位,类似于磁盘的页,每次都是读写一个块

  4. 每个块都会被复制到多台机器,默认复制3份

      NameNode:

      负责管理整个文件系统的元数据

    Secondary NameNode:

      定时与NameNode进行同步(定期合并文件系统镜像和编辑日&#x#x5FD7;,然后把合并后的传给NameNode,替换其镜像,并清空编辑日志,类似于CheckPoint机制),但NameNode失效后仍需要手工将其设置成主机——namenode的冷备份

      关于这点,可以参考http://blog.csdn.net/scgaliguodong123_/article/details/46335427

    DataNode:

      负责管理用户的文件数据块    

     文件会按照固定的大小(blocksize)切成若干块(由上传的客户端进行切块处理,这样不大于128M切块大小的实际是多少就是多少)后分布式存储在若干台datanode上

      Datanode会定期向Namenode汇报自身所保存的文件block信息,而namenode则会负责保持文件的副本数量

  详细角色信息,参考:https://www.w3cschool.cn/hadoop/xvmi1hd6.html

  漫画式的讲解,参考:https://www.cnblogs.com/raphael5200/p/5497218.html

二、读写数据流程

  简要的说明参考上文漫画式讲解处

  专业深入讲解,参考https://www.cnblogs.com/codeOfLife/p/5375120.html

  1.写数据流程

、根namenode通信请求上传文件,namenode检查目标文件是否已存在,父目录是否存在
、namenode返回是否可以上传
、client请求第一个 block该传输到哪些datanode服务器上
、namenode返回3个datanode服务器ABC
、client请求3台dn中的一台A上传数据(本质上是一个RPC调用,建立pipeline),A收到请求会继续调用B,然后B调用C,将真个pipeline建立完成,逐级返回客户端
、client开始往A上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位,A收到一个packet就会传给B,B传给C;A每传一个packet会放入一个应答队列等待应答
、当一个block传输完成之后,client再次请求namenode上传第二个block的服务器。

  图解请参考上文

  注意:HDFS只允许修改文件名/文件追加等,无法直接修改原来的文件!

  2.读数据流程

、跟namenode通信查询元数据,找到文件块所在的datanode服务器
、挑选一台datanode(就近原则,然后随机)服务器,请求建立socket流
、datanode开始发送数据(从磁盘里面读取数据放入流,以packet为单位来做校验)
、客户端以packet为单位接收,现在本地缓存,然后写入目标文件

三、HDFS元数据管理与解析

  1.元数据管理原理

    元数据分类:

  • 第一类是文件和目录自身的属性信息,例如文件名、目录名、父目录信息、文件大小、创建时间、修改时间等。
  • 第二类记录文件内容存储相关信息,例如文件块情况、副本个数、每个副本所在的Data Node 信息等。
  • 第三类用来记录HDFS中所有Data Node信息,用于Data Node管理。

    存储机制:

      A、内存中有一份完整的元数据(内存meta data)

      B、磁盘有一个“准完整”的元数据镜像(fsimage)文件(在namenode的工作目录中),整个运行过程中,fsimage是不会改变的!editslog的更新会同步到内存

      C、用于衔接内存metadata和持久化元数据镜像fsimage之间的操作日志(edits文件)注:当客户端对hdfs中的文件进行新增或者修改操作,操作记录首先被记入edits日志文件中,当客户端操作成功后,相应的元数据会更新到内存meta.data中

    更多参考http://blog.csdn.net/xiaming564/article/details/23165253

        http://blog.csdn.net/chenkfkevin/article/details/61196409

    这里就能很清楚的知道secondary namenode的工作原理了!

    关于元数据的存取流程与分析,参考网友的白话讲解http://blog.csdn.net/lepton126/article/details/53183037

    2.修改工作目录

    之前我们配置过hadoop.tmp.dir来设置临时目录,这里可以通过以下参数设置HDFS工作目录

配置文件:hdfs-site.xml
参数名:dfs.namenode.name.dir
格式:file://${hadoop.tmp.dir}/dfs/name
说明:Determines where on the local filesystem the DFS name node should store the name table(fsimage).
If this is a comma-delimited list of directories then the name table is replicated in all of the directories, for redundancy.
示例:
<property>
<name>dfs.namenode.name.dir</name>
<value>/home/hadoop/name1,/home/hadoop/name2</value>
</property>

  //当然,dfs.datanode.data.dir也是可以配置的,这样重新格式化后就可以重新使用工作目录了!点击查看对比

  注:如果还在使用dfs.name.dir/dfs.data.dir,请查看官网配置的deprecated properties

    3.元数据各目录解析

      详细解析,参考https://www.iteblog.com/archives/967.html

              http://blog.csdn.net/opensure/article/details/51452058

四、HDFS的Java-API操作

   完整API参考官网:http://hadoop.apache.org/docs/current/api/

  1.基本的增删改查API:

package com.hdfs.demo;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.Before;
import org.junit.Test; import java.net.URI; /**
* HDFS客户端demo
*
* @author zcc ON 2018/1/28
**/
public class HdfsClientDemo {
FileSystem fs = null;
@Before
public void init() throws Exception{
// 如配置完环境变量,下行可省略
// System.setProperty("hadoop.home.dir", "F:\\work\\hadoop-2.6.4");
Configuration conf = new Configuration();
// 配置文件系统(注意hosts的配置)
// conf.set("fs.defaultFS","hdfs://mini1:9000");
// 拿到一个操作的客户端实例对象(此处使用3个参数则上一行省略)
fs = FileSystem.get(new URI("hdfs://mini1:9000"),conf,"hadoop");
}
@Test
public void testUpload() throws Exception{
// 就对应get的别名
fs.copyFromLocalFile(new Path("F:/c.log"),new Path("/c.log.copy"));
// 关闭
fs.close();
}
@Test
public void testDelete() throws Exception{
// 第一个是Path,第二个为是否递归删除
boolean b = fs.delete(new Path("/c.log.copy"), true);
System.out.println("删除状态:" + b);
fs.close();
}
@Test
public void testList() throws Exception{
/*
一般而言,大数据方面使用迭代器场景居多,因为Iterator它本身并不存数据(可以查看源码)
它只是提供了几个简单的方法帮你去取数据,而使用ArrayList则是直接把数据拿过来了,大数据
量下不适合
*/
RemoteIterator<LocatedFileStatus> iterator = fs.listFiles(new Path("/"), true);
while (iterator.hasNext()) {
LocatedFileStatus next = iterator.next();
System.out.println("Path:" + next.getPath());
System.out.println("BlockSize" + next.getBlockSize());
System.out.println("Name:" + next.getPath().getName());
} // Path:hdfs://mini1:9000/1.txt
// BlockSize134217728
// Name:1.txt
fs.close();
} /**
* 既可以遍历文件,又可以遍历文件夹
* @throws Exception
*/
@Test
public void testList2() throws Exception{
FileStatus[] files = fs.listStatus(new Path("/"));
for (FileStatus file : files) {
System.out.println(file.getPath().getName());
if (file.isFile()) {
System.out.println("it is a file");
}
}
}
}

  这里引入Java中迭代器的机制浅谈,供参考https://www.cnblogs.com/hasse/p/5024193.html

  更多实例,参考:http://blog.csdn.net/litianxiang_kaola/article/details/70983904

  2.流操作API

    相对那些封装好的方法而言的更底层一些的操作方式上层那些mapreduce   spark等运算框架,去hdfs中获取数据的时候,就是调的这种底层的api

package com.hdfs.stream;

import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.Before;
import org.junit.Test; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI; /**
* HDFS的流API操作
*
* @author zcc ON 2018/1/30
**/
public class HdfsStreamAccess {
FileSystem fs = null;
@Before
public void init() throws Exception{
Configuration conf = new Configuration();
fs = FileSystem.get(new URI("hdfs://mini1:9000"),conf,"hadoop");
} /**
* 通过流的操作,就可以为上层MR程序提供服务,例如跑任务只需要跑60M,这样就不用通过
* 之前的直接得到一整个文件的复杂处理
* 而建立文件夹等直接通过简单API即可!
* @throws Exception
*/
@Test
public void testUpload() throws Exception{
// 输出到HDFS
FSDataOutputStream outputStream = fs.create(new Path("/angle"), true);
// 本地输入流
FileInputStream inputStream = new FileInputStream("F:\\c.log");
// 通过IOUtils进行拷贝(使用更加通用的IOUtils)
IOUtils.copy(inputStream, outputStream);
}
@Test
public void testDownload() throws Exception{
FSDataInputStream inputStream = fs.open(new Path("/angle"));
FileOutputStream outputStream = new FileOutputStream("F:\\c-download.log");
IOUtils.copy(inputStream, outputStream);
} /**
* 指定随机长度读取
*/
@Test
public void testRandomAccess() throws Exception{
FSDataInputStream inputStream = fs.open(new Path("/angle"));
inputStream.seek(12);
FileOutputStream outputStream = new FileOutputStream("F:\\c-random.log");
// 从12字节读到末尾(也可以通过while结合自定义的count等来控制读文件的大小)
// 后续会避免读取读到单词一半这样的问题
// IOUtils.copy(inputStream, outputStream);
IOUtils.copyLarge(inputStream, outputStream, 12, 100);
} /**
* 以下模拟实现:获取一个文件的所有block位置信息,然后读取指定block中的内容
* @throws IllegalArgumentException
* @throws IOException
*/
@Test
public void testCat() throws IllegalArgumentException, IOException { FSDataInputStream in = fs.open(new Path("/weblog/input/access.log.10"));
//拿到文件信息
FileStatus[] listStatus = fs.listStatus(new Path("/weblog/input/access.log.10"));
//获取这个文件的所有block的信息
BlockLocation[] fileBlockLocations = fs.getFileBlockLocations(listStatus[0], 0L, listStatus[0].getLen());
//第一个block的长度
long length = fileBlockLocations[0].getLength();
//第一个block的起始偏移量
long offset = fileBlockLocations[0].getOffset(); System.out.println(length);
System.out.println(offset); //获取第一个block写入输出流
// IOUtils.copyBytes(in, System.out, (int)length);
byte[] b = new byte[4096]; FileOutputStream os = new FileOutputStream(new File("d:/block0"));
while(in.read(offset, b, 0, 4096)!=-1){
os.write(b);
offset += 4096;
if(offset>=length) return;
};
os.flush();
os.close();
in.close();
}
}

五、案例:shell脚本日志采集

  采用shell编写的脚本如下:

#!/bin/bash

#set java env
export JAVA_HOME=/home/hadoop/app/jdk1..0_51
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH #set hadoop env
export HADOOP_HOME=/home/hadoop/app/hadoop-2.6.
export PATH=${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH #版本1的问题:
#虽然上传到Hadoop集群上了,但是原始文件还在。如何处理?
#日志文件的名称都是xxxx.log1,再次上传文件时,因为hdfs上已经存在了,会报错。如何处理? #如何解决版本1的问题
# 、先将需要上传的文件移动到待上传目录
# 、在讲文件移动到待上传目录时,将文件按照一定的格式重名名
# /export/software/hadoop.log1 /export/data/click_log/xxxxx_click_log_{date} #日志文件存放的目录
log_src_dir=/home/hadoop/logs/log/ #待上传文件存放的目录
log_toupload_dir=/home/hadoop/logs/toupload/ #日志文件上传到hdfs的根路径
hdfs_root_dir=/data/clickLog// #打印环境变量信息
echo "envs: hadoop_home: $HADOOP_HOME" #读取日志文件的目录,判断是否有需要上传的文件
echo "log_src_dir:"$log_src_dir
ls $log_src_dir | while read fileName
do
if [[ "$fileName" == access.log.* ]]; then
# if [ "access.log" = "$fileName" ];then
date=`date +%Y_%m_%d_%H_%M_%S`
#将文件移动到待上传目录并重命名
#打印信息
echo "moving $log_src_dir$fileName to $log_toupload_dir"xxxxx_click_log_$fileName"$date"
mv $log_src_dir$fileName $log_toupload_dir"xxxxx_click_log_$fileName"$date
#将待上传的文件path写入一个列表文件willDoing
echo $log_toupload_dir"xxxxx_click_log_$fileName"$date >> $log_toupload_dir"willDoing."$date
fi done
#找到列表文件willDoing
ls $log_toupload_dir | grep will |grep -v "_COPY_" | grep -v "_DONE_" | while read line
do
#打印信息
echo "toupload is in file:"$line
#将待上传文件列表willDoing改名为willDoing_COPY_
mv $log_toupload_dir$line $log_toupload_dir$line"_COPY_"
#读列表文件willDoing_COPY_的内容(一个一个的待上传文件名) ,此处的line 就是列表中的一个待上传文件的path
cat $log_toupload_dir$line"_COPY_" |while read line
do
#打印信息
echo "puting...$line to hdfs path.....$hdfs_root_dir"
hadoop fs -put $line $hdfs_root_dir
done
mv $log_toupload_dir$line"_COPY_" $log_toupload_dir$line"_DONE_"
done

脚本文件

  加入定时任务调度,参考大数据之Linux基础

大数据入门第六天——HDFS详解的更多相关文章

  1. hadoop大数据基础框架技术详解

    一.什么是大数据 进入本世纪以来,尤其是2010年之后,随着互联网特别是移动互联网的发展,数据的增长呈爆炸趋势,已经很难估计全世界的电子设备中存储的数据到底有多少,描述数据系统的数据量的计量单位从MB ...

  2. 大数据平台Lambda架构详解

    Lambda架构由Storm的作者Nathan Marz提出.旨在设计出一个能满足.实时大数据系统关键特性的架构,具有高容错.低延时和可扩展等特. Lambda架构整合离线计算和实时计算,融合不可变( ...

  3. CentOS6.5下如何正确下载、安装Intellij IDEA、Scala、Scala-intellij-bin插件、Scala IDE for Eclipse助推大数据开发(图文详解)

    不多说,直接上干货! 第一步:卸载CentOS中自带openjdk Centos 6.5下的OPENJDK卸载和SUN的JDK安装.环境变量配置   第二步:安装Intellij IDEA 若是3节点 ...

  4. 30个mysql千万级大数据SQL查询优化技巧详解

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  5. 【大数据系列】MapReduce详解

    MapReduce是hadoop中的一个计算框架,用来处理大数据.所谓大数据处理,即以价值为导向,对大数据加工,挖掘和优化等各种处理. MapReduce擅长处理大数据,这是由MapReduce的设计 ...

  6. 大数据之ETL设计详解

    ETL是BI项目最重要的一个环节,通常情况下ETL会花掉整个项目的1/3的时间,ETL设计的好坏直接关接到BI项目的成败.ETL也是一个长期的过程,只有不断的发现问题并解决问题,才能使ETL运行效率更 ...

  7. 【大数据笔记】白话详解Zookeeper的一致性

    下面内容主要摘抄于<<Hadoop实战>>,红色高亮部分是本人添加的白话注释. Zookeeper 是一种高性能.可扩展的服务. Zookeeper 的读写速度非常快,并且读的 ...

  8. 大数据入门基础系列之Hadoop1.X、Hadoop2.X和Hadoop3.X的多维度区别详解(博主推荐)

    不多说,直接上干货! 在前面的博文里,我已经介绍了 大数据入门基础系列之Linux操作系统简介与选择 大数据入门基础系列之虚拟机的下载.安装详解 大数据入门基础系列之Linux的安装详解 大数据入门基 ...

  9. HDFS详解

    HDFS详解大纲 Hadoop HDFS 分布式文件系统DFS简介 HDFS的系统组成介绍 HDFS的组成部分详解 副本存放策略及路由规则 命令行接口 Java接口 客户端与HDFS的数据流讲解 目标 ...

随机推荐

  1. JSP九大内置对象与Servlet的对应关系

    JSP对象                              Servlet中怎样获得 request service方法中的request参数 response service方法中的res ...

  2. Oracle EBS AP 供应商取值

    SELECT --nvl(substr(po.vendor_name,1,instr(po.vendor_name,',',1)-1),po.vendor_name) vendor_name, po. ...

  3. idea 版本控制忽略文件、文件夹设置

    setting 或者底部的 设置 忽略某个文件 后面选择框可以去选择 忽略某个文件夹 后面选择框可以去选择 忽略某种文件 后面输入填写如: *.txt

  4. Windows+Git+TortoiseGit+COPSSH 安装教程及问题收集

    准备工作: 1. git-1.8.1.2-preview20130201.exe 下载地址: https://code.google.com/p/msysgit/downloads/list 2. C ...

  5. 发布MVCIIS报错未能加载文件或程序

    未能加载文件或程序集“System.Web.Http.WebHost, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e3 ...

  6. ASP.NET MVC 5搭建自己的视图基架 (CodeTemplate)

    我们知道,在MVC项目中添加视图时,在添加面板有模板可以选择,这里会有人疑问,这个模板位于哪里?我可以搭建自己的基架吗? 首先回答第二个问题,答案是当然可以 我这里使用的是Visual Studio ...

  7. C++:sprintf()的用法(转)

    转:http://blog.csdn.net/masikkk/article/details/5634886 更多:http://blog.csdn.net/zjuwispersure/article ...

  8. C# 词法分析器(一)词法分析介绍

    系列导航 (一)词法分析介绍 (二)输入缓冲和代码定位 (三)正则表达式 (四)构造 NFA (五)转换 DFA (六)构造词法分析器 (七)总结 虽然文章的标题是词法分析,但首先还是要从编译原理说开 ...

  9. python第五课——流程控制语句

    流程控制语句: 分类: 1).顺序结构 2).判断结构解析:如果...否则... 3).循环结构 1.判断结构: 格式分类:三种格式 格式一: ① if 条件表达式: 语句块 ② 执行流程: 计算机会 ...

  10. linux中Vi编辑器使用

    1.如需要编辑aaa.txt文件: vi  aaa.txt   就可以进入到   aaa.txt文件中, 输入  i   进入到编辑模式, 按 Esc 退出编辑模式  , :wq   保存退出编辑模式 ...