HDFS的副本存放策略

 
HDFS作为Hadoop中的一个分布式文件系统,而且是专门为它的MapReduce设计,所以HDFS除了必须满足自己作为分布式文件系统的高可靠性外,还必须为MapReduce提供高效的读写性能,那么HDFS是如何做到这些的呢?首先,HDFS将每一个文件的数据进行分块存储,同时每一个数据块又保存有多个副本,这些数据块副本分布在不同的机器节点上,这种数据分块存储+副本的策略是HDFS保证可靠性和性能的关键,这是因为:一.文件分块存储之后按照数据块来读,提高了文件随机读的效率和并发读的效率;二.保存数据块若干副本到不同的机器节点实现可靠性的同时也提高了同一数据块的并发读效率;三.数据分块是非常切合MapReduce中任务切分的思想。在这里,副本的存放策略又是HDFS实现高可靠性和搞性能的关键。

HDFS采用一种称为机架感知的策略来改进数据的可靠性、可用性和网络带宽的利用率。通过一个机架感知的过程,NameNode可以确定每一个DataNode所属的机架id(这也是NameNode采用NetworkTopology数据结构来存储数据节点的原因,也是我在前面详细介绍NetworkTopology类的原因)。一个简单但没有优化的策略就是将副本存放在不同的机架上,这样可以防止当整个机架失效时数据的丢失,并且允许读数据的时候充分利用多个机架的带宽。这种策略设置可以将副本均匀分布在集群中,有利于当组件失效的情况下的均匀负载,但是,因为这种策略的一个写操作需要传输到多个机架,这增加了写的代价。

在大多数情况下,副本系数是3,HDFS的存放策略是将一个副本存放在本地机架节点上,一个副本存放在同一个机架的另一个节点上,最后一个副本放在不同机架的节点上。这种策略减少了机架间的数据传输,提高了写操作的效率。机架的错误远远比节点的错误少,所以这种策略不会影响到数据的可靠性和可用性。与此同时,因为数据块只存放在两个不同的机架上,所以此策略减少了读取数据时需要的网络传输总带宽。在这种策略下,副本并不是均匀的分布在不同的机架上:三分之一的副本在一个节点上,三分之二的副本在一个机架上,其它副本均匀分布在剩下的机架中,这种策略在不损害数据可靠性和读取性能的情况下改进了写的性能。下面就来看看HDFS是如何来具体实现这一策略的。

NameNode是通过类来为每一分数据块选择副本的存放位置的,这个ReplicationTargetChooser的一般处理过程如下:

       
     上面的流程图详细的描述了Hadoop-0.2.0版本中副本的存放位置的选择策略,当然,这当中还有一些细节问题,如:如何选择一个本地数据节点,如何选择一个本地机架数据节点等,所以下面我还将继续展开讨论。

1.选择一个本地节点
        这里所说的本地节点是相对于客户端来说的,也就是说某一个用户正在用一个客户端来向HDFS中写数据,如果该客户端上有数据节点,那么就应该最优先考虑把正在写入的数据的一个副本保存在这个客户端的数据节点上,它即被看做是本地节点,但是如果这个客户端上的数据节点空间不足或者是当前负载过重,则应该从该数据节点所在的机架中选择一个合适的数据节点作为此时这个数据块的本地节点。另外,如果客户端上没有一个数据节点的话,则从整个集群中随机选择一个合适的数据节点作为此时这个数据块的本地节点。那么,如何判定一个数据节点合不合适呢,它是通过isGoodTarget方法来确定的:

  1. private boolean isGoodTarget(DatanodeDescriptor node, long blockSize, int maxTargetPerLoc, boolean considerLoad, List<DatanodeDescriptor> results) {
  2. Log logr = FSNamesystem.LOG;
  3. // 节点不可用了
  4. if (node.isDecommissionInProgress() || node.isDecommissioned()) {
  5. logr.debug("Node "+NodeBase.getPath(node)+ " is not chosen because the node is (being) decommissioned");
  6. return false;
  7. }
  8. long remaining = node.getRemaining() - (node.getBlocksScheduled() * blockSize);
  9. // 节点剩余的容量够不够
  10. if (blockSize* FSConstants.MIN_BLOCKS_FOR_WRITE>remaining) {
  11. logr.debug("Node "+NodeBase.getPath(node)+ " is not chosen because the node does not have enough space");
  12. return false;
  13. }
  14. // 节点当前的负载情况
  15. if (considerLoad) {
  16. double avgLoad = 0;
  17. int size = clusterMap.getNumOfLeaves();
  18. if (size != 0) {
  19. avgLoad = (double)fs.getTotalLoad()/size;
  20. }
  21. if (node.getXceiverCount() > (2.0 * avgLoad)) {
  22. logr.debug("Node "+NodeBase.getPath(node)+ " is not chosen because the node is too busy");
  23. return false;
  24. }
  25. }
  26. // 该节点坐在的机架被选择存放当前数据块副本的数据节点过多
  27. String rackname = node.getNetworkLocation();
  28. int counter=1;
  29. for(Iterator<DatanodeDescriptor> iter = results.iterator(); iter.hasNext();) {
  30. Node result = iter.next();
  31. if (rackname.equals(result.getNetworkLocation())) {
  32. counter++;
  33. }
  34. }
  35. if (counter>maxTargetPerLoc) {
  36. logr.debug("Node "+NodeBase.getPath(node)+ " is not chosen because the rack has too many chosen nodes");
  37. return false;
  38. }
  39. return true;
  40. }

