Hadoop权威指南-第三章学习笔记(超详细)

第三章 Hadoop分布式文件系统

3.1 HDFS的设计
超大文件;
流式数据访问;
低时间延迟的数据访问; 不适用于访问时间有限制的应用
多用户写入;只能但用户在文件末尾修改
大量的小文件;
商用硬件; 硬件便宜
任意修改文件;只能在文件末尾修改

3.2 HDFS的概念
数据块:大小默认为64MB,磁盘的一般为512字节;块设置大一点可以缩短寻址时间开销
抽象为数据块的好处:当一个文件大于一个磁盘的容量的时候,可以分为多个块存于多个磁盘
一块为存储单元,简化了存储子系统的设计
块方便用于数据备份

3.2.2 namenode 和 datanode(namenode:管理节点 datanode:工作节点)
namenode:管理文件系统的命名空间,维护着整棵文件系统树和树内的文件和目录,这些信息以两个文件的信息存在硬盘上(命名空间镜像文件和编辑日志文件)。在内存中保存文件系统的每个文件和数据块的引用关系。客户端通过和namenode和datanode交互来访问文件系统,但不知道它们也能实现其功能
datanode:根据需要存储和捡取数据块,并且定期向namenode发送他们的存储块列表。
hadoop对namenode的容错机制:
第一种:备份那些组成文件系统元数据持久状态的文件。一般做配置:将持久化文件写入磁盘的同时,写入一个网络文件系统NFS。
第二种:运行一个辅助的namenode,他的作用是定期通过编辑日志合并命名空间镜像,以此防止编辑日志过大。在真正的namenode发生故障的时候启用,但由于辅助namnode保存的状态落后于namenode,所以会丢失部分数据。

3.2.3 联邦HDFS
允许向系统中添加namenode实现横向扩展,使每个namenode管理一个独立的命名空间卷(管理一部分文件夹和下面的文件)。所有的命名空间卷之间相互独立,互补影响,所以每个datanode都需要注册到所有的namenode。

3.2.3 HDFS的高可用性
当namenode失效后,让新的namenode上线使用很麻烦,需配置datanode,导入命名空间的镜像,重做编辑日志,接收来自datanode的数据块报告,等待namenode的冷启动,需花费很长的时间,这时候所有的客户端均无法读写。
hadoop 2.x增加了对高可用性的支持,配置了一对:活动—备用namenode,当活动namenode失效,备用namenode就会接管他的任务并开始服务于来自客户端的请求,这个过程不会有明显的中断。
在架构上做的更改:这两个namenode之间需要通过高可用的共享存储实现编辑日志的共享;
datanode需要同时向两个namenode发送数据块处理报告;
客户端需要使用特定的机制来处理namenode失效的问题,这一机制对用户是透明的。
故障切换:故障转移控制器将活动节点转为备用节点; 每个namenode都运行这一个故障转移控制器,监视宿主namenode是否失效,失效就转换,管理员也可以收到那个转换。
故障规避:无法确切知道namenode是否失效,比如网速慢也会激发故障转移,但namenode并未失效。进一步做了优化:提供了一系列规避机制。杀死namenode进程;收回其访问共享目录的权限,屏蔽网络端口,断电等。

