1. HDFS概述
2. HDFS的Shell操作
3. Python API操作HDFS
4. HDFS的读写流程
5. NN和2NN
6. Datanode工作机制
1. HDFS概述1.1 HDFS产出背景及定义
1)HDFS产生背景
随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种。
2)HDFS定义
HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
HDFS的使用场景:适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后就不需要改变。
1.2 HDFS优缺点
优点:
1)高容错性
数据自动保存多个副本,它会通过增加副本的形式,提高容错性。如果某一个副本丢失了,它可以自动从别的副本上拷贝一份过来。
2)适合处理大数据
1. 能够处理数据规模达到GB,TB,甚至PB级别的数据
2. 能够处理百万规模以上的文件数量
3)可构建在廉价的机器上,通过副本机制,提高可靠性
缺点:
1)不适合做低延时数据访问,比如要求毫秒级别的存储数据,是做不到的。
2)无法高效的对大量小文件进行存储
1. 存储大量的小文件,那么会占用NameNode大量的内存来存储文件目录和块信息
2. 小文件存储的寻址时间会超过读取时间,这样就违反了HDFS的设计目标
3)不支持并发写入,并且文件数据只能追加不能修改
1.3 HDFS组成架构
1)NameNode(nn):就是Master,它就像是一个主管,管理者。
(1)管理HDFS的名称空间
(2)配置副本的策略,比如A文件存几个副本
(3)管理数据块(Block)映射信息
(4)处理客户端读写请求
2)DataNode(DN):就是Slave,NameNode下达指令,DataNode执行实际的操作
(1)存储实际的数据块
(2)执行数据块的读写操作
3)Client:客户端
(1)文件切分,文件上传HDFS的时候,Client将文件切分成一个一个的Block然后上传
(2)与NameNode交互,获取文件的位置信息
(3)与DataNode交互,读取或者写入数据
(4)Client提供一些命令来管理HDFS,比如NameNode格式化
(5)Client可以通过一些命令访问HDFS,比如增删改查
4)Secondary NameNode(2nn):相当于NameNode的秘书,当NameNode挂了,它保存了NameNode的备份,但是它并不是热备,因此它不能马上替换NameNode和服务。
(1)辅助NameNode,分担工作量
(2)在紧急情况下,可以辅助回复NameNode(只能恢复一部分)
1.4 HDFS文件块大小
HDFS中文件在物理上是分块储存的,块的大小可以通过参数(dfs.blocksize)来规定,默认大小在Hadoop2.X/3.X中是128M,1.X版本中是64M。
那么数据块的大小应该设置多少合适?
(1)一般寻址时间为10ms
(2)寻址时间为传输时间的1%
(3)看自己的磁盘传输的速度了,如果是固态硬盘传输的快,可以把block换成256M
一般情况下这个值不能设置的太大,也不能设置的太小,
(1)如果设置的太小,那么会增加寻址时间
(2)如果块设置的太大,从磁盘传数据的时间会明显大于定位这个块开始位置所需的时间,导致处理块上数据的时候就会非常非常慢。
总结:HDFS块大小设置主要取决于磁盘传输的速率,一般情况下128M,传输速度快的话256M
2. HDFS的Shell操作2.1 基本语法
hadoop fs + 具体命令 OR hdfs dfs + 具体命令
2.2 命令大全
2.2.1 准备工作
# 输出该命令参数 hadoop fs -help rm # 启动集群 sbin/start-dfs.sh # 启动yarn sbin/start-yarn.sh # 创建一个文件夹 hadoop fs -mkdir /sanguo
2.2.2 上传
1)-moveFromLocal:从本地剪切粘贴到HDFS vim shuguo.txt 输入: shuguo hadoop fs -moveFromLocal ./shuguo.txt /sanguo 2)-copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去 vim weiguo.txt 输入: weiguo hadoop fs -copyFromLocal weiguo.txt /sanguo 3)-put:等同于copyFromLocal,生产环境更习惯用put vim wuguo.txt 输入: wuguo hadoop fs -put ./wuguo.txt /sanguo 4)-appendToFile:追加一个文件到已经存在的文件末尾 vim liubei.txt 输入: liubei hadoop fs -appendToFile liubei.txt /sanguo/shuguo.txt
2.2.3 下载
1)-copyToLocal:从HDFS拷贝到本地 hadoop fs -copyToLocal /sanguo/shuguo.txt ./ 2)-get:等同于copyToLocal,生产环境更习惯用get hadoop fs -get /sanguo/shuguo.txt ./shuguo2.txt
2.2.4 HDFS直接操作
1)-ls: 显示目录信息 hadoop fs -ls /sanguo 2)-cat:显示文件内容 hadoop fs -cat /sanguo/shuguo.txt 3)-chgrp、-chmod、-chown:Linux文件系统中的用法一样,修改文件所属权限 4)-mkdir:创建路径 hadoop fs -mkdir /jinguo 5)-cp:从HDFS的一个路径拷贝到HDFS的另一个路径 hadoop fs -cp /sanguo/shuguo.txt /jinguo 6)-mv:在HDFS目录中移动文件 hadoop fs -mv /sanguo/wuguo.txt /jinguo hadoop fs -mv /sanguo/weiguo.txt /jinguo 7)-tail:显示一个文件的末尾1kb的数据 hadoop fs -tail /jinguo/shuguo.txt 8)-rm:删除文件或文件夹 hadoop fs -rm /sanguo/shuguo.txt 9)-rm -r:递归删除目录及目录里面内容 hadoop fs -rm -r /sanguo 10)-du统计文件夹的大小信息 hadoop fs -du -s -h /jinguo 27 81 /jinguo hadoop fs -du -h /jinguo 14 42 /jinguo/shuguo.txt 7 21 /jinguo/weiguo.txt 6 18 /jinguo/wuguo.tx 说明:27表示文件大小;81表示27*3个副本;/jinguo表示查看的目录 11)-setrep:设置HDFS中文件的副本数量 hadoop fs -setrep 10 /jinguo/shuguo.txt 12)单节点启动命令 hdfs --deamon start datanode
这里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10。
3. Python API操作HDFS
3.1 安装:pip install hdfs
3.2 创建集群连接
from hdfs import *
client = Client("http://hadoop100:9870",root='/')
Client里边有一些参数:
classhdfs.client.Client(url, root=None, proxy=None, timeout=None, session=None)
url:ip和端口号
root:指定hdfs的根目录
proxy:指定用户身份
timeout:设置超时
session:连接标识
from hdfs import *
client = Client("http://hadoop100:9870",root='/')
# list()方法,会列出指定路径下的文件信息,等于hadoop fs -ls
# hdfs_path:要列出的路径
# status:默认False,是否显示详细信息
print('hdfs目录:',client.list(hdfs_path='/',status=True))
# status()方法会查询文件或者文件夹的状态
# hdfs_path:要查询的文件路径或者文件夹的路径
# staict:是否开启严格模式,严格模式下目录或文件不存在不会返回None,而是raise
print(client.status(hdfs_path='/jinguo/shuguo.txt',strict=False))
# makedirs()创建文件夹,等于hadoop fs -mkdir + hadoop fs -chmod
# hadfs_path:创建文件的路径
# permission:文件权限
print("创建目录", client.makedirs(hdfs_path="/python_test", permission="755"))
# rename()文件或者文件夹
# hdfs_src_path:原始的
# hdfs_src_path:修改后的
client.rename(hdfs_src_path="/python_test",hdfs_dst_path="/python.test")
# resolve()返回绝对路径,接收一个参数hdfs_path
print(client.resolve("python.test"))
# set_replication()设置文件副本数
# hdfs_path 文件路径
# replication 副本数量
client.set_replication(hdfs_path="/jinguo/wuguo.txt",replication=10)
# read()读取文件信息,相当于hadoop fs -cat
# hdfs_path hdfs:路径
# offset:读取位置
# length:读取长度
# buffer_size:用于传输数据的字节的缓冲区的大小。默认值设置在HDFS配置。
# encoding:指定编码
# chunk_size:如果设置为正数,上下文管理器将返回一个发生器产生的每一chunk_size字节而不是一个类似文件的对象
# delimiter:设置分隔符,必须和encodeing一起设置
# progress:读取进度回调函数 读取一个chunk_size回调一次
# 读取指定长度
with client.read('/jinguo/shuguo.txt',length=50,encoding='utf-8') as obj:
for i in obj:
print(i)
# 从某个位置开始读,读取去指定长度
with client.read('/jinguo/shuguo.txt',offset=2,length=50,encoding='utf-8') as obj:
for i in obj:
print(i)
# 设置buffer
with client.read('/jinguo/shuguo.txt', buffer_size=1024, encoding='utf-8') as obj:
for i in obj:
print(i)
# 设置分隔符为换行,这个参数优点向列表中的切分,设置完这个参数之后,obj的类型就变成了一个迭代器
with client.read('/jinguo/shuguo.txt', encoding='utf-8', delimiter='n') as obj:
for i in obj:
print(type(obj))
print(i)
# 设置读取每个块的大小为8,经过chunk_size,ojb也会变成迭代器,并且设置多大就会产生该大小的字节
with client.read("/jinguo/shuguo.txt", encoding='utf-8', chunk_size=5)as obj:
for i in obj:
print(i)
# download()从hdfs下载文件到本地,等于hadoop fs -copyToLocal /jinguo/wuguo.txt /
# hdfs_path hdfs路径
# local_path 下载到的本地路径
# overwrite 是否覆盖(如果有同名文件) 默认为Flase
# n_threads 启动线程数量,默认为1,不启用多线程
# temp_dir下载过程中文件的临时路径
print("下载文件:", client.download(hdfs_path="/jinguo/wuguo.txt", local_path="~/",overwrite=True))
# upload()上传文件到hdfs,等于hadoop fs -copyFromLocal local_file hdfs_path
# hdfs_path, hdfs上位置
# local_path, 本地文件位置
# n_threads=1 并行线程数量
# temp_dir=None 文件的临时路径
# overwrite=True 是否覆盖(如果有同名文件) 默认为Flase
# chunk_size 文件上传的大小区间
# progress=None, 报告进度的回调函数 完成一个chunk_size回调一次 chunk_size可以设置大点 如果大文件的话
# cleanup=True, 上传错误时 是否删除已经上传的文件
def callback(filename, size):
print(filename, "完成了一个chunk上传", "当前大小:", size)
if size == -1:
print("文件上传完成")
# 上传成功返回 hdfs_path
client.upload(hdfs_path="python.test", local_path=r"data/input.zh.txt", chunk_size=2 << 19, progress=callback, cleanup=True)
# delete()删除文件,等于hadoop fs -rm(-r)
# hdfs_path 路径
# recursive=False 是否递归删除
# skip_trash=True 是否移到垃圾箱而不是直接删除 hadoop 2.9+版本支持
client.delete('/python.test/input.zh.txt')
# set_owner()等于hadoop fs -chown root root hdfs_path修改目录或文件的所属用户,用户组,接收三个参数
# hdfs_path hdfs路径
# owner 用户
# group 用户组
client.set_owner(hdfs_path="python.test", owner="root", group="root")
# set_permission修改权限,等于hadoop fs -chmod 777 hdfs_path
# hdfs_path hdfs路径
# permission 权限
client.set_permission(hdfs_path="python.test",permission='755')
这么些Python API 操作HDFS,基本上和上述的Shell命令一一对应着来的,当然还有很多其他的一些操作,等后续更新一个比较全面的Shell命令操作文档和Python操作文档。
4. HDFS的读写流程(重点)4.1 HDFS写入数据
(1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
(2)NameNode返回是否可以上传。
(3)客户端请求第一个 Block上传到哪几个DataNode服务器上。
(4)NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
(5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
(6)dn1、dn2、dn3逐级应答客户端。
(7)客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
(8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
4.2 网络拓扑-节点距离计算
节点距离:两个节点到达最近的共同祖先的距离总和。
4.3 副本储存节点选择
1. 首选是本地的节点进行储存
2. 其次会选择另一个机架的一个节点储存
3. 第三个副本会存储再和第二个相同机架上的不同节点上
4.4 HDFS读数据的流程
(1)客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
(2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
(4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
5. NN和2NN的工作机制5.1 HDFS存储位置
首先NameNode中的元数据存放在哪里,如果是存放在内存当中,那么计算快,但是可靠性很差,如果存放在磁盘当中,那么可靠性比较高,但是计算速度就会很慢,这时候来个东西:FsImage(在磁盘中备份元数据)。但是,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效率过低,但如果不更新,那么数据就可能丢失,仍然是不安全的。因此又引入了Edits文件(它只进行追加操作,效率很高,有点像docker中提交任务只保存修改一样),每当元数据有更新或者添加元数据的时候,修改内存中的元数据,并且追加到Edis中,这样一旦NameNode节点挂了,仍然可以通过FsImage和Edits的合并,合成元数据。
但是,如果长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且恢复元数据需要的时间过长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并。
1)第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode记录操作日志,更新滚动日志。
(4)NameNode在内存中对元数据进行增删改。
2)第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
(2)Secondary NameNode请求执行CheckPoint。
(3)NameNode滚动正在写的Edits日志。
(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件fsimage.chkpoint。
(7)拷贝fsimage.chkpoint到NameNode。
(8)NameNode将fsimage.chkpoint重新命名成fsimage。
5.2 Fsimage和Edits解析
FsImage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息。
Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到Edits文件中。
seen_txid:该文件保存了一个数字,就是最后一个edits_X 的数字。
每次NameNode启动的时候,都会将FsImage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的,同步的,可以看成NameNode启动的时候九江FsImage和Edits文件进行了合并。
查看语法:hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径
# 查看FsImage cd /opt/module/hadoop-3.1.3/data/dfs/name/current hdfs oiv -p XML -i fsimage_0000000000000000213 -o /opt/software/fsimage.xml cat /opt/software/fsimage.xml # 查看Edits cd /opt/module/hadoop-3.1.3/data/dfs/name/current hdfs oev -p XML -i edits_inprogress_0000000000000000227 -o /opt/software/eidts.xml cat /opt/software/eidts.xml6. DataNode的工作机制
(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
(2)DataNode启动后向NameNode注册,通过后,周期性(6小时)的向NameNode上报所有的块信息。
(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过 10分钟+30秒 没有收到某个DataNode的心跳,则认为该节点不可用。
(4)集群运行中可以安全加入和退出一些机器。
注意:上述所提到的,2NN中的FsImage和Edits合并时间,包括DN中的默认6个小时和十分钟 + 30秒,这些配置都在hdfs-default.xml中可以查看。
6.1 数据的完整性
注意:Hadoop没有使用最简单的就校验,使用的是更为复杂的crc校验方式。
总结:
1. HDFS文件大小,取决于硬盘传输速度,一般128M或者256M。
2. HDFS的Shell操作
3. HDFS读写流程
4. NN,2NN,DN工作机制



