hdfs
namenode的作用
主要负责命名空间和文件数据块的地址映射。
整个集群的大小受限于namenode的内存大小。
- 存储元数据信息
其包含一个文件的添加时间,大小,权限,块列表(大于【默认128M】的文件会被切割成多个块),以及每个块的备份信息。
该元数据信息保存在内存中。fsimage和edits提供数据持久化 - namenode文件操作
处理文件存放请求,确定文件块以及副本的具体存放位置。
但是真正的数据文件不经过namenode,而是客户端根据namenode返回的datanode信息,与datanode进行通信 - namenode副本
根据当前集群的情况来确定副本的存储位置。 - namdenode心跳机制
周期性的接受DN的心跳和块状态报告。
接收到心跳意味着结点正常,长时间没有接收到意味着结点宕机,namenode会将宕机节点上的数据块进行重新复制到其他结点。
块的状态报告包含了DN上的所有数据快列表,一小时发送一次。
datanode的作用
提供真实的文件存储服务
- 以数据块的形式存储文件
- 响应客户端的读写请求
- 向namenode发送心跳包
- 向namenode发送数据块信息
- 向namenode发送数据块缓存信息
数据块缓存信息是,如果结点的上的某个数据块的访问频率比较高,那么就会将该数据块放到内存中,以提高访问效率。
副本机制和机架感知
副本机制,一份数据默认存储3份。可以在hdfs-sizte.xml中进行配置。
机架感知策略,和所谓的两地三中心策略相似。
namenode在存储数据时,会首先确定数据的存放结点。然后在本机架上的另外一台机器上存储第一个副本,最后在另外机架上选择一台机器存储第二个副本。namenode的数据选择首先是数据本体机器,其次是相同机架上的机器,最后是另外机架上的副本。
命令
hdfs dfs 命令开头,可以使用大部分linux文件系统的命令来操作hdfs
上传
hdfs dfs -put wenjia /目录
删除命令
hdfs dfs -rm /a.txt
删除后的文件会移动到回收站中。
可以使用 -skipTrash
数据追加
hdfs dfs -appendTOFile file1 file2 合并后文件名
可用于小文件的合并
设置文件限额
安全模式
集群在启动后会进行安全检查,对所有副本率小于0.999的文件块进行复制。
默认的文件副本率:0.999
安全模式下,文件系统只接受读数据请求,不接受删除,修改等变更请求。
文件写入过程
- client发送上传请求
- NN进行权限检查,上传权限,返回可以上传回应
- client进行切分文件,分为多个文件block
- client上传第一个block
- NN根据系欸但那的block信息和机架感知策略选取三个datanode,返回给client
- client和第一个DN之间建立上传管道(pipeline),第二个和第三DN结点互相建立管道。形成client-1-2-3的管道。
- client向DN传递数据,传递的数据不是一整个数据块,而是一个个小的packet【64k】
DN收到数据后,首先在本地进行缓存,然后将数据传递给DN2,DN2传递给DN3。使得数据在管道里从client流到DN3。
其中Client发送数据与数据在DN之间的传递是并行的。也就是说Client在传递了第一个packet,被DN1接收了之后就会传递第二个,然后开启应答队列来确保每个packet都被接收到了,DN3完成数据接受返回给DN2,DN2传递给DN1,最后返回给client完成整个block的传输
一个block传递完成后,再次请求NN获取block的存储结点,然后重复发送步骤。
- 所有数据发送完成过后,DN会向NN报告block修改信息
- NN接受block修改信息,记录在edits日志中,周期性的合并到fsimage。
文件的读取流程
- client请求下载文件,RPC
nn进行权限检查,文件block列表检查,选取出合适的结点进行返回
如果一个文件过大,由多个文件块组成,那么在选取时会根据副本的结点优先选取较少的结点。举例说明:
一个文件由三个block组成
bl1:dn1,dn2,dn3
bl2:dn2,dn3,dn4
bl3:dn1,dn3,dn4
那么返回的结点可能为dn1,dn4等几种情况,一般会少于三个结点
节点数量少,但是block对应的地址不会少。
client分别与返回的DN建立管道
- dn开始发送数据,数据同样为切割后的packet
- client收到数据进行ack回应,dn继续发送
- 直到所有block传输完成,client进行文件块合并组成完成的文件。
元数据管理
fsimage:存放元数据镜像文件(内存中元数据的备份)
edits:存放最近一段时间堆元数据的修改操作
NN重启后会将二者都读取到内存中,然后将edits文件的内容重新执行。
二者结合起来保证NN重启前后元数据的一致性。
fsimage文件的查看方法
在fsimge目录下找到fasimgexxxx文件。
hdfs oiv -i 文件名 -p 查看格式(xml)-o hello.xml
-i指定需要查看的文件
-p指定查看格式
-o将fsimage文件转成新的文件
edits文件的查看方法
查看方式和fsimage类似
hdfs oev -i 文件名 -p 格式 -o 文件名
SecondaryNameNode的作用
其主要作用是帮助NN减轻元数据处理的压力
SNN会对fsimge和edits进行检测。
每隔一小时或者当edits的文件大小达到64M(默认)时,对二者进行一个合并。在snn读取edits之后,做一个保存后,将真正的edits进行清空。然后将读取的fsimage和edits进行合并,返回生成的新的fsimage文件。
snn的合并执行时间和文件大小的控制在core-site.xml中。
文件访问权限
设置文件访问权限需要修改hdfs-site.xml文件的dfs.permissions
参数值为true。
修改前后需要重启dfs
当当前用户没有权限访问当前用户时,可以使用伪装用户的方法访问
FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration, "root");
小文件的合并
hfds的特点是适合大文件的存储,不适合小文件的存储。
所以当需求是存储大量小文件时,可以对多个小文件进行合并后存储,这样可以节省block空间。
- 通过命令行合并
hdfs dfs -getmerge 目标目录 合并后的文件
- 在代码中使用
/**
其思想是将本地文件系统转成hdfs文件系统进行修改
将本地文件进行合并
*/
public void mergeFile() {
FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration, "root");
FSDataOutputStream fos = fs.create(new Path("/big_txt.txt"));
LocalFileSystem lfs = fs.getLocal(new Configuration());
FileStatus[] fss =lfs.listStatus(new Path("D:\\input"));
for(FileStatus status : fss) {
FSDataInputStream fis = lfs.open(status.getPath());
IOUtils.copy(fis, fos);
IOUtils.coloseQuietly(fis);
}
IOUtils.close(lfs);
IOUtils.closeQuietly(fos);
fs.close();
}
高可用机制HA
ZKFC
ZKFC是zookeeper提供的一个hdfs文件系统控制进程,主要用来监控namenode的情况,控制namenode的状态切换。
其包含三个组件
- ZKFailoverController【故障转移控制器】
负责进行namenode主备之间的切换。检测nn健康状态,通知zk进行选举,完成主备切换 - HealthMonitor
主节点健康状态监控。周期性调用HAServiceProtocolRPC接口,来监控nn的健康情况并向ZKFailoverController反馈 - ActiveStandbyElector
配合zk进行主nn选举。接受选举请求,zk选举完成后,回调ZKFailoverController的主备切换完成nn的状态转换
JournalNode
JournalNode主要负责主备节点之间的日志同步,保证在active出现问题时,standby的数据与active保持一致,保证主备切换时没有数据丢失。
工作原理
脑裂问题
脑裂问题是同时存在两个处于active的nn主节点,这时会造成数据块的丢失,或者对dn下发错误的复制或者删除指令问题。
解决办法:
hdfs提供了三个级别的隔离
- 共享存储隔离:同一时间只有一个nnjn写入edits日志数据
- 客户端隔离:同一时间只允许一个nn响应客户端请求
- dn隔离:同一时间只允许一个nn向dn下发指令。
联邦机制
federation机制
hdfs集群的大小受nn内存的限制。
所以,当nn的内存不足以支撑整个集群的时候,可以采用联邦机制。
联邦机制是对nn的横向扩展。同时存在多个nn,同时处于active状态。他们之间相互分离。共同管理datanode。所以通常对每一个nn不同的命名空间。
同一dn上的不同块文件可能属于不同的nn。标志该文件块是由那个nn来进行管理。
federation机制只能通过横向扩展解决nn内存不足的问题。而不是由多个nn来做热备。
所以真正的场景中需要federation+HA同时保证热备与横向扩展,即每个active状态的nn都有standby的nn作为热备。
API
获取文件系统
主要两种方式:configuration和URI
两种获取类型:直接get和newInstanace
- 创建configuration对象
Configuration configuration = new Configuration();
- 设置文件系统类型以及nn
configuration.set("fs.defaultFS", "hdfs://namenode:8020");
- 指定文件系统
FileSytem fs = FileSystem.get(configuration)
或者
FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
遍历
//1. 获取fs实例
FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
/**
2.调用listFiles方法
传入参数 一个路径,一个是否递归所有下属文件夹
返回结果是一个迭代器
*/
RemoteIterator<LocatedFileStatus> iter = fs.listFiles(new Path("/"), true);
//3.遍历迭代器
while(iter.hasnext()) {
LocatedFileStatus lfs = iter.next();
//获取文件的绝对路径
String path =lfs.getPath();
//获取文件名
String name = lfs.getPath().getName();
//获取文件的block信息,可以通过length获取block数。
BlockLocation[] = lfs.getBlockLocations();
}
创建文件夹
//1. 获取fs实例
FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
//2.创建文件夹
fs.mkdir(new Path("/a/b/c"));
//3.创建文件(会自动创建不存在目录)
fs.create(new Path("a/b/c.txt"));
//4.关闭
fs.close();
下载文件
//1. 获取fs实例
FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
//2.获取hdfs输入流
FSDataInputStream fsi = fs.open(new Paht("a.txt"));
//3.获取本地文件输出流
FileOutputStream fos = new FileOutputStream("D://a.txt");
//4.文件拷贝
IOUtils.copy(fsi,fos);
//5.关闭流
IOUtils.closeQuietly(fsi);
IOUtils.closeQuietly(fos);
IOUtils.close();
//关闭
fs.close();
----------------
//简化方法,参数的顺序是hdfs路径-->本地路径
fs.copyToLoaclFile(new Path("a.txt"), new Path("D://a.txt"));
文件上传
//1. 获取fs实例
FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
//2.上传,参数的顺序是本地路径 --> hdfs路径
fs.copyFromLocalFile(new Path("D://a.txt"), new Path("/"));