2.选择一个本地机架节点
   实际上,选择本地节假节点和远程机架节点都需要以一个节点为参考,这样才是有意义,所以在上面的流程图中,我用红色字体标出了参考点。那么,ReplicationTargetChooser是如何根据一个节点选择它的一个本地机架节点呢?
这个过程很简单,如果参考点为空,则从整个集群中随机选择一个合适的数据节点作为此时的本地机架节点;否则就从参考节点所在的机架中随机选择一个合适的数据节点作为此时的本地机架节点,若这个集群中没有合适的数据节点的话,则从已选择的数据节点中找出一个作为新的参考点,如果找到了一个新的参考点,则从这个新的参考点在的机架中随机选择一个合适的数据节点作为此时的本地机架节点;否则从整个集群中随机选择一个合适的数据节点作为此时的本地机架节点。如果新的参考点所在的机架中仍然没有合适的数据节点,则只能从整个集群中随机选择一个合适的数据节点作为此时的本地机架节点了。

  1. <font xmlns="http://www.w3.org/1999/xhtml">private DatanodeDescriptor chooseLocalRack(DatanodeDescriptor localMachine, List<Node> excludedNodes, long blocksize, int maxNodesPerRack, List<DatanodeDescriptor> results)throws NotEnoughReplicasException {
  2. // 如果参考点为空,则从整个集群中随机选择一个合适的数据节点作为此时的本地机架节点
  3. if (localMachine == null) {
  4. return chooseRandom(NodeBase.ROOT, excludedNodes, blocksize, maxNodesPerRack, results);
  5. }
  6. //从参考节点所在的机架中随机选择一个合适的数据节点作为此时的本地机架节点
  7. try {
  8. return chooseRandom(localMachine.getNetworkLocation(), excludedNodes, blocksize, maxNodesPerRack, results);
  9. } catch (NotEnoughReplicasException e1) {
  10. //若这个集群中没有合适的数据节点的话,则从已选择的数据节点中找出一个作为新的参考点
  11. DatanodeDescriptor newLocal=null;
  12. for(Iterator<DatanodeDescriptor> iter=results.iterator(); iter.hasNext();) {
  13. DatanodeDescriptor nextNode = iter.next();
  14. if (nextNode != localMachine) {
  15. newLocal = nextNode;
  16. break;
  17. }
  18. }
  19. if (newLocal != null) {//找到了一个新的参考点
  20. try {
  21. //从这个新的参考点在的机架中随机选择一个合适的数据节点作为此时的本地机架节点
  22. return chooseRandom(newLocal.getNetworkLocation(), excludedNodes, blocksize, maxNodesPerRack, results);
  23. } catch(NotEnoughReplicasException e2) {
  24. //新的参考点所在的机架中仍然没有合适的数据节点,从整个集群中随机选择一个合适的数据节点作为此时的本地机架节点
  25. return chooseRandom(NodeBase.ROOT, excludedNodes, blocksize, maxNodesPerRack, results);
  26. }
  27. } else {
  28. //从整个集群中随机选择一个合适的数据节点作为此时的本地机架节点
  29. return chooseRandom(NodeBase.ROOT, excludedNodes, blocksize, maxNodesPerRack, results);
  30. }
  31. }
  32. }</font>