3.3 命令行接口
配置伪分布使的两个属性项
1:fs.default.name:hdfs://localhost/ 用于设置hadoop的默认文件系统 HDFS守护程序通过该属性来确定HDFS的namenode主机及端口。HDFS客户端通过该属性得知anmenode在哪里进而连接到它;
2:dfs.replication:1 (设置为1,默认为3)这样HDFS就不会按默认的设置将文件系统复本设置为3.在单独的datanode上运行时,HDFS就无法将块复制到3个datanode上,所以会持续给出复本不足的警告,设置这个属性后就不会有问题了。(这样配置之后,文件系统就可以使用了)
文件系统的基本操作
①. hadoop fs -help 获取每个命令的详细帮助文件;
②. %hadoop fs -copyFromLocal input/docs/quangle.txt hdfs://localhost/user/tom/quangle.txt 从本地文件系统复制一份文件到HDFS;该命令调用的hadoop文件系统的shell命令 fs , 后者提供了一系列的子命令(该条命令执行的是 -copyFromLocal,由于core-site.xml 默认的就是:hdfs://localhost,所以这条命令可以省略hdfs://lcoahost为:%hadoop fs -copyFromLocal input/docs/quangle.txt /user/tom/quangle.txt)还可以使用相对路径
③. %hadoop fs -copyToLocal quanle.txt quanle.copy.txt
%md5 input/docs/quanle.txt quanle.copy.txt 这两条命令是将文件复制到本地并检查是否一致(输出的是MD5的键值,键值一致代文件内容相同)
④. %hadoop fs -mkdir books 新建一个文件夹
%hadoop fs -ls 列出所有的文件(第一列:文件模式 ;第二列 :备份数 ; 第三列:所属用户 第四列:组别 第五列:文件大小,字节单位,目录为0 ; 第六列和第七列显示的是最后修改日期和时间; 第八列是文件的绝对路径)
HDFS中的文件权限
三类权限模式:只读r 写入w 可执行:x(对于文件系统忽略,访问一个目录的子目录需要改权限) 启用权限控制的配置属性:dfs.permissions

3.4 Hadoop 文件系统
Hadoop有一个抽象的文件系统的概念HDFS只是其中的一个实现。 org.apache.hadoop.fs.FileSystem定义了Hadoop中的一个文件系统接口,并有以下几个具体实现:
文件系统 URL方案 java实现(在org.apache.hadoop.*包中) 描述
Local file fs.LocalFileSystem 使用了客户端校验和的本地磁盘文件系统;没有使用校验和的本地磁盘文件系统:RawLocalFileSystem
HDFS hdfs hdfs.DistributedFileSystem Hadoop的分布式文件系统。将HDFS设计成与MapReduce结合使用,可以提高性能
HFTP Hftp hdfs.hftpFileSystem 一个在HTTP上提供对HDFS只读访问的文件系统。通常与distcp结合使用,以实现运行不同版本的HDFS集群之间复制数据
HSFTP hsftp hdfs.HsftpFileSystem 在HTTPS上提供对HDFS只读访问的文件系统
WebHDFS Webhdfs Hdfs.web.WebHdfsFileSystem 基于HTTP,对HDFS提供安全读写访问的文件系统,WebHDFS是为了替代HFTP和HSFTP而构建的
HAR har fs.HarFileSystem 一个构建与在其他文件系统之上用于文件存档的文件系统。Hadoop存档文件系统通常用于需要将HDFS中的文件存档时,一以减少namenode内存的使用
hfs(云存储) kfs fs.kfs.kosmosFileSystem CloudStore时类似于HDFS或是谷歌的GFS的文件系统,C++写的
FTP ftp fs.ftp.FTPFileSystem 由FTP服务器支持的文件系统
S3(原生) S3n fs.s3native.NativeS3FileSystem 由Amazon S3支持的文件系统
S3(基于块) S3 fs.sa.S3FileSyetm 由Amazon S3支持的文件系统,以块格存储文件,与HDFS像是,解决S3的5GB文件大小限制
RAID(分布式) hdfs hdfs.DistributedRaidFileSytem RAID版本的HDFS是为了存档而设计的
View viewfs viewfs.ViewFileSystem 针对其他Hadoop文件系统挂载的客户端表,通常用于联邦namenode创建挂载点
Hadoop根據URI方案选取对应的文件系统,我们可以根据URI来配置合适的文件系统
文件系统的命令行可以解释Hdoop的所有命令, 如在文件系统命令航执行本地文件系统命令:%hadoop fs -ls file:///
接口
由于Hadoop时用java开发的,所以java可以调用所有的文件系统与之交互。访问文件系统的途径方式:
1:HTTP
①:直接访问,namenode内嵌的web服务器(port:50070)提供目录服务,目录以xml或者json格式存储,并且文件数据由datanode的web服务器(prot:50075)以数据流的形式传输。WebHDFS必须将dfs.webhdfs.enable=true之后才能启用,只有启用之后才能使用webhdfs URI。它支持所有的文件系统
②:代理访问(客户端通常使用DistributedFileSystemAPI访问HDFS)

