HDFS读取副本的选择策略

2019-03-28 14:01|来源: 网络

HDFS对文件的存储是分块来存储的,即HDFS对于客户端写入的数据先按照固定大小对这些数据进行分块,然后把每一个数据块的多个副本存储在不同的DataNode节点上,同时不同的数据块也可能存储在不同的DataNode节点上。那么,当客户端要从HDFS上读取某个文件时,它又是如何处理的呢?很明显,客户端也是按照分块来读取文件的数据的,关于客户端如何分块读取文件的详细原理与过程我已经在前面的博文中详细的阐述了,在这里就不多赘述了。但是,一个数据块有多个副本,客户端到底优先读取那个DataNode节点上的该数据块的副本呢?这将是本要所要讨论的重点了,即HDFS读取副本的选择策略,而这个工作具体是由NameNode来完成的。

当客户端读到一个文件的某个数据块时,它就需要向NameNode节点询问这个数据块存储在那些DataNode节点上,这个过程如下图:

当然,客户端至少需要向NameNode传输三个参数:文件路径、读取文件的起始位置、读取文件的长度,而NameNode会向该客户端返回它所要读取文件内容所在的位置——LocatedBlock对象,该对象包含对应的数据块所有副本所在的DataNode节点的位置信息,客户端接着就会依次从这些DataNode节点上读取该数据块,直到成功读取为止。这里就设计到了一个优化问题了:客户端应该总是选择从距离它最近的可用DataNode节点上读取需要的数据块,所以此时的关键就是如何来计算客户端与DataNode节点之间的距离。这个计算距离的问题本质上涉及到我前面介绍过的一种数据结构——NetworkTopology,这种数据结构被NameNode节点用来形象的表示HDFS集群中所有DataNode节点的物理位置,自然这个优化工作就交由NameNode来处理了。

NameNode对文件的读优化的实现很简单,基本原理就是按照客户端与DataNode节点之间的距离进行排序,距客户端越近的DataNode节点越被放在LocatedBlock的前面,该算法的基本思路如下:

1.如果该Block的一个副本存在于客户端,则客户端优先从本地读取该数据块;

2.如果该Block的一个副本与客户端在同一个机架上,且没有一个副本存放在客户端,则客户端优先读取这个同机架上的副本;否则客户端优先读取同机器的副本,失败的情况下然后再优先考虑这个同机架上的副本;

3.如果该Block既没有一个副本存在客户端,又没有一个副本与客户端在同一个机架上,则随机选择一个DataNode节点作为优先节点。

其详细实现如下:

  1. /**   
  2.    * @param reader 客户端 
  3.    * @param nodes 某个Block所在的DataNode节点 
  4.  */  
  5. public void pseudoSortByDistance( Node reader, Node[] nodes ) {  
  6.     int tempIndex = 0;  
  7.     if (reader != null ) {  
  8.       int localRackNode = -1;  
  9.       //scan the array to find the local node & local rack node   
  10.       for(int i=0; i<nodes.length; i++) {  
  11.         if(tempIndex == 0 && reader == nodes[i]) { //local node   
  12.           //swap the local node and the node at position 0   
  13.           if( i != 0 ) {  
  14.             swap(nodes, tempIndex, i);  
  15.           }  
  16.           tempIndex=1;  
  17.           if(localRackNode != -1 ) {  
  18.             if(localRackNode == 0) {  
  19.               localRackNode = i;  
  20.             }  
  21.             break;  
  22.           }  
  23.         } else if(localRackNode == -1 && isOnSameRack(reader, nodes[i])) {  
  24.           //local rack   
  25.           localRackNode = i;  
  26.           if(tempIndex != 0 ) break;  
  27.         }  
  28.       }  
  29.   
  30.       // swap the local rack node and the node at position tempIndex   
  31.       if(localRackNode != -1 && localRackNode != tempIndex ) {  
  32.         swap(nodes, tempIndex, localRackNode);  
  33.         tempIndex++;  
  34.       }  
  35.     }  
  36.       
  37.     // put a random node at position 0 if it is not a local/local-rack node   
  38.     if(tempIndex == 0 && nodes.length != 0) {  
  39.       swap(nodes, 0, r.nextInt(nodes.length));  
  40.     }  
  41.   }  