3.选择一个远程机架节点
   选择一个远程机架节点就是随机的选择一个合适的不在参考点坐在的机架中的数据节点,如果没有找到这个合适的数据节点的话,就只能从参考点所在的机架中选择一个合适的数据节点作为此时的远程机架节点了。

4.随机选择若干数据节点
   这里的随机随机选择若干个数据节点实际上指的是从某一个范围内随机的选择若干个节点,它的实现需要利用前面提到过的NetworkTopology数据结构。随机选择所使用的范围本质上指的是一个路径,这个路径表示的是NetworkTopology所表示的树状网络拓扑图中的一个非叶子节点,随机选择针对的就是这个节点的所有叶子子节点,因为所有的数据节点都被表示成了这个树状网络拓扑图中的叶子节点。

5.优化数据传输的路径
   以前说过,HDFS对于Block的副本copy采用的是流水线作业的方式:client把数据Block只传给一个DataNode,这个DataNode收到Block之后,传给下一个DataNode,依次类推,...,最后一个DataNode就不需要下传数据Block了。所以,在为一个数据块确定了所有的副本存放的位置之后,就需要确定这种数据节点之间流水复制的顺序,这种顺序应该使得数据传输时花费的网络延时最小。ReplicationTargetChooser用了非常简单的方法来考量的,大家一看便知:

  1. private DatanodeDescriptor[] getPipeline( DatanodeDescriptor writer, DatanodeDescriptor[] nodes) {
  2. if (nodes.length==0) return nodes;
  3. synchronized(clusterMap) {
  4. int index=0;
  5. if (writer == null || !clusterMap.contains(writer)) {
  6. writer = nodes[0];
  7. }
  8. for(;index<nodes.length; index++) {
  9. DatanodeDescriptor shortestNode = nodes[index];
  10. int shortestDistance = clusterMap.getDistance(writer, shortestNode);
  11. int shortestIndex = index;
  12. for(int i=index+1; i<nodes.length; i++) {
  13. DatanodeDescriptor currentNode = nodes[i];
  14. int currentDistance = clusterMap.getDistance(writer, currentNode);
  15. if (shortestDistance>currentDistance) {
  16. shortestDistance = currentDistance;
  17. shortestNode = currentNode;
  18. shortestIndex = i;
  19. }
  20. }
  21. //switch position index & shortestIndex
  22. if (index != shortestIndex) {
  23. nodes[shortestIndex] = nodes[index];
  24. nodes[index] = shortestNode;
  25. }
  26. writer = shortestNode;
  27. }
  28. }
  29. return nodes;
  30. }

可惜的是,HDFS目前并没有把副本存放策略的实现开放给用户,也就是用户无法根据自己的实际需求来指定文件的数据块存放的具体位置。例如:我们可以将有关系的两个文件放到相同的数据节点上,这样在进行map-reduce的时候,其工作效率会大大的提高。但是,又考虑到副本存放策略是与集群负载均衡休戚相关的,所以要是真的把负载存放策略交给用户来实现的话,对用户来说是相当负载的,所以我只能说Hadoop目前还不算成熟,尚需大踏步发展。

