hdfs

namenode的作用

主要负责命名空间和文件数据块的地址映射。

整个集群的大小受限于namenode的内存大小。

  1. 存储元数据信息
    其包含一个文件的添加时间,大小,权限,块列表(大于【默认128M】的文件会被切割成多个块),以及每个块的备份信息。
    该元数据信息保存在内存中。fsimage和edits提供数据持久化
  2. namenode文件操作
    处理文件存放请求,确定文件块以及副本的具体存放位置。
    但是真正的数据文件不经过namenode,而是客户端根据namenode返回的datanode信息,与datanode进行通信
  3. namenode副本
    根据当前集群的情况来确定副本的存储位置。
  4. namdenode心跳机制
    周期性的接受DN的心跳和块状态报告。
    接收到心跳意味着结点正常,长时间没有接收到意味着结点宕机,namenode会将宕机节点上的数据块进行重新复制到其他结点。
    块的状态报告包含了DN上的所有数据快列表,一小时发送一次。

datanode的作用

提供真实的文件存储服务

  1. 以数据块的形式存储文件
  2. 响应客户端的读写请求
  3. 向namenode发送心跳包
  4. 向namenode发送数据块信息
  5. 向namenode发送数据块缓存信息
    数据块缓存信息是,如果结点的上的某个数据块的访问频率比较高,那么就会将该数据块放到内存中,以提高访问效率。

副本机制和机架感知

副本机制,一份数据默认存储3份。可以在hdfs-sizte.xml中进行配置。

机架感知策略,和所谓的两地三中心策略相似。

namenode在存储数据时,会首先确定数据的存放结点。然后在本机架上的另外一台机器上存储第一个副本,最后在另外机架上选择一台机器存储第二个副本。namenode的数据选择首先是数据本体机器,其次是相同机架上的机器,最后是另外机架上的副本。

命令

hdfs dfs 命令开头,可以使用大部分linux文件系统的命令来操作hdfs

上传

  1. hdfs dfs -put wenjia /目录

删除命令

  1. hdfs dfs -rm /a.txt
  2. 删除后的文件会移动到回收站中。
  3. 可以使用 -skipTrash

数据追加

  1. hdfs dfs -appendTOFile file1 file2 合并后文件名
  2. 可用于小文件的合并

设置文件限额

安全模式

集群在启动后会进行安全检查,对所有副本率小于0.999的文件块进行复制。

默认的文件副本率:0.999

安全模式下,文件系统只接受读数据请求,不接受删除,修改等变更请求。

文件写入过程

  1. client发送上传请求
  2. NN进行权限检查,上传权限,返回可以上传回应
  3. client进行切分文件,分为多个文件block
  4. client上传第一个block
  5. NN根据系欸但那的block信息和机架感知策略选取三个datanode,返回给client
  6. client和第一个DN之间建立上传管道(pipeline),第二个和第三DN结点互相建立管道。形成client-1-2-3的管道。
  7. client向DN传递数据,传递的数据不是一整个数据块,而是一个个小的packet【64k】
  8. DN收到数据后,首先在本地进行缓存,然后将数据传递给DN2,DN2传递给DN3。使得数据在管道里从client流到DN3。

    其中Client发送数据与数据在DN之间的传递是并行的。也就是说Client在传递了第一个packet,被DN1接收了之后就会传递第二个,然后开启应答队列来确保每个packet都被接收到了,DN3完成数据接受返回给DN2,DN2传递给DN1,最后返回给client完成整个block的传输

  9. 一个block传递完成后,再次请求NN获取block的存储结点,然后重复发送步骤。

  10. 所有数据发送完成过后,DN会向NN报告block修改信息
  11. NN接受block修改信息,记录在edits日志中,周期性的合并到fsimage。

文件的读取流程

  1. client请求下载文件,RPC
  2. nn进行权限检查,文件block列表检查,选取出合适的结点进行返回

    如果一个文件过大,由多个文件块组成,那么在选取时会根据副本的结点优先选取较少的结点。举例说明:

    一个文件由三个block组成

    bl1:dn1,dn2,dn3

    bl2:dn2,dn3,dn4

    bl3:dn1,dn3,dn4

    那么返回的结点可能为dn1,dn4等几种情况,一般会少于三个结点

    节点数量少,但是block对应的地址不会少。

  3. client分别与返回的DN建立管道

  4. dn开始发送数据,数据同样为切割后的packet
  5. client收到数据进行ack回应,dn继续发送
  6. 直到所有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

当当前用户没有权限访问当前用户时,可以使用伪装用户的方法访问

  1. FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration, "root");

小文件的合并

hfds的特点是适合大文件的存储,不适合小文件的存储。

