hadoop学习笔记(一)hadoop分布式文件系统
超大文件;
流式数据访问,一次写入多次读取;
商用硬件,庞大的集群遇到少部分节点故障时,任然更够继续运行,且用户察觉不到中断;
不适合低时间延迟的数据访问,对于低时间延迟的需求,可以考虑hbase;
大量小文件的元数据会占用namenode过多的内存资源;
hdfs只能有一个writer,写操作总是将数据添加至文件末尾。不支持多个写入者操作,也不支持文件在任意位置修改。
HDFS的概念
数据块
磁盘系统有块的概念,指磁盘进行数据读写的最小单位,一般为512字节;HDFS中的块默认为64M,一个大的文件被划分为多个分块,作为独立的存储单元。注意,HDFS中小于一个块大小的文件不会占据整个块的空间,存储小文件会增加namenode的负载,而不会增加在datanode中的存储容量。(why?)HDFS块比磁盘块大,是为了最小化寻址开销。也不可过大,MapReduce中的map任务通常一次处理一个块中的数据,如果任务数太少,将会降低作业速度。
好处:可以存储大于单个磁盘容量的文件;简化存储子系统的设计;适合数据备份的需要。
namenode和datanode
主从结构
namenode:文件系统的命名空间 -- 命名空间镜像文件和编辑日志文件。存储 文件块与数据节点之间的对应关系
datanode:存储数据块,并定期向namenode发送它们所存储的块的列表。
namenode容错:1、将持久状态写入本地磁盘的同时写入一个远程挂载的网络文件系统,这是一个原子操作;2、运行一个辅助namenode,定期通过编辑日志合并命名空间镜像,以防止编辑日志过大,他会保存合并后的命名空间镜像的副本,在namenode发生故障时启用。但总是滞后于namenode
基本文件系统操作
从本地复制文件到HDFS
% hadoop fs -copyFromLocal input/docs/quangle.txt hdfs://localhost/user/tom/quangle.txt 注:hdfs://localhost/可以改为fs.default.name的值
从HDFS拷贝到本地文件系统
% hadoop fs -copyToLocal quangle.txt quangle.copy.txt
Hadoop文件系统
Local,HDFS,HFTP,HSFTP,HAR,hfs,FTP,s3(原生),s3(基于块)
Java接口
从hadoop url中读取数据
使用java.net.URL对象,但需要FsUrlStreamHandlerFactory实例调用URL.setURLStreamHandlerFactory()方法,弊端:jvm只能调用一次上述方法,有第三方在使用的情况下,用户将无法使用。
通过FileSystem API 读取数据
使用FileSystem API打开一个文件的输入流。使用Configuration确定文件系统类型。
FSDataInputStream类
FileSystem类的open()方法返回的是FSDataInputStream对象。
实现了seekable、positionedreadable两个接口
seekable:支持随机访问,seek()可以移到文件的任意一个绝对位置,skip()则是相对于当前位置进行移动。
positionedreadable:从一个指定偏移量处读取文件的一部分
seek()开销较高,尽量采用流数据来构建应用的访问模式(如使用MapReduce)
另:IOUtils类是一个跟IO相关的工具类,对输入输出流进行控制。
写入数据
create()方法创建新文件,append()方法将数据流追加到文件的末尾处,progress()方法与进度有关。
目录
创建目录:mkdirs(),create()也可以在生成文件时创建对应的目录(若目录不存在)
查询文件系统
文件元数据
FileStatus类,封装了文件系统中文件和目录的元数据。FileSystem类的getFilesystem方法可以返回FileStatus对象。
列出文件
FileStatus对象的listStatus()方法返回文件或文件夹的FileStatus对象并存入一个数组
文件模式
单次操作处理一批文件,采用通配符是一个不错的选择。FileSystem提供了globStatus(Path pathPattern) 和globStatus(Path pathPattern,PathFilter filter)两个方法,返回所有匹配文件的FileStatus对象数组,并按照路径排序。PathFilter对匹配作进一步限制。Hadoop支持的通配符与Unix bash相同,就不罗列了。
PathFilter对象
过滤器类,是Path对象而不是File对象。
删除数据
显然,用delete()方法
至此,FileSystem API终于结束了。
数据流
文件读取分析
个人理解:
用户想读取一个文件,向分布式系统发送一个请求(1),文件系统就会通过RPC的将访问的文件名等信息传递给namenode,namenode返回该文件被切分的块所在的datanode地址给文件系统,并按照一定规则对datanode排序,优先访问较近的datanode。分布式系统就会返回一个FSDataInputStream对象给用户,FSDataInputStream对象封装了DFSInputStream对象,该对象对datanode和namenode的IO进行管理。之后用户对这个输入流进行调用(3),根据排序好的datanode序列去依次读取,当然,每个块具体存储在哪个位置要问datanode才知道。读完一个数据块(4),继续读取其他块(5),直到结束并关闭数据流(6)。当然,如果数据块比较多,可能就不能一次将块与datanode地址的对应关系传递给FSDataInputStream对象,就要再读取过程中反复访问namenode了。
文件写入剖析
与读取操作有很多类似的地方。只说一些不同点,用户请求传递到namenode,namenode将做一些检测(用户权限、文件是否已存在等),没问题了,就会在namenode添加创建文件的记录。FSdataOutputStream封装了OutputStream类,负责通信。客户端写入数据时(3),FSdataOutputStream将文件分成一个个数据包,并写入内部队列,DataStreamer负责处理数据队列,他的责任是根据datanode列表要求namenode分配合适的新块存储数据备份,及块与datanode的对应关系。这些datanode构成一个管线。写入到第一个datanode后,数据是直接从datanode传递到另一个datanode,该过程不经过客户端和namenode。
当然,这其中也涉及到写入过程中datanode宕机的情况以及namenode是如何分配块与datanode的对应关系的。容错的具体内容比较复杂,就不写了。而副本布局的问题则要考虑可靠性、写入带宽和读取带宽之间的平衡。关于副本的存放,可以参考下图。若副本数大于3,其他节点将随机存放,但也会考虑负载均衡、避免一个机架上副本过多等情况。
一致模型
一致模型描述了文件读写的数据可见性。写入文件并不保证能立即可见,当写入的数据超过一个块时,新的reader就可以看到第一个块了,却看不到当前正在写入的块。FSDataOutputStream调用sync()方法,强制所有的缓存与数据节点同步。hdfs关闭文件其实隐含的调用了sync()方法。注意:不使用sync()在客户端或系统发生故障时可能会丢失一个数据块,但该方法有额外开销,所以在数据鲁棒性与吞吐量之间要做一定取舍,视不同应用而定,确定一个较好的使用频率。
通过distcp并行复制
主要是用于两个hadoop集群之间的并行复制,对于已有的文件,可以选择覆盖、保留和update三种操作。例如:
% hadoop distcp -update hdfs://namenode1/foo hdfs://namenode2/bar/foo
distcp是作为一个MapReduce作业来实现的,以上对不同版本的hadoop之间的数据传输并不适用。不同版本之间数据传输要使用基于只读http协议的HFTP文件系统从源文件系统中读取数据,例如:
% hadoop distcp hftp://namenode1:50070/foo hdfs://namenode2/bar
通过使用webhdfs也可以解决上述问题,这样就不会触及任何兼容性的问题(第三版新增内容)。例如:
% hadoop distcp webhdfs://namenode1:50070/foo webhdfs://namenode2:50070/bar
保持hdfs集群的均衡
若只有一个map来执行distcp任务,则第一个副本存放在map任务运行的节点,直到该节点被填满。最好使用默认的每个节点20个map来运行distcp命令。另外,可以使用均衡器改善块分布状况。
hadoop存档
archive工具对文件进行存档,存档文件(.har)包括两个索引文件以及若干个部分文件,这些部分文件包含了已经链接的大量原始文件的集合。 个人理解:在datanode上的又一层索引
har文件必须以.har结尾,要想增加和删除文件必须重建存档文件,在mapreduce中处理har文件是低效的。处理小文件的另一方案是使用“CombineFileFormat”,将在后续内容中提到。