前言
本文是基于石杉码农公众号关于HDFS的4篇文章做得总结梳理由于微信公众好跳转链接会过期,就不放上来了,有兴趣的可以直接前往公众号查看《2018年原创汇总》文章查找。
HDFS的架构原理
角色
- NameNode-文件结构树保存节点
Active NameNode-主节点:对外提供服务接收请求。
Standby NameNode-备节点:一是接收和同步主节点的edits log以及执行定期checkpoint,二是在Active NameNode的时候进行主备切换。
- JournalNode-日志节点
存放NameNode元数据写入的editLogs,供Standby NameNode拉取,主要意义是保持Active NameNode和Standby NameNode的同步,但必须高可用,所以是集群部署。
- DataNode-数据节点
存放真实数据的节点
- Block
一份数据会分为多个block存放在各个DataNode上,一个block会建立副本,放入其他的DataNode,保证一个block有三个副本在不同的DataNode上.
上传一个文件的流程
- 客户端上传文件,请求NameNode
- Active NameNode-主节点在内存中修改文件目录树,同时写入本地的editLog以及向JournalNode集群发送editLog,然后返回给客户端,客户端接收响应后才把文件上传到dataNode集群。
- Standby NameNode-备节点从JournalNode集群拉取editLog,修改自己的文件目录树
- Standby NameNode 定时在磁盘生成fsimage镜像文件,并合并自己的editLog文件(这个称为checkpoint检查点操作),然后上传到Active NameNode,Active NameNode收到后清空老的editLog文件以及fsimage文件。
- 若Active NameNode故障,Standby NameNode将从JournalNode读取全部的editLog,然后切换为Active NameNode(可以保证故障转移时,目录树是完全和原来的ActiveNode同步的),若原来的Active NameNode重启了,将根据JournalNode同步editLog到最新,恢复时直接根据fsimage文件以及本地editLog文件重放命令,进行数据恢复
- 为什么需要这套机制:为了加快NameNode重启后恢复数据的效率(镜像恢复速度快,不需要重放所有editLog)。
几个重要的优化
获取transactionId以及本地磁盘写+网络磁盘写JournalNode的优化
为什么需要引入transactionId
因为在Active NameNode向JournalNode发送editLog的时候,必须保证顺序。所以需要获取transacationId来维护这个顺序,这也意味着transacationId的获取不能并发。
原理与流程解析
主要是运用了double-buffer双缓冲机制,什么是双缓冲勒。要发送的editLog数据是先放在缓冲区里面,然后再由某个线程统一发送到JournalNode,提高网络写的效率。但这里有个问题得解决,如何让在网络写的过程中不影响数据写入缓冲区勒?只能引入双缓冲机制,即将缓冲区域分为两份,一份用于存放即时写入的数据,另一份用于存放将要被网络写的数据,当需要网络写的时候将两个区域切换,这样两个操作才能互相不影响。
上图第一次获取锁是为了生成transacationId,第二次获取锁的操作就是优化网络写,保证每个请求nameNode的线程不做无效的写入请求。这里保留一个疑问?第二次获取锁应该是非公平锁把,理论上最后一个transacationId的线程获取到锁效率会更高一些,HDFS没有对这里做优化吗?文章里没有提到这里更细的解释,这里暂时存疑。
大文件上传优化
HDFS上传文件的流程
- 比如有人上传一个1TB的大文件到网盘,或者是上传个1TB的大日志文件。
- HDFS客户端把一个一个的block上传到对应的第一个DataNode
- 第一个DataNode会把这个block复制一份,做一个副本发送给第二个DataNode。
- 第二个DataNode发送一个block副本到第三个DataNode。
为什么需要优化
如果使用传统的IO传输,将会产生频繁的网络传输,效率很低。
优化机制
Chunk缓冲机制
加快磁盘写入内存效率,默认是512Byte。
Packet数据包机制
进一步缓冲要发送网络的数据,默认64M。
内存队列异步发送机制
- 当一个Packet被塞满了chunk之后,就会将这个Packet放入一个内存队列来进行排队。
- 然后有一个DataStreamer线程会不断的获取队列中的Packet数据包,通过网络传输直接写一个Packet数据包给DataNode。
注:这里是异步的,意思是往OutPutStream写入数据的同时,也在DataStreamer线程也在发送数据包。
文件契约机制
为什么要有文件契约
为了保证同一时间只能有一个客户端获取NameNode上面一个文件的契约,然后才可以写入数据。在写文件的过程期间,客户端需要开启一个线程,不停的发送请求给NameNode进行文件续约。
优化机制
NameNode如果每次都遍历NameNode去淘汰过期的契约,效率是非常低下的,这里的优化就是对每个契约按最近一次续约时间进行排,序每次都把续约时间最老的契约排在最前头。当每次检查是否过期时,从头开始遍历,只要遍历到没有过期的就不遍历了,因为后面的必定没有过期。类似的,运用这种方式优化的案例还有eureka维护服务实例心跳续约使用的机制。