所以当需求是存储大量小文件时,可以对多个小文件进行合并后存储,这样可以节省block空间。

  • 通过命令行合并
  1. hdfs dfs -getmerge 目标目录 合并后的文件
  • 在代码中使用
  1. /**
  2. 其思想是将本地文件系统转成hdfs文件系统进行修改
  3. 将本地文件进行合并
  4. */
  5. public void mergeFile() {
  6. FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration, "root");
  7. FSDataOutputStream fos = fs.create(new Path("/big_txt.txt"));
  8. LocalFileSystem lfs = fs.getLocal(new Configuration());
  9. FileStatus[] fss =lfs.listStatus(new Path("D:\\input"));
  10. for(FileStatus status : fss) {
  11. FSDataInputStream fis = lfs.open(status.getPath());
  12. IOUtils.copy(fis, fos);
  13. IOUtils.coloseQuietly(fis);
  14. }
  15. IOUtils.close(lfs);
  16. IOUtils.closeQuietly(fos);
  17. fs.close();
  18. }

高可用机制HA

ZKFC

ZKFC是zookeeper提供的一个hdfs文件系统控制进程,主要用来监控namenode的情况,控制namenode的状态切换。

其包含三个组件

  1. ZKFailoverController【故障转移控制器】
    负责进行namenode主备之间的切换。检测nn健康状态,通知zk进行选举,完成主备切换
  2. HealthMonitor
    主节点健康状态监控。周期性调用HAServiceProtocolRPC接口,来监控nn的健康情况并向ZKFailoverController反馈
  3. ActiveStandbyElector
    配合zk进行主nn选举。接受选举请求,zk选举完成后,回调ZKFailoverController的主备切换完成nn的状态转换

JournalNode

JournalNode主要负责主备节点之间的日志同步,保证在active出现问题时,standby的数据与active保持一致,保证主备切换时没有数据丢失。

工作原理

脑裂问题

脑裂问题是同时存在两个处于active的nn主节点,这时会造成数据块的丢失,或者对dn下发错误的复制或者删除指令问题。

解决办法:

hdfs提供了三个级别的隔离

  1. 共享存储隔离:同一时间只有一个nnjn写入edits日志数据
  2. 客户端隔离:同一时间只允许一个nn响应客户端请求
  3. 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

  1. 创建configuration对象
    Configuration configuration = new Configuration();
  2. 设置文件系统类型以及nn
    configuration.set("fs.defaultFS", "hdfs://namenode:8020");
  3. 指定文件系统
    FileSytem fs = FileSystem.get(configuration)

或者

  1. FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);

遍历

  1. //1. 获取fs实例
  2. FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
  3. /**
  4. 2.调用listFiles方法
  5. 传入参数 一个路径,一个是否递归所有下属文件夹
  6. 返回结果是一个迭代器
  7. */
  8. RemoteIterator<LocatedFileStatus> iter = fs.listFiles(new Path("/"), true);
  9. //3.遍历迭代器
  10. while(iter.hasnext()) {
  11. LocatedFileStatus lfs = iter.next();
  12. //获取文件的绝对路径
  13. String path =lfs.getPath();
  14. //获取文件名
  15. String name = lfs.getPath().getName();
  16. //获取文件的block信息,可以通过length获取block数。
  17. BlockLocation[] = lfs.getBlockLocations();
  18. }

创建文件夹

  1. //1. 获取fs实例
  2. FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
  3. //2.创建文件夹
  4. fs.mkdir(new Path("/a/b/c"));
  5. //3.创建文件(会自动创建不存在目录)
  6. fs.create(new Path("a/b/c.txt"));
  7. //4.关闭
  8. fs.close();

下载文件

  1. //1. 获取fs实例
  2. FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
  3. //2.获取hdfs输入流
  4. FSDataInputStream fsi = fs.open(new Paht("a.txt"));
  5. //3.获取本地文件输出流
  6. FileOutputStream fos = new FileOutputStream("D://a.txt");
  7. //4.文件拷贝
  8. IOUtils.copy(fsi,fos);
  9. //5.关闭流
  10. IOUtils.closeQuietly(fsi);
  11. IOUtils.closeQuietly(fos);
  12. IOUtils.close();
  13. //关闭
  14. fs.close();
  15. ----------------
  16. //简化方法,参数的顺序是hdfs路径-->本地路径
  17. fs.copyToLoaclFile(new Path("a.txt"), new Path("D://a.txt"));

文件上传

  1. //1. 获取fs实例
  2. FileSytem fs = FileSystem.newInstance(new URI("http://namenode:8020"), new Configuration);
  3. //2.上传,参数的顺序是本地路径 --> hdfs路径
  4. fs.copyFromLocalFile(new Path("D://a.txt"), new Path("/"));