小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

HBase 數(shù)據(jù)文件在HDFS上的存儲

 daomucun 2011-07-14

HBase 數(shù)據(jù)文件在HDFS上的存儲

517人閱讀 評論(0) 收藏 舉報(bào)

 

英文原文:http://www./2010/05/hbase-file-locality-in-hdfs.html

 

HDFS上面最不明確的事情之一就是數(shù)據(jù)的冗余。它完全是自動(dòng)進(jìn)行的,因?yàn)闊o法得知其中詳細(xì)的信息,我們需要做的就是相信它。HBase完全相信HDFS存儲數(shù)據(jù)的安全性和完整性,并將數(shù)據(jù)文件交給HDFS存儲。正是因?yàn)?/span>HDFS的數(shù)據(jù)冗余方式對于HBase來說是完全透明的,產(chǎn)生了一個(gè)問題:HBase的效率會受到多大的影響?說的簡單一點(diǎn),當(dāng)HBase需要存取數(shù)據(jù)時(shí),如何保證有一份冗余的數(shù)據(jù)塊離自己最近?當(dāng)我們對HBase做一次MapReduce的掃描操作時(shí),這個(gè)問題尤其顯現(xiàn)出來。所有的RegionServer都在從HDFS上面讀取數(shù)據(jù),理想的狀況當(dāng)然是每個(gè)RegionServer要讀取的數(shù)據(jù)都離自己很近。這個(gè)問題就牽扯到HBase的數(shù)據(jù)文件是如何在HDFS上面存儲的。

讓我們首先拋開HBase,假設(shè)要處理的數(shù)據(jù)就是HDFS上面的數(shù)據(jù)塊,看看Hadoop是如何工作的。MapReduce總是有一個(gè)建議,那就是在每個(gè)TaskTracker上面Map/Reduce程序要處理的數(shù)據(jù)在本地就有一份冗余。這樣程序只需要與本地?cái)?shù)據(jù)交互,減少了網(wǎng)絡(luò)流量并提高了效率。為了做到這一點(diǎn),HDFS會把大文件分割成很多小文件來存儲,我們稱之為數(shù)據(jù)塊(Block)。每個(gè)數(shù)據(jù)塊的大小比操作系統(tǒng)數(shù)據(jù)塊的大小要大得多,默認(rèn)是64M,但通常我們選擇128M,或者某個(gè)更大的值(這取決與你的文件大小,最好你的單個(gè)文件大小總是大于一個(gè)數(shù)據(jù)塊)。在MapReduce中,每個(gè)數(shù)據(jù)塊會被分配給一個(gè)Task,這個(gè)Task就負(fù)責(zé)處理這個(gè)數(shù)據(jù)塊中的數(shù)據(jù)。所以數(shù)據(jù)塊越大,產(chǎn)生的Task就越少,需要mapper的數(shù)量就越少。Hadoop自己知道每個(gè)數(shù)據(jù)塊存儲的位置,這樣在任務(wù)分配的時(shí)候就可以直接在存儲數(shù)據(jù)塊的機(jī)器上啟動(dòng)Task,或者選擇一個(gè)最近機(jī)器啟動(dòng)Task。真是因?yàn)槊總€(gè)數(shù)據(jù)塊有多份冗余,使得Hadoop有更大的選擇空間。只要找到一份冗余符合條件就行了,不是嗎?這樣Hadoop就可以保證在MapReduce期間Task總是操作本地?cái)?shù)據(jù)。

讓我們回到HBase,現(xiàn)在你已經(jīng)理解了Hadoop是如何保證在MapReduce的過程中每個(gè)Task都盡量處理本地?cái)?shù)據(jù)。如果你看過HBase的存儲架構(gòu)你就會知道HBase只是簡單的將HFileWAL log存儲在HDFS上面。通過簡單的調(diào)用HDFSAPI來創(chuàng)建文件:FileSystem.create(Path path)。接下來你會關(guān)心兩件事情的效率:1)隨機(jī)的訪問 2)通過MapReduce掃描全表。我們當(dāng)然希望當(dāng)每個(gè)RegionServer讀取數(shù)據(jù)時(shí)存儲數(shù)據(jù)的數(shù)據(jù)塊就在本地。它能做到嗎?