3.5 Java接口
Hadoop FileSystem类,它是Hadoop与文件系统进行交互的API。我们主要聚焦于HDFS的实例DistributedFileSystem,但是还是应该集成FileSystem抽象类,编写代码,使其在不同的文件系统中时可以移植的。
3.5.1 从Hadoop URL读取数据
要从Hadoop文件系统 读取数据,最简单的方式是用java.net.URL打开数据流,从中读取数据。具体格式:

    InputStream in = null;
    try{
        in = new URL("hdfs://host/paht").openStream()
                }catche(Exception e){}finally{
                    IOUtils.closeStream(in);
                            } 
}

由于java程序是不能识别hdfs的URL的,所欲需要通过FsUrlStreamHandlerFactory实例调用setURLStreamHandlerFactory方法,每个java虚拟机只能调用一次这个方法,所以需要在静态中使用。
指定URLStreamHandlerFactory读取并显示数据(相当于Unix的Cat命令的功能):

static{
    setURLStreamHnadlerFactory(new FsUrlStreamHandlerFactory);
}
InputStream in = null;
try{
    in = new URL(args[0]).openStream();
    //在输入流和输出流之间复制数据(System.out为输出流)
    IOUtils.copyBytes(in,System.out,4096,false);//这里的IOUtils是一个Hadoop的一个IO流工具类,作用于System.out;打印在控制台
}catche(Exception e){

}finally{
    IOUtils.closeStream(in);
}

以上程序的hadoop命令:%hadoop URLCat hdfs://localhost/usr/tom/quanle.txt

3.5.2 通过FileSstemAPI来读取文件
由于有时候不能设置URLStreamHandlerFactory,这时候就可以根据FileSystemAPI来读取数据。
(1)检索文件系统的实例(因为FileSystem是一个所有文件系统通用的类)
如果是HDFS,有这样的几个静态方法:public static FileSystem get(Configuration conf ) 返回的是默认的文件系统(conf/core-site.xml中的默认文件系统),如果没有指定,则返回本地文件系统
public static FileSystem get(URI uri, Configuration conf) 通过给的uri方案和权限来确定使用的文件系统,如果uri没有给出方案,那么返回默认的文件系统
public static FileSystem get(URI uri, Configuration conf, String user) 作为给定的用户访问文件系统,对安全十分重要。
重写上面的例子:

class Reader{
    public static void main(String [] args) {
        String uri = agrs[0];
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(uri), conf);
        InputStream in = null;
        try{
            in = fs.open(new Path(uri));
            IOUtils.copyBytes(in,System.out,4096,false);
        }catch(Exception e){}finally{
            IOUtils.closeStream(in);
        }
    }
}

FsDataInputStream对象
实际上,FileSsystem.open()返回的是FsDataInputStream,而FsDataInputStream extends DataInputStream implements Seekable ,PositionedReadable
Seekable{
void seek(long pos);移动到指定(绝对)位置,但位置长度大于文件长度会引发异常,而java标准的IO流的skip只能移动到当前位置的相对位置
long getPos();
boolean seekToNewSource(long targetPos);
}
PositionedReadable接口:从一个指定偏移量处都群文件的一部分
PositionedReadable{
public int read(long position, byte[] buffer,int offSet, int length);从指定偏移量position处读取至多length字节的数据存入缓存去buffer的指定偏移量的offSet处
public void readFully(long position, byte [] buffer,int offSet , int length);读取length长度的数据(除非读到文件末尾)
public void readFully(long position, byte [] buffer);读取buffer.length长度的数据(除非读到文件末尾)
}
例子:使用seek方法将hadoop文件系统的文件在便准输出上输出两次,第一次显示完成后重新定位到开始再输出一次

