在HDFS上面最不明确的情形之一确实是数据的冗余。它完满是自动进行的,因为无法得知其中详细的信息,咱们需要做的确实是相信它。HBa完全相信HDFS存储数据的平安性和完整性,并将数据文件交给HDFS存储。正是因为HDFS的数据冗余方式关于HBa来讲是完全透明的,产生了一个问题:HBa的效率会受到多大的阻碍?说的简单一点,当HBa需要存取数据时,如何保证有一份冗余的数据块离自己最近?
当咱们对HBa做一次MapReduce的扫描操作时,那个问题尤其显现出来。所有的RegionServer都在从HDFS上面读取数据,理想的状况固然是每一个RegionServer要读取的数据都离自己很近。那个问题就牵涉到HBa的数据文件是如安在HDFS上面存储的。
让咱们第一抛开HBa,假设要处置的数据确实是HDFS上面的数据块,看看Hadoop是如何工作的。MapReduce老是有一个建议,那确实是在每一个TaskTracker上面Map/Reduce程序要处置的数据在本地就有一份冗余。如此程序只需要与本地数据交互,减少了网络流量并提高了效率。为了做到这一点,HDFS会把大文件分割成很多小文件来存储,咱们称之为数据块(Block)。每一个数据块的大小比操作系统数据块的大小要大得多,默许是64M,但通常咱们选择128M,或某个更大的值(这取决与你的文件大小,最好你的单个文件大小老是大于
一个数据块)。在MapReduce中,每一个数据块会被分派给一个Task,那个Task就负责处置那个数据块中的数据。因此数据块越大,产生的Task就越少,需要mapper的数量就越少。Hadoop自己明白每一个数据块存储的位置,如此在任务分派的时候就能够够直接在存储数据块的机械上启动Task,或选择一个最近机械启动Task。真是因为每一个数据块有多份冗余,使得Hadoop有更大的选择空间。只要找到一份冗余符合条件就好了,不是吗?如此Hadoop就能够够保证在MapReduce期间Task老是操作本地数据。
让咱们回到HBa,此刻你已经明白得了Hadoop是如何保证在MapReduce的进程中每一个Task都尽可能处置本地数据。若是你看过HBa的存储架构你就会明白HBa只是简单的将HFile和WAL log存储在HDFS上面。通过简单的挪用HDFS的API来创建文件:ate(Path path)。接下来你会关切两件情形的效率:1)随机的访问 2)通过MapReduce扫描全表。咱们固然希望当每一个RegionServer读取数据时存储数据的数据块就在本地。它能做到吗?
第一种情形,你有两个集群,一个集群装Hadoop,另一个集群装HBa,两个集群是分隔开的,只有网线来传输数据。好了,讨论到此为止,神也帮不了你。
第二种情形,你有一个大的集群,每台机械都混装了Hadoop和HBa,每一个RegionServer上面都有一个DataNode(这是咱们最希望看到的)。好,如此的话RegionServer就具有了从本地读取数据的前提。咱们还剩下一个问题,如何保证每一个RegionServer治理的Region所对应的HFile和WAL log就存在本地的DataNode上面?假想一种情形,你对HBa创建了大量的数据,每一个RegionServer都治理了各自的Region,这时你重启了HBa,重启了所有的RegionServer,所有的Region都会被随机的分派给各个RegionServer,这种情形下你显然无法保证咱们希望的本地数据存储。
在讨论如何解决那个问题之前咱们先强调一点:HBa不该该频繁的被重启,而且部署的架构不该该被频繁的改变,这是能解决那个问题的一个基础。写入HDFS的文件都有一个特点,一旦写入一个文件就无法更改(由于各类缘故)。因此HBa会按期的将数据写入HDFS中并生成一个新文件。那个地址有一个让人惊奇的地址:HDFS足够伶俐,它明白如何将文件写到最适合的地址。换句话说,它明白把文件放到什么地址使得RegionServer用起来最方便。若是想明白HDFS如何做到这一点,咱们需要深切学习Hadoop的源代码,看看前面提到的ate(Path path) 具体是怎么工作的。
在HDFS中实际挪用的函数是:ate(Path path), 他看起来是那个样子的:
public FSDataOutputStream create(Path f) throws IOException {
return create(f, true);
}
public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException {
return new ate(getPathName(f), permission, overwrite, replication, blockSize, progress, bufferSize), statistics);
}
其中dfs是一个连接到HDFS NameNode的DFSClient。当你向HDFS写入数据的时候,
数据都流过DFSClient.DFSOutputStream,DFSClient将这些数据搜集,积攒到必然程度后,作为一个Block写入到DataNode里面。
将一个Block写到DataNode的进程都发生在DFSClient.DFSOutputStream.DataStreamer里面,它是一个运行在后台的守护线程。注意,从此刻开始咱们将慢慢揭开解决问题的秘密方式。
在接收到一个Block以后,DataStreamer需要明白那个Block应该被写到哪些DataNode上面,同时它也应该让NameNode明白那个Block写到了哪些DataNode上面。它的做法是联络NameNode:Hi,我那个地址有一个文件的一个Block,请告知我应该写在哪些DataNode上面?