第一種情況,你有兩個(gè)集群,一個(gè)集群裝Hadoop,另一個(gè)集群裝HBase,兩個(gè)集群是分隔開的,只有網(wǎng)線來傳輸數(shù)據(jù)。好了,討論到此為止,神也幫不了你。

第二種情況,你有一個(gè)大的集群,每臺機(jī)器都混裝了HadoopHBase,每個(gè)RegionServer上面都有一個(gè)DataNode(這是我們最希望看到的)。好,這樣的話RegionServer就具備了從本地讀取數(shù)據(jù)的前提。我們還剩下一個(gè)問題,如何保證每個(gè)RegionServer管理的Region所對應(yīng)的HFileWAL log就存在本地的DataNode上面?設(shè)想一種情況,你對HBase創(chuàng)建了大量的數(shù)據(jù),每個(gè)RegionServer都管理了各自的Region,這時(shí)你重啟了HBase,重啟了所有的RegionServer,所有的Region都會被隨機(jī)的分配給各個(gè)RegionServer,這種情況下你顯然無法保證我們希望的本地?cái)?shù)據(jù)存儲。

在討論如何解決這個(gè)問題之前我們先強(qiáng)調(diào)一點(diǎn):HBase不應(yīng)該頻繁的被重啟,并且部署的架構(gòu)不應(yīng)該被頻繁的改變,這是能解決這個(gè)問題的一個(gè)基礎(chǔ)。寫入HDFS的文件都有一個(gè)特點(diǎn),一旦寫入一個(gè)文件就無法更改(由于種種原因)。因此HBase會定期的將數(shù)據(jù)寫入HDFS中并生成一個(gè)新文件。這里有一個(gè)讓人驚奇的地方:HDFS足夠聰明,它知道如何將文件寫到最合適的地方。換句話說,它知道把文件放到什么地方使得RegionServer用起來最方便。如果想知道HDFS如何做到這一點(diǎn),我們需要深入學(xué)習(xí)Hadoop的源代碼,看看前面提到的FileSystem.create(Path path) 具體是怎么工作的。

HDFS中實(shí)際調(diào)用的函數(shù)是:DistributedFileSystem.create(Path path), 他看起來是這個(gè)樣子的:

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 FSDataOutputStream(dfs.create(getPathName(f), permission, overwrite, replication, blockSize, progress, bufferSize), statistics);

}

其中dfs是一個(gè)連接到HDFS NameNodeDFSClient。當(dāng)你向HDFS寫入數(shù)據(jù)的時(shí)候,數(shù)據(jù)都流過DFSClient.DFSOutputStream,DFSClient將這些數(shù)據(jù)收集,積攢到一定程度后,作為一個(gè)Block寫入到DataNode里面。

將一個(gè)Block寫到DataNode的過程都發(fā)生在DFSClient.DFSOutputStream.DataStreamer里面,它是一個(gè)運(yùn)行在后臺的守護(hù)線程。注意,從現(xiàn)在開始我們將逐漸揭開解決問題的秘密方法。

在接收到一個(gè)Block以后,DataStreamer需要知道這個(gè)Block應(yīng)該被寫到哪些DataNode上面,同時(shí)它也應(yīng)該讓NameNode知道這個(gè)Block寫到了哪些DataNode上面。它的做法是聯(lián)絡(luò)NameNodeHi,我這里有一個(gè)文件的一個(gè)Block,請告訴我應(yīng)該寫在哪些DataNode上面?

nodes = nextBlockOutputStream(src);

->

long startTime = System.currentTimeMillis();

lb = locateFollowingBlock(startTime);

block = lb.getBlock();

nodes = lb.getLocations();

->

return namenode.addBlock(src, clientName);