public class FileSystemDoubleCat{
            public static void main(String [] args){
                String uri = args[0];
                Configuration conf = new Configuration();
                FileSystem fs = FileSystem.get(URI.create(uri),conf);
                FsDataInputStream in = null;
                try{
                    in = fs.open(new Path(uri));
                    IOUtils.copyBytes(in,System.out,4096,false);
                    in.seek(0);
                    IOUtils.copyBytes(in,System.out,4096,false);
                    }catch(Exception e){}finally{
                        IOUtils.closeableStream(in);
                        }
                }

        }
运行命令:%hadoop FileSystemDoubleCat hdfs://localhost/usr/local/quanle.txt(seek方法是高开销的,不能大量使用)

3.5.3 写入数据
public FsDataOutputStream create(Path path);给一个指定的文件创建一个输出流,该方法可以为不存在的文件创建父目录
public FsDataOutputStream append(Path path);在一个文件后面追加的输入流,对有些文件系统适用(HDFS),有些文件系统不提供该API(S3)
例子把本地文件系统的文件复制到Hadoop文件系统中去

    public class FileCopyWithProgress{
        public static void main(String [] args){
                String locSrc = args[0];
                String dst = args[1];
                InputStream in = new BufferedInputStream(new FileInputStream(Path dst));
                Configuration conf = new Configuration();
                FileSystem fs = FileSystem.get(URI.create(locSrc),conf);
                FsDataOutputStream out = fs.create(new Path(dst),new progressable(){//目前,其他文件系统写入文件的时候均不调用progress方法
                        public void progress(){
                            System.out.println("回调接口,可用来显示进度!");
                        }
                    });
                IOUtils.copyBytes(in,out,4096,true);
            }
    }

java执行命令:%hadoop FileCopyWithProgress input/doc/14008.txt hdfs://localhost/usr/tom/14008.txt
FsDataOutputStream对象:该对象与FsDataInputStream类似,不同的是它不允许定位,因为HDFS只允许对一个一打开的文件顺序写入,也就是只能在文件末尾追加数据。
有一个查询当前位置的方法:

    public class FsDataOutputStream extends DataOutputStream implements Syncable{
        public long getPos()throws IOException(){}//定位到当前的位置
    }

3.5.4 目录
public boolean mkdirs(Path path);创建多层目录,通常不会显式调用,因为调用写入文件的create方法就可以创建不村子的父目录和文件。

3.5.5 查询文件系统
(1) 文件元数据:FileStatus
所有文件系统的共同特征:提供目录结构浏览和检索所存文件和目录的信息的功能; FileStatus提供了文件系统的文件和目录的元数据,包括文件的长度,块大小,复本,修改时间,所有者以及权限信息;FileSystem类(FileSystem fs = FileSystem.get(URI.create(uri),conf))的getFileStatus()方法发布会文件系统的元数据
范例3-5 展示文件的状态信息

public class ShowFileStatusTest{
    private FileSystem fs;
    private MiniDFSCluster cluster;

    @Before
    public void setUp() throws IOException {
        Configuration conf = new Configuration();
        if(System.getProperty("test.build.data") == null){
            System.setProperty("test.build.data","/tmp");
        }
        cluster = new MiniDFSCluster(conf,1,true,null);
        FileSystem fs = cluster.getFileSsytem();
        OutputStream out = fs.create(new Path("/dir/file"));
        out.write("content".getBytes("UTF-8"));
        out.close();
    }
    @After
    public void tearDown() throws IOException {
        if(fs != null){
            fs.close();
        }
        if(cluster != null){
            cluster.shutdown();
        }
    }

    @Test(expected = FileNotFoundException.class)//不存在的文件取得FileStatus的时候会抛出异常
    public void throwsFileNotFoundExceptionForNonExisterFile(){
        fs.getFileStatus("non-exist-file");
    }