从上面的具体实现可以看,这种优化策略存在一个很大的缺陷:完全没有考虑到DataNode上的负载情况,从而致使HDFS的读性能急速下降。这一点也正好反映了HDFS好友很大的优化空间,但遗憾的是,HDFS对这种读Block时的副本选择策略并没有开放给开发者,以至于大大的增加了优化的难度。

相关问答

更多
  • 看看这篇文章: http : //blog.cloudera.com/blog/2013/08/how-improved-short-circuit-local-reads-bring-better-performance-and-security-to-hadoop/ 文章摘要: 原始实施的一个主要缺点是它与安全隐患相关。 它必须让客户直接读取数据文件。 我想这对于kerberos启用hdfs是不利的。 相反,新的实现传递一个文件描述符,这应该是更安全,更快速的。 我想这个旧方法有一些缺点。 我不明白新方 ...
  • 您在构建时是否在./configure中启用了HDFS支持? 如果禁用HDFS,那就是你会得到的错误。 我认为你做了正确的改变让它发挥作用。 随意发送拉取请求以在macOS上查找.dylib。 Did you enable HDFS support in ./configure when building? That's the error you would get if HDFS is disabled. I think you made the correct change to make it wo ...
  • 这完全取决于你想用它做什么; 如果您要重复使用文件内容,您也可以将其复制到本地以将重复查找保存到HDFS,但如果您只需要阅读一次,那么直接来自HDFS没有任何危害,因为复制到本地将涉及阅读无论如何它从那里。 It depends entirely what you want to do with it; if you will be reusing the file contents, you might as well just copy it to local to save the repeat lo ...
  • 是的,它会将导入数据的副本存储在HDFS中(如StoreFiles / HFiles),因为HBase只能使用自己的文件集进行操作。 也许你会发现这个很好的概述很有趣。 您可以直接使用存储在HDFS中的数据进行操作,而无需使用EXTERNAL HIVE表将其导入HBase: CREATE EXTERNAL TABLE page_view(viewTime INT, userid BIGINT, page_url STRING, referrer_url STRING, ip STRING ...
  • 这里给出的Indrajit的答案解决了我的问题。 问题出在spark-csv jar上。 The answer by Indrajit given here solved my problem. The problem was with the spark-csv jar.
  • 当然! 以下是如何从HDFS读取文件的示例: import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; // ...stuff... FileSystem fs = FileSystem.get(URI.create("hdfs://prodserver"), new Configuration()); String hdfspath = "/apps/storm/conf/config.json"; Path p ...
  • 注册Hadoop的Url处理程序。 标准的Url处理程序将不知道如何处理hdfs://方案。 尝试这个: public static void main(String[] args) throws MalformedURLException, IOException { URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory()); String url = "hdfs://" + N ...
  • 从Tom White的“Hadoop:权威指南” 过度复制块这些块超出了它们所属文件的目标复制。 通常,过度复制不是问题,HDFS会自动删除多余的副本。 未复制的块这些块不符合它们所属文件的目标复制。 HDFS将自动创建未复制块的新副本,直到它们满足目标复制。 您可以使用hdfs dfsadmin -metasave获取有关正在复制(或等待复制)的块的信息。 错误复制的块这些块不满足块副本放置策略(请参阅副本放置)。 例如,对于多框群集中的复制级别为三的情况,如果一个块的所有三个副本位于同一机架上,则该块会 ...
  • 可以使用前缀file:///从Spark引用本地文件系统 Eg: sparkContext.textFile("file:///<>") 此命令从本地文件系统读取文件。 注意:如果在多节点群集中执行,则此文件应在所有节点上可用。 Local filesystem can be referred from Spark with the prefix file:/// Eg: sparkContext.textFile("file:///<
  • 您不清楚是否在与HDFS数据节点相同的计算机上安装了Presto工作程序。 如果还没有, 安装说明将帮助您完成此操作。 在所有数据节点上安装Presto工作器后,Presto应在从本地DFS节点访问数据时自动执行本地读取。 Presto更喜欢在与DFS节点相同的机器上安排工作,但如果该机器过载,它将在另一台机器上安排工作,因此您通常会获得一些远程读取。 大多数读取应该是本地的,您可以使用协调器上的com.facebook.presto.execution:name = NodeScheduler mbean ...