這時(shí)NameNode收到了一個(gè)添加Block的請求,它包含兩個(gè)參數(shù):srcclientName

其中src標(biāo)明了這個(gè)Block屬于哪個(gè)文件,clientName則是client端的名稱。

我們跳過一些簡單的步驟來看最重要的一步:

public LocatedBlock getAdditionalBlock(String src, String clientName) throws IOException {

  ...

  INodeFileUnderConstruction pendingFile  = checkLease(src, clientName);

  ...

  fileLength = pendingFile.computeContentSummary().getLength();

  blockSize = pendingFile.getPreferredBlockSize();

  clientNode = pendingFile.getClientNode();

  replication = (int)pendingFile.getReplication();

 

  // choose targets for the new block tobe allocated.

  DatanodeDescriptor targets[] = replicator.chooseTarget(replication, clientNode, null, blockSize);

  ...

}

最重要的一步就是replicator.chooseTarget(),它的具體實(shí)現(xiàn)如下:

private DatanodeDescriptor chooseTarget(int numOfReplicas, DatanodeDescriptor writer, List<Node> excludedNodes, long blocksize, int maxNodesPerRack, List<DatanodeDescriptor> results) {

 

  if (numOfReplicas == 0 || clusterMap.getNumOfLeaves()==0) {

    return writer;

  }

 

  int numOfResults = results.size();

  boolean newBlock = (numOfResults==0);

  if (writer == null && !newBlock) {

    writer = (DatanodeDescriptor)results.get(0);

  }

 

  try {

    switch(numOfResults) {

    case 0:

      writer = chooseLocalNode(writer, excludedNodes, blocksize, maxNodesPerRack, results);

      if (--numOfReplicas == 0) {

        break;

      }

    case 1:

      chooseRemoteRack(1, results.get(0), excludedNodes, blocksize, maxNodesPerRack, results);

      if (--numOfReplicas == 0) {

        break;

      }

    case 2:

      if (clusterMap.isOnSameRack(results.get(0), results.get(1))) {

        chooseRemoteRack(1, results.get(0), excludedNodes, blocksize, maxNodesPerRack, results);

      } else if (newBlock) {

        chooseLocalRack(results.get(1), excludedNodes, blocksize, maxNodesPerRack, results);

      } else {

        chooseLocalRack(writer, excludedNodes, blocksize, maxNodesPerRack, results);

      }

      if (--numOfReplicas == 0) {

        break;

      }

    default:

      chooseRandom(numOfReplicas, NodeBase.ROOT, excludedNodes, blocksize, maxNodesPerRack, results);

    }

  } catch (NotEnoughReplicasException e) {

    FSNamesystem.LOG.warn("Not able to place enough replicas, still in need of " + numOfReplicas);

  }

  return writer;

}

這段代碼很清楚的說明了整個(gè)的選擇過程,NameNode總是為第一份冗余優(yōu)先選擇本地節(jié)點(diǎn)作為存儲空間,對于第二份冗余,則是優(yōu)先選擇另一個(gè)機(jī)架的節(jié)點(diǎn)。如果前兩份冗余位于不同機(jī)架,第三份冗余偏向于選擇與第一份冗余相同的機(jī)架,否則選擇不同的機(jī)架。大于三份的冗余就聽天由命,隨機(jī)挑選節(jié)點(diǎn)了。

總結(jié)一下,基于當(dāng)前的情況,每個(gè)Region Server運(yùn)行的時(shí)間越長,那么數(shù)據(jù)的存儲地點(diǎn)就越穩(wěn)定,每個(gè)Region Server就能保證它要管理的數(shù)據(jù)在本地就有一份拷貝。這樣無論是Scan還是MapReduce都能達(dá)到效率的最優(yōu)化。

最后要說的是HBase Team正在致力于重新設(shè)計(jì)MasterServer分配Region的機(jī)制。新的設(shè)計(jì)能夠盡量保證每個(gè)Region被分配給擁有最多Region BlockRegion Server。這將能夠部分解決重啟RegionServer所帶來的問題。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多