一、寫入數(shù)據(jù)
HDFS寫入數(shù)據(jù)時(shí)率寡,根據(jù)block存儲策略可能不同剪返。首先要看是否啟用了hadoop的機(jī)架感知废累,默認(rèn)是關(guān)閉的,所有機(jī)架名為“/default-rack”所以在hdfs寫入數(shù)據(jù)時(shí)是隨機(jī)的随夸,也就是說九默,很可能hadoop將第一個(gè)數(shù)據(jù)塊block1寫到rack1的node1上,然后將block2寫在了rack2上node2上宾毒,再接下來隨機(jī)的選擇block3又重新寫回了rack1上的node3上時(shí)驼修,這樣rack間的數(shù)據(jù)流量會成倍增加,成為性能的瓶頸诈铛,進(jìn)而影響整個(gè)集群的服務(wù)乙各。啟用機(jī)架感知,只需修改在namenode的配置文件hadoop-site.xml中的選項(xiàng):
<property>
<name>topology.script.file.name</name>
<value>/path/to/RackAware.py</value>
</property>
這個(gè)配置選項(xiàng)的value指定為一個(gè)可執(zhí)行程序幢竹,通常為一個(gè)腳本耳峦,該腳本接受一個(gè)參數(shù),輸出一個(gè)值焕毫。接受的參數(shù)通常為某臺datanode機(jī)器的 ip地址蹲坷,而輸出的值通常為該ip地址對應(yīng)的datanode所在的rack,例如"/rack1"邑飒。Namenode啟動時(shí)循签,會判斷該配置選項(xiàng)是否為空,如果非空疙咸,則表示已經(jīng)用機(jī)架感知的配置县匠,此時(shí)namenode會根據(jù)配置尋找該腳本,并在接收到每一個(gè)datanode的heartbeat時(shí)撒轮,將該 datanode的ip地址作為參數(shù)傳給該腳本運(yùn)行乞旦,并將得到的輸出作為該datanode所屬的機(jī)架,保存到內(nèi)存的一個(gè)map中题山。
至于腳本的編寫兰粉,就需要將真實(shí)的網(wǎng)絡(luò)拓樸和機(jī)架信息了解清楚后,通過該腳本能夠?qū)C(jī)器的ip地址正確的映射到相應(yīng)的機(jī)架上去顶瞳。一個(gè)簡單的實(shí)現(xiàn)如下:
!/usr/bin/python
--coding:UTF-8 --
import sys
rack = {"hostname1":"rack1",
"hostname2":"rack1",
"hostname3":"rack2",
"hostname4":"rack2",
"ip1":"rack1",
"ip2":"rack1",
"ip3":"rack2",
"ip4":"rack2",
}
if name=="main":
print "/" + rack.get(sys.argv[1],"rack0")
而當(dāng)Hadoop集群中配置了機(jī)架感知信息以后亲桦,hadoop在選擇三個(gè)datanode時(shí)崖蜜,就會進(jìn)行相應(yīng)的判斷:
1.如果上傳本機(jī)不是一個(gè)datanode,而是一個(gè)客戶端客峭,那么就從接近客戶端所在的機(jī)架下slave機(jī)器中隨機(jī)選擇一臺datanode作為第一個(gè)塊block1的寫入機(jī)器(datanode1)。
注意:而此時(shí)如果上傳機(jī)器本身就是一個(gè)datanode(例如mapreduce作業(yè)中task通過DFSClient向hdfs寫入數(shù)據(jù)的時(shí)候)抡柿,那么就將該datanode本身作為第一個(gè)塊寫入機(jī)器(datanode1)舔琅。
2.隨后在datanode1所屬的機(jī)架以外的另外的機(jī)架上,隨機(jī)的選擇一臺洲劣,作為第二個(gè)block的寫入datanode機(jī)器(datanode2)备蚓。
3.在寫第三個(gè)block前,先判斷是否前兩個(gè)datanode是否是在同一個(gè)機(jī)架上囱稽,如果是在同一個(gè)機(jī)架郊尝,那么就嘗試在另外一個(gè)機(jī)架上選擇第
三個(gè)datanode作為寫入機(jī)器(datanode3)。而如果datanode1和datanode2沒有在同一個(gè)機(jī)架上战惊,則在datanode2所在的機(jī)架上選擇一臺datanode作為datanode3流昏。
4.得到3個(gè)datanode的列表以后,從namenode返回該列表到DFSClient之前吞获,會在namenode端首先根據(jù)該寫入客戶端跟 datanode列表中每個(gè)datanode之間的"距離"由近到遠(yuǎn)進(jìn)行一個(gè)排序况凉。如果此時(shí)DFS寫入端不是datanode,則選擇datanode列表中的第一個(gè)排在第一位各拷〉笕蓿客戶端根據(jù)這個(gè)順序有近到遠(yuǎn)的進(jìn)行數(shù)據(jù)塊的寫入。
因此烤黍,判斷兩個(gè)datanode之間"距離"的算法就比較關(guān)鍵知市,hadoop目前實(shí)現(xiàn)如下,以兩個(gè)表示datanode的對象DatanodeInfo(node1,node2)為例:
每個(gè)datanode都會對應(yīng)自己在集群中的位置和層次速蕊,如node1的位置信息為"/rack1/datanode1",那么它所處的層次就為2嫂丙,其余類推。得到兩個(gè)node的層次后互例,會沿著每個(gè)node所處的拓樸樹中的位置向上查找奢入,如"/rack1/datanode1"的上一級就是" /rack1",此時(shí)兩個(gè)節(jié)點(diǎn)之間的距離加1媳叨,兩個(gè)node分別同上向上查找腥光,直到找到共同的祖先節(jié)點(diǎn)位置,此時(shí)所得的距離數(shù)就用來代表兩個(gè)節(jié)點(diǎn)之間的距
離糊秆。所以武福,如上圖所示,node1和node2之間的距離就為4.
5.當(dāng)根據(jù)"距離"排好序的datanode節(jié)點(diǎn)列表返回給DFSClient以后痘番,DFSClient便會創(chuàng)建BlockOutputStream捉片,并將這次block寫入pipeline中的第一個(gè)節(jié)點(diǎn)(最近的節(jié)點(diǎn))平痰。寫入塊時(shí),以更小的數(shù)據(jù)包packet(64k)寫入伍纫,datanode寫入一個(gè)小單位完成后就會把這個(gè)小單位的數(shù)據(jù)推送給下一個(gè)datanode宗雇,直到第一個(gè)block的最后一個(gè)數(shù)據(jù)包在第三個(gè)備份完成,向namenode報(bào)告寫完了第一個(gè)block莹规。第一個(gè)datanode向client 通知寫完了赔蒲,然后client向namenode確認(rèn)寫完以后,第一個(gè)block寫入完成良漱。其中校驗(yàn)和寫入時(shí)并行的舞虱。
6.寫完第一個(gè)block以后,依次按照datanode列表中的次遠(yuǎn)的node進(jìn)行寫入母市,直到最后一個(gè)block寫入成功矾兜,DFSClient返回成功,該block寫入操作結(jié)束患久。
概括下:client負(fù)責(zé)切割文件椅寺,NN負(fù)責(zé)為數(shù)據(jù)塊分配DN,DN對數(shù)據(jù)進(jìn)行存儲冗余G奖配并!
數(shù)據(jù)塊的第一個(gè)副本優(yōu)先放在寫入數(shù)據(jù)塊的客戶端所在的節(jié)點(diǎn)上,但是如果這個(gè)客戶端上的數(shù)據(jù)節(jié)點(diǎn)空間不足或者是當(dāng)前負(fù)載過重高镐,則應(yīng)該從該數(shù)據(jù)節(jié)點(diǎn)所在的機(jī)架中選擇一個(gè)合適的數(shù)據(jù)節(jié)點(diǎn)作為本地節(jié)點(diǎn)溉旋。如果客戶端上沒有一個(gè)數(shù)據(jù)節(jié)點(diǎn)的話,則從整個(gè)集群中隨機(jī)選擇一個(gè)合適的數(shù)據(jù)節(jié)點(diǎn)作為此時(shí)這個(gè)數(shù)據(jù)塊的本地節(jié)點(diǎn)嫉髓。HDFS的存放策略是將一個(gè)副本存放在本地機(jī)架節(jié)點(diǎn)上观腊,另外兩個(gè)副本放在不同機(jī)架的不同節(jié)點(diǎn)上。這樣集群可在完全失去某一機(jī)架的情況下還能存活算行。同時(shí)梧油,這種策略減少了機(jī)架間的數(shù)據(jù)傳輸,提高了寫操作的效率州邢,因?yàn)閿?shù)據(jù)塊只存放在兩個(gè)不同的機(jī)架上儡陨,減少了讀取數(shù)據(jù)時(shí)需要的網(wǎng)絡(luò)傳輸總帶寬。這樣在一定程度上兼顧了數(shù)據(jù)安全和網(wǎng)絡(luò)傳輸?shù)拈_銷量淌。
通過以上策略骗村,namenode在選擇數(shù)據(jù)塊的寫入datanode列表時(shí),就充分考慮到了將block副本分散在不同機(jī)架下呀枢,并同時(shí)盡量的避免了之前描述的過多的網(wǎng)絡(luò)開銷胚股。
讀取數(shù)據(jù)
我們看一下Hadoop集群配置中如何讀取數(shù)據(jù)。當(dāng)對某個(gè)文件的某個(gè)block進(jìn)行讀取的時(shí)候裙秋,hadoop采取的策略也是一樣:
1.首先得到這個(gè)block所在的datanode的列表琅拌,有幾個(gè)副本數(shù)該列表就有幾個(gè)datanode缨伊。用a列列出文件的數(shù)據(jù)塊,b列列出數(shù)據(jù)塊的對應(yīng)的DN进宝,告訴client刻坊,client知道用多少數(shù)據(jù)塊可以下載,并知道數(shù)據(jù)塊的位置党晋。
2.根據(jù)列表中datanode距離讀取端的距離進(jìn)行從小到大的排序:
a)首先查找本地是否存在該block的副本紧唱,如果存在,則將本地datanode作為第一個(gè)讀取該block的datanode
b)然后查找本地的同一個(gè)rack下是否有保存了該block副本的datanode
c)最后如果都沒有找到隶校,或者讀取數(shù)據(jù)的node本身不是datanode節(jié)點(diǎn),則返回datanode列表的一個(gè)隨機(jī)順序蛹锰。