    @Test
    public void fileStatusForFile(){
        Path path = new Path("/dir/file");
        FileStatus stat = fs.getFileStatus(path);
        //是否为文件夹
        assertThat(stat.isDir(),is(false));
        //长度
        assertThat(stat.getLen(), is(7L));
        //路径
        assertThat(stat.getPath().toUri().getPath(), is("/dir/file"))
        //修改时间
        assertThat(stat.getModificationTime(), is(lessThanOrEqualTo(System.getCurrentMills()));
        //replication
        assertThat(stat.getReplication(), is((short)1));
        //blockSize
        assertThat(stat.getBlocakSize(), is(64*1024*1024L));
        //所有者
        assertThat(stat.getOwner(), is("Tom"));
        //所属那个group
        assertThat(stat.getGroup(), is("superGroup"));
        //权限
        assertThat(stat.getPermissions().toString(), is("rw-r--r--"));
    }

    @Test
    public void fileStatusForFile(){
        Path path = new Path("/dir/file");
        FileStatus stat = fs.getFileStatus(path);
        assertThat(stat.getPath().toUri().getPath(), is("/dir/file"));
        assertThat(stat.isDir(), is(true));
        assertThat(stat.getLen(), is(0L));
        assertThat(stat.getModificationTime(), is(lessThanOrEqualTo(System.getTimeMills())));
        assertThat(stat.getReplication(), is((short)0));
        assertThat(stat.getBlocakSize(), is(0)L);
        assertThat(stat.getOwner(), is("Tom"));
        assertThat(stat.getGroup(), is("superGoup"));
        assertThat(stat.getPermissions().toString(), is("rwxr-xr-w"));
    }
}
(2)列出文件
    listStatus方法:
            public FileStatus [] listStatus(Path path) throws IOException;
            public FileStatus [] listStatus(Path path , PathFilter pathFilter) throws IOException;
            public FileStatus [] listStatus(Path [] files) throws IOException;
            public FileStatus [] listStatus(Path [] files , PathFilter pathFilter) throws IOException;

范例3.6-显示Hadoop文件系统中一组路径的文件信息

执行的命令demo:
%hadoop ListStatus hdfs://localhost/
hdfs://localhost/usr/tom
hdfs://localhost/usr
hdfs://localhost/usr/tom/books
hdfs://localhost/usr/tom/quanle.txt

(3)文件模式
    利用通配符来选取多个文件,FileSystem提供了两个方法
        public FileStatus [] globStatus(Path pathPattern) throws IOException;
        public FileStatus [] globStatus(Path pathPattern, PathFilter pathFilter) throws IOException;------globStatus方法返回的是与其路径匹配于指定模式的所有文件的FileStatus数组,并且按路径排序

各个通配符及其含义如下
通配符 匹配
* 匹配0个或多个字符
? 匹配单一字符
[ab] 匹配{a-b}中的任意一个字符
[^ab] 匹配非{a-b}中的任意一个字符
[a-b] 匹配一个在{a-b}范围内的字符,包括ad,a在字典上的顺序小于等于b
[^a-b] 匹配一个不在{a-b}范围内的字符
{a,b} 匹配包含a或b中的一个表达式
\c 转义字符,匹配元字符c

(4)PathFilter对象
    public interface PathFilter//Path对象
    boolean accept(Path path);
}

范例3.7-PathFilter用于排除正则表达式的路径

public class RegexExcludePathFilter extends PathFilter{
    private final String regex;
    public RegexExcludePathFilter(String regex){
        this.regex = regex;
    }
    public boolean accpet(Path path){
        return !path.toString().matches(regex);
    }
}

//调用例子 : fs.globStatus(new Path(“/2007/*/*”), new RegexExcludePathFilter(“^.*/2007/12/31$”))
3.5.6 删除数据
使用FileSystem.delete(Path path,boolean recursive);recursive:是否删除path目录下面的子目录及其文件,当时文件时忽略这个属性

