HDFS
带着疑问学!
分布式文件系统那么多为什么Hadoop项目中还要开发出一个hdfs文件系统?
理论知识点
- 存储模型
- 架构设计
- 角色功能
- 元数据持久化
- 安全模式
- 副本放置策略
- 读写流程
- 安全策略
存储模型
- 文件线性按字节切割成块,具有offset,id
- 文件与文件的block的大小可以不一样
- 一个文件除最后一个block,其他block大小一致
- block的大小一句硬件的I/O特性调整
- block被分散存放在集群的节点中,具有location
- Block具有副本,没有主从概念,副本不能出现在同一个节点
- 副本是满足可靠性和性能的关键
- 文件上传可以指定block大小和副本数,上传后只能修改副本数
- 一次写入多次读取不支持修改
- 支持追加数据
架构设计
- HDFS是一个主从( Master/Slaves)架构
- 由一个 NameNode和一些 DataNode组成
- 面向文件包含:文件数据(data)和文件元数据(metadata)
- NameNode负责存储和管理文件元数据,并维护了一个层次型的文件目录树
- DataNode负责存储文件数据(bock块),并提供 block的读写
- DataNode与 Namenode维持心跳,并汇报自己持有的block信息
- Client和NameNode交互文件元数据和DataNode交互文件block数据
角色功能
NameNode
- 完全基于内 目录结构、文件 block的映射
- 需要持久化方案保证数据可靠性
- 提供副本放置策略
DataNode
- 基于本地磁盘存储bock(文件的形式)
- 并保存 block的校验和数据保证block的可靠性
- 与 NameNode保持心跳,汇报 block列表状态
元数据持久化
- 任何对文件系统元数据产生修改的操作, Namenode都会使用一种称为 EditLog的事务日志记录下来
- 使用 Fslmage存储内存所有的元数据状态
- 使用本地磁盘保存 EditLog和 Fslmage
- EditLog具有完整性,数据丢失少,但恢复速度慢,并有体积膨胀风险
- Fslmage具有恢复速度快,体积与内存数据相当,但不能实时保存,数据丢失多
- NameNode使用了 Fslmage+EditLog整合的方案;
- 滚动将增量的 EditLog更新到 Fslmage,以保证更近时点的 Talmage和更小的 EditLog体积
安全模式
-
HDFS搭建时会格式化,格式化操作会产生一个空的 Fslmage
-
当 Namenode启动时,它从硬盘中读取 Editlog和 FsImage
-
将所有EditLog中的事务作用在内存中的 Fslmage上,并将这个新版本的 Fslmage从内存中保存到本地磁盘上 然后删除旧的 Editlog,因为这个旧的 Editlog的事务都已经作用在 Fslmage上了
-
Namenode启动后会进入一个称为安全模式的特殊状态。
-
处于安全模式的 Namenode是不会进行数据块的复制的。
-
Namenode从所有的 Datanode接收心跳信号和块状态报告。
-
每当 Namenode检测确认某个数据块的副本数目达到这个最小值,那么该数据块就会被认为是副本安 全( safely replicated)的,在一定百分比(这个参数可配置)的数据块被Namenode检测确认是安全之后(加上一个额外的30秒 等待时间),Namenode将退出安全模式状态。
-
接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他 Datanode上
HDFS中的SNN
Secondary Node (SNN)
- 在非高可用HA(High Availability)模式下,SNN一般是独立的节点,周期完成对NN的 EditLog向Fslmage合并,减少EdtitLog大小,减少NN启动时间
- 根据配置文件设置的时间间隔 fs.checkpoint.period 默认3600秒
- 根据配置文件设置 edits log大小 fs.checkpoint size规定edits文件的最大值默认是64MB
Block的副本放置策略
- 第一个副本:放置在上传文件的DN;如果是集群外提交,则随机挑选一台磁盘不太满,CPU不太忙的节点。
- 第二个副本:放置在于第一个副本不同的机架的节点上。
- 第三个副本:与第二个副本相同机架的节点。
- 更多副本:随机节点。
HDFS写流程
NameNode--NN;DateNode--DN
- Client和NN连接创建文件元数据
- NN判定元数据是否有效
- NN触发副本放置策略,返回一个有序的DN列表
- Client和DN建立 Pipeline连接
- Client将块切分成 packet(64KB),并使用 chunk(512B)+ checksum(4B)填充
- Client将 packet放入发送队列 dataqueue中,并向第一个DN发送
- 第一个DN收到 packet后本地保存并发送给第二个DN
- 第二个DN收到 packet后本地保存并发送给第三个DN
这一个过程中,上游节点同时发送下一个 packet,生活中类比工厂的流水线。
结论:流式其实也是变种的并行计算
HDFS使用这种传输方式,副本数对于clent是透明的。当block传输完成,DN们各自向NN汇报,同时client继续传输下一个block 所以,client的传输和 block的汇报也是并行的。
HDFS读流程
- 为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本。
- 如果在读取程序的同一个机架上有一个副本,那么就读取该副本。
- 如果一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本。
- 语义:下载一个文件:
- Client和NN交互文件元数据获取fileBlockLocation
- NN会按距离策略排序返回
- Client尝试下载 block并校验数据完整性
- 语义:下载一个文件其实是获取文件的所有的block元数据,那么子集获取某些block应该成立
HDFS支持Client给出文件的offset自定义连接哪些block的DN,自定义获取数据。这个是支持计算层的分治、并行计算的核心!!
实操&理论验证
HDFS安装
HDFS命令行使用
理论知识点验证
基础设施
操作系统、环境、网络、必须软件
- 设置IP及主机名
- 关闭防火墙&selinux
- 设置hosts映射
- 时间同步
- 安装JDK
- 设置SSH免秘钥
bin-放功能命令
sbin-放服务脚本命令
HDFS-HA解决方案
HA方案:
- 多台NN主备模式,Active和 Standby状态,Active对外提供服务
- 增加 journalnode 角色(>3台),负责同步NN的 editlog 最终一致性
- 增加zkfc角色(与NN同台主机),通过 zookeeper集群协调NN的主从选举和切换事件回调机制
- DN同时向NNs汇报block清单
CAP原则:
Consistency:一致性
Availability:可用性
Partition tolerance:分区容忍性
三者不可兼得,三者只能尽量满足两者
Hadoop-HDFS-HA
思路
- 主从集群:结构相对简单,主与从协作
- 主:单点,数据一致,好掌握
- 问题
- 单点故障,集群整体不可用
- 压力过大,内存受限
HDFS解决方案
单点故障
高可用方案:HA(High Available)
多个NN,主备切换,主
压力过大,内存受限:
联邦机制:Federation(元数据分片)
多个NN,管理不同的元数据
Hadoop 2.x 只支持HA的一主一备
Paxos算法
Paxos算法是莱斯利兰伯特于1990年提出的一种基于消息传递的一致性算法。 这个算法被认为是类似算法中最有效的。 该算法覆盖全部场景的一致性。 每种技术会根据自己技术的特征选择简化算法实现。 传递:NN之间通过一个可靠的传输技术,最终数据能同步就可以 我们一般假设网络等因素是稳定的 类似一种带存储能力的消息队列
简化思路
分布式节点是否明确
节点权重是否明确
强一致性破坏可用性
过半通过可以中和一致性和可用性
最简单的自我协调实现:主从
主的选举:明确节点数量和权重
主从的职能:
主:增删改查
从:查询吗,增删改传递给主
主与从:过半数同步数据
HDFS-Federation 解决方案
NN的压力过大,内存受限问题
元数据分治,复用DN存储
元数据访问隔离性
DN目录隔离Block
HDFS-HA实操配置
流程:
1、基础设施
ssh免密:
1)启动 start-dfs.sh脚本的机器需婆将公钥分发给別的节点
2)在HA模式下,每一个NN身边会启动ZKFC
ZKFC会用免密的方式控制自己和其他NN节点的N状态
2、应用搭建
HA依赖ZK 搭建ZK集群
修改 hadoop的配置文件,并集群同步
3、初始化启动
1)先启动 hadoop-daemon sh start journalnode
2)选择一个NN做格式化: hdfs namenode -format
<只有第一次搭建做,以后不用做>
3)启动这个格式化的NN,以备另外一台同步 hadoop-daemon.sh start namenode
4)在另外一台机器中: hdfs namenode -bootstrapStandby
5)格式化zk:hdfs zkfc -formatZK
<只有第一次搭建做,以后不用做>
6) start-dfs.sh
4、使用
实操:
HDFS的权限
hdfs是一个文件系统
类unix、linux 有用户概念和权限概念,但是hdfs没有相关命令和接口去创建用户。
默认情况使用 操作系统提供的用户 -> 信任客户端,扩展kerberos LDAP继承第三方用户认证系统
有超级用户的概念
linux系统中的超级用户是root
hdfs中的超级用户是namenode进程的启动用户
权限概念
hdfs的权限是自己控制的,来自于hdfs的超级用户
面向操作系统 root是管理员,其他用户都叫普通用户
面向操作系统的软件 谁启动,管理这个进程,那么这个用户叫做这个软件的管理员
实操
切换我们用root搭建的HDFS 用god这个用户来启动
node01~node04
-
stop-dfs sh
-
添加用户:root
useradd god
passwd god
-
讲资源与用户绑定(a,安装部署程序 b,数据存放的目录)
chown -R god src
chown -R god /opt/bigdata/hadoop-2.6.5
-
切换到god去启动
start-dfs.sh
需要免密 给god做免密 因为我们是HA模式所以免密的两种场景都要做
ssh 1oca1host #>为了拿到.ssh
#node0l-node02:
cd /home/god/.ssh
sh-keygen-t dsa -P '' -f ./id_dsa
#node01:
ssh-copy-id -i id_dsa node01
ssh-copy-id -i id_dsa node02
ssh-copy-id -i id_dsa node03
ssh-copy-id -i id_dsa node04
#node02:
cd/home/god/ ssh
ssh-copy-id -i id_dsa node01
ssh-copy-id -i id_dsa node02
#hdfs-site xml
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/god/.ssh/id_dsa</value>
</property>
#分发给node02~04
5)god: start-dfs.sh
用户权限验证实操
#node01:
su god
hdfs dfs -mkdir /temp
hdfs dfs -chown god: ooxx /temp
hdfs dfs -chmod 770/temp
#node04:
root:
useradd good
groupadd ooxx
usermod -a-G ooxx good
id good
su good:
hdfs dfs- mkdir/temp/abc #失败
hdfs groups #失败 因为hdfs己经启动了,不知道你操作系统又偷偷摸摸创建
#*nodel
root:
useradd good
groupadd ooxx
usermod -a -G ooxx good
su god:
hdfs dfsadmin -refreshUserToGroupsMappings
#node04
good:
hdfs groups
good: good ooxx
结论:默认hdfs依赖操作系统上的用户和组
HDFS api实操
使用IDEA开发
实际上在做的事:
开发HDFS的Client
如何获取权限
- 参考系统登录用户名
- 参考环境变量
- 代码中给出 HADOOP USER NAME god
注意:
- 获取权限这一步操作要在启动IDEA前先做
- 集群和开发环境jdk版本确保一致
- 使用maven构建 https://mvnrepository.com
- HDFS的pom:
hadoop: (common,hdfs,yarn,mapreduce)
public class TestHDFS{
public Configuration conf = null;
public FileSystem fs = null;
@Before
public void conn() throws Exception{
conf = new Configuration(true);
fs = FileSystem.get(conf);
}
@Test
public void mkdir(){
Path dir = new Path("/msb01");
if(fs, exists(dir)){
fs.delete(dir, true);
}
fs mkdir(dir);
}
@Test
public void upload() throws Exception{
BufferedInputStream input = new BufferedInputStream(new FileInputStream(New File("./data/hello.txt")));
Path outfile = new Path("/msb/out.txt");
FSDataOutputStream output = fs.create(outfile);
IOUtil.copyBytes(input,output,conf,true);
}
@Test
public void blocks() throws Exception{
Path file = new Path("/user/god/data.txt");
FileStatus fss = fs.getFileStatus(file);
BlockLocation[] blks = fs.getFileBlockLocations(fss,0,fss.getLen());
for(BlockLocation b : blks){
System.out.println(b);
}
// 0~1048576 node04,node02
// 1048576~540319 node04,node03
// 计算向数据移动,其实用户和程序读取的是文件这个级别,并不知道有块的概念
FSDataInputStream in = fs.open(file);//面向文件打开的输入流 无论怎么读都是从文件开始读起
// blk01:he
//blk02:llo msb 66231
System.out.println((char)in.readByte());
System.out.println((char)in.readByte());
System.out.println((char)in.readByte());
System.out.println((char)in.readByte());
System.out.println((char)in.readByte());
System.out.println((char)in.readByte());
System.out.println((char)in.readByte());
System.out.println((char)in.readByte());
/**执行结果:
h
e
l
l
o
m
s
b
1
*/
}
@After
public void close() throws Exception{
fs.close();
}
}
MapReduce
思考:为什么叫MapReduce?
所有计算机的动作和处理都可以统称为计算->compute
像以上三种需求所做的事情,可以发现它们都是对单一一条数据做处理,由此我们可以总结Map是什么做了什么事
Map:以一条记录为单位做映射
按照专业统计每个专业有多少人在学
Reduce:以一组为单位做计算
前置约束:什么叫一组->做分组->依赖一种数据格式:key-value键值对
所以所有计算的动作基本就分为两类一类是Map,一类是Reduce。分别是以一条为单位和以一组为单位
归纳总结
着重分析两张图
总结比例关系:
-
block 和 split 1:1
N:1
1:N
-
split 和 map
1:1
-
map 和 reduce
N:1
N:N (reduce数量变成N个切记一点,分组不能被打散、分割的)
1:1
1: N
-
group(key)和 partition
1:1
N:1
N:N
1:N
MapReduce :数据以一条记录为单位经过map方法映射成Key-Value,相同的Key为一组,这一组数据调用一次 reduce方法,在方法内迭代计算这一组数据。
知识点总结
- 切片会格式化出记录,以记录为单位调用map方法
- map的输出映射成KV,KV会参与分区计算,拿着Key算出P(分区号),也就是K、V、P
- mapTask的输出是一个文件,存在本地的文件系统中
- 内存缓冲区溢写磁盘时,做一个2次排序,达到分区有序且分区内Key有序的目的,使得未来相同的一组Key会相邻地排在一起
- reduce的归并排序其实可以和reduce方法的计算同时发生,尽量减少IO,因为有迭代器模式的支持
- 迭代器模式是批量计算中非常优美的实现形式
场景模拟
模拟从单条数据到分组的流程
注:Abc,null,1依次是Key,Value,Partition
模拟统计单词出现次数
模拟统计相同词频的个数