HDFS的副本存放策略(全)的更多相关文章

  1. 大数据:Hadoop(HDFS 的设计思路、设计目标、架构、副本机制、副本存放策略)

    一.HDFS 的设计思路 1)思路 切分数据,并进行多副本存储: 2)如果文件只以多副本进行存储,而不进行切分,会有什么问题 缺点 不管文件多大,都存储在一个节点上,在进行数据处理的时候很难进行并行处 ...

  2. hadoop 集群中数据块的副本存放策略

    HDFS采用一种称为机架感知(rack-aware)的策略来改进数据的可靠性.可用性和网络带宽的利用率.目前实现的副本存放策略只是在这个方向上的第一步.实现这个策略的短期目标是验证它在生产环境下的有效 ...

  3. HDFS副本存放策略

    在client向DataNode写入block之前,会与NameNode有一次通信,由NameNode来选择指定数目的DataNode来存放副本.具体的副本选择策略在BlockPlacementPol ...

  4. hadoop2.0的数据副本存放策略

    在hadoop2.0中,datanode数据副本存放磁盘选择策略有两种方式: 第一种是沿用hadoop1.0的磁盘目录轮询方式,实现类:RoundRobinVolumeChoosingPolicy.j ...

  5. HDFS副本存放读取

    HDFS作为Hadoop中 的一个分布式文件系统,而且是专门为它的MapReduce设计,所以HDFS除了必须满足自己作为分布式文件系统的高可靠性外,还必须为 MapReduce提供高效的读写性能,那 ...

  6. HDFS副本放置策略和机架感知

    副本放置策略 的副本放置策略的基本思想是: 第一block在复制和client哪里node于(假设client它不是群集的范围内,则这第一个node是随机选取的.当然系统会尝试不选择哪些太满或者太忙的 ...

  7. 014_HDFS存储架构、架构可靠性分析、副本放置策略、各组件之间的关系

    1.HDFS存储架构

  8. Hadoop_HDFS文件读写代码流程解析和副本存放机制

    Hadoop学习笔记总结 01.RPC(远程过程调用) 1. RPC概念 远程过程指的不是同一个进程的调用.它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. 不能直接拿到远 ...

  9. Hadoop 副本放置策略的源码阅读和设置

    本文通过MetaWeblog自动发布,原文及更新链接:https://extendswind.top/posts/technical/hadoop_block_placement_policy 大多数 ...

随机推荐

  1. Zip包解压工具类

    最近在做项目的自动检测离线升级,使用到了解压zip包的操作,本着拿来主义精神,搞了个工具类(同事那边拿的),用着还不错. package com.winning.polaris.admin.utils ...

  2. MongoDB、MySQL

    我的电脑的系统Path:   D:\sqlite;D:\Program Files\MongoDB\Server\3.4\bin;%MYSQL_HOME%\bin;D:\Program Files\B ...

  3. 22.LinkedList

    LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现.基于链表实现的方式使得Linked ...

  4. (转载)RHEL7(RedHat 7)本地源的配置

    配置yum源 1.首先连接RedHat7的DVD再把挂载DVD光盘到/mnt   因为配置时候路径名里面不能有空格,否则不能识别  [root@ mnt]# mount /dev/cdrom /mnt ...

  5. YUM仓库安装LAMP与LNMP

    LAMP动态网站部署架构是由一套 Linux+Apache+MySQL+PHP 组成的动态网站系统解决方案. LNMP动态网站部署架构是由一套 Linux+Nginx+MySQL+PHP 组成的动态网 ...

  6. 深入浅出TCP/IP协议

    目录 什么是网络协议? 谁来制定这个网络协议? TCI/IP协议 什么是socket? http协议属于应用层还是传输层? soap可以使用HTTP协议进行传输吗? 各层协议举例 什么是网络协议? 话 ...

  7. HashMap的源码分析

    hashMap的底层实现是 数组+链表 的数据结构,数组是一个Entry<K,V>[] 的键值对对象数组,在数组的每个索引上存储的是包含Entry的节点对象,每个Entry对象是一个单链表 ...

  8. 谷歌浏览器怎么FQ(一)(想使用谷歌浏览器应用商城的小伙伴这边看)

    谷歌浏览器的应用商城里本身有很多不错的扩展程序和插件,比如Wappalyzer(能够识别某个网站用的什么框架和库)广告终结者(能屏蔽大部分浮动,弹窗,甚至视频广告)等 但是谷歌因为某些原因需要FQ以后 ...

  9. Java学习笔记50(DBCP连接池)

    实际开发中,连接数据库是十分消耗资源的操作,但是,我们又需要频繁地连接数据库 这时候,为了提高效率,这里就会采用连接池技术: 连接池地通俗理解: 一个池里面放入很多的连接,需要哪一个取出来用即可,用完 ...

  10. SpringBoot跨域小结

    前言:公司的SpringBoot项目出于某种原因,经常样处理一些跨域请求. 一.以前通过查阅相关资料自己写的一个处理跨域的类,如下. 1.1首先定义一个filter(拦截所有请求,包括跨域请求) pu ...