3.6 数据流
3.6.1 剖析文件读取
文件读取流程图
Distributed FileSystem 通过RPC调用namenode,每个namenode都返回存有该块副本的datanode的地址。(这些datanode通过距离客户端的远近排序,倘若客户端本身就是一个datanode,那么就从客户端本地文件系统读取)
Distributed FileSystem 通过open方法返回一个FsDataInputStream给客户端(定位并读取数据),而FsDataInputStram又封装了DFSDataInputStream(管理文件系namenode和datanode的I/O)
接着,客户端调用返回的FsDataInputStream的read方法,DFSDataInputStream链接最近的几个datanode,调用自身的read方法,知道读到末端 DFSDataInputStream就关闭与datanode的链接
客户端从流中读取数据,由于顺序时按照DFSDataIputStream对datanode的新建顺序排列的,所以会询问namenode来再次排序,完成后关闭
错误处理:在读取数据的时候,DFSDataInputStream与datanode通信时遇到错误,会尝试从另外一个最临近的datanode读取数据,并记住那个故障datanode,避免重复从故障的datanode读取数据。DFSDataInputStream也会通过校验确认从datanode读取的数据是否完整,如果发现有损坏的块,那么在读取下一个datanode之前通知namenode。
这个设计的总店是:namenode告知客户端每个块中最佳的datanode,并让客户端直接连接到datanode读取数据,由于数据流分散到所有的datanode,所以这样设计能使HDFS扩展到大量并发客户端,namenode只需要响应块位置的请求,无需相应数据请求,否者随着客户端数量的增长,namenode会很快成为瓶颈。

3.6.2 剖析文件写入
详细步骤如下:
(1)客户端通过调用Distributed FileSystem的create方法来新建文件,
(2)DistributedFileSystem 对namenode创建一个RPC的调用,在文件系统命名空间中新建一个文件,此时该文件中还没有相应的数据块。
(3)namenode执行各种不同的检查以确保这个文件不存在以及客户端有新建该文件的权限。
(4)如果这些检查都通过,那么namenode就会为创建新文件记录一条新记录;否则,文件创建失败并向客户端抛出IOException异常
(5)DistributedFileSystem向客户端返回一个FSDataOutputStream对象,由此客户端可以开始写入数据
(6)FsDataOutputStream封装的DFSOutputStream负责处理datanode和namenode之间的通信
(7)在客户端写入数据时,DFSOutputStream将它分成一个个的数据包,并写入内部的“数据队列”。
(8)DataStreamer处理数据队列,他的责任是根据datanode列表来要求namenode分配适合的新块来存储数据复本。
(9)这一组的datanode构成一个管线,假设复本数位3,那么则管线有3个节点。
(10)DataStreamer将数据包流式传输到管线中第一个节点datanode,该datanode存储数据包并将它发送到第2个datanode,第二个也是如此。
DFSOutputStream也维护着一个 内部数据包队列来等待datanode的收到确认回执,成为确认队列。收到管道中的所有的datanode的确认回执信息后,该数据包才会从确认队列删除
故障解决:如果在写入期间datanode发生故障,处理步骤如下:
(1)首先关闭管线,确认把队列中的所有数据包都添加回数据队列的最前端,以确保故障节点的下游的datandoe不会漏掉任何一个数据包。
(2)位存储在另一正常的datanode的当前数据块制定一个新的标识,并将该标识传递给namenode,以便故障datanode在恢复后可以删除部分的存储数据块。
(3)从管线中删除故障数据节点并且把余下的数据块写入管线中其他正常的datanode。
(4)namenode注意到块复本量不足时,会在另一个节点上创建一个新的复本,后续的数据块继续正常接受处理
(11)客户端完成的数据写入后,对数据流调用close方法。该操作将剩余的所有数据包写入datanode管线,并在联系到namenode且发送文件写入完成信号之前,等待确认。

3.6.3 一致模型
文件系统的一致模型描述了文件读/写的数据可见性。
新建一个文件后,他能在文件系统的命名空间中立即可见
Path p = new Path(“p”);
fs.create(p);
新建一个文件写入内容后,即使数据流已经刷新并保存,写入的内容不是立即可见的

Path p = new Path();
    OutputStream out = fs.create(p);
    out.write("content".getBytes("UTF-8"));
    out.flush();
    assertThat(fs.getFileStatus(p).getLen(),is(0L));
总结:当前写入的数据块对其他reader是不可见的,当一个数据块写完之后才对新的reader可见

FsDataOutputStream提供了sync方法,该方法执行成功后,对所有的reader而言,HDFS能保证文件中到目前为止写入的数据均达到所有datanode的写入管道并且对所有新的reader可见
    Path p = new Path("p");
    FsDataOutputStream = fs.create(p);
    out.write("content".getBytes("UTF-8"));
    out.flush();
    out.sync();
    assertThat(fs.getFileStatus(p).getLen(),is(((long)"content".getLength())));
同时out.close()方法也包含了sync方法,调用close也能达到同样的效果。在Hadoop 1.X之后sync已经被启用,提供了新的hflush和hsync两个方法代替
sync方法会让系统产生许多额外的开销,但是不调用此方法又会面临随时可能丢失数据的风险,所以根据系统的具体设计要求需要有一定的取舍才能满足。

3.7 通过Flume和Sqoop导入数据
Apache Flume是一个将大规模流数据导入HDFS的的工具。最典型的应用就是从另一个系统收集日志数据,再导入HDFS处理,实现聚集操作,便于后期分析。Flume的典型配置是在WEB服务器上,每个服务器都是一个Flume节点,各个节点通过多个层级的聚合节点,最后将数据导入HDFS。
Flume提供了不同级别的数据投递可靠性:
最大努力投递:不允许任何Flume节点失效
端到端投递:确保当源节点和HDFS之间有多个Flume节点失效的情况下数据成功投递
Apache Sqoop是为了将数据从结构化设备批量导入HDFS而设计的。引用用场景:将白天数据库生产的数据在晚间导入Hive数据仓库分析

3.8 通过 distcp 并行复制
distcp是Hadoop的一个分布式复制程序,可以从Hadoop文件系统间复制大量数据,也可以将数据复制到Hadoop中。如果两个集群之间使用的是相同版本的Hadoop,那么就非常适合hdfs方案:%hadoop distcp -update(overwrite修改或者覆盖已经存在的文件) hdfs://namenode1/foo hdfs://namenode2/bar 把集群1的/foo目录复制到集群2的bar目录,如果bar不存在则新建
distcp是一个mapreduce作业,靠并行运行的map来完成复制的,没有reduce,每个文件通过一个map进行复制,distcp试图位每一个map分配大致相同大小的数据来执行。
当集群之间的版本不同时,HDFS用distcp复制文件会失败,这时候需要用使用基于只读HTTP协议的HFTP文件系统:%hadoop distcp hftp://namenode1:50070/foo hftp://noamenode2/bar 注意:URI源中需要指定namenode的web端口,这是由dfs.http.address属性决定的,默认为50070
使用新出的Webhdfs协议可以对原集群和目标集群军使用HTTP通信,且不会造成任何不兼容的问题:%hadoop webhdfs://namenode1/foo:50070 webhdfs://namenode2/bar
保持HDFS集群的均衡
distcp -m参数指定map的个数,默认使用每个节点20gemap来复制;也可以使用均衡器工具来使集群达到均衡

3.9 Hadoop存档
hadoop存储每个文件按块的方式存储,每个块的元数据存储在(namenode)内存中,所以存储小文件效率很低,很快会把内存消耗完。值得注意的是,即使文件很小也不会把一个块使用完,是多大也就消耗多大的内存。

3.9.1 使用Hadoop存档工具
Hadoop存档文件可以将文件存入HDFS块,并减少内存的消耗,可以允许对文件的透明访问。可以作为mapreduce的输入。
对文档存档:
%hadoop archive -archiveName files.har /my/files/ /my
.har文件是HAR存档工具所必须的,.har文件的构成:两个索引文件以及部分文件的集合。这些部分文件包含了大量的源文件的内容,可以通过索引寻找到内容。
以递归的方式列出存档文件的部分文件:%hadoop fs -lsr har:///my/files.har
删除存档文件:%hadoop fs -rmr /my/files.har

3.9.2 HAR存档的不足
创建存档文件需要跟源文件一样大小的容量,当对源文件修改删除后不能对存档文件修改。只能重新创建。内存namenode的限制。HAR作为MapReduce的输入时,InputFormat类不知道已经存档。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值