1.1、介紹Hadoop
廣義上來說,Hadoop通常是指一個更廣泛的概念——Hadoop生態圈。
狹義上說,Hadoop指Apache這款開源框架,它的核心元件有:
(1)、HDFS(分散式檔案系統):解決海量資料儲存
(2)、YARN(作業排程和叢集資源管理的框架):解決資源任務排程
(3)、MAPREDUCE(分散式運算程式設計框架):解決海量資料計算
1.2、Hadoop特性優點
(1)、擴容能力(Scalable):Hadoop是在可用的計算機叢集間分配資料並完成計算任務的,這些叢集可用方便的擴充套件到數以千計的節點中。
(2)、成本低(Economical):Hadoop透過普通廉價的機器組成伺服器叢集來分發以及處理資料,以至於成本很低。
(3)、高效率(Efficient):透過併發資料,Hadoop可以在節點之間動態並行的移動資料,使得速度非常快。
(4)、可靠性(Rellable):能自動維護資料的多份複製,並且在任務失敗後能自動地重新部署(redeploy)計算任務。所以Hadoop的按位儲存和處理資料的能力值得人們信賴。
1.3、hadoop叢集都需要啟動哪些程序,作用分別是什麼?
(1)、namenode =>HDFS的守護程序,負責維護整個檔案系統,儲存著整個檔案系統的後設資料資訊,image+edit log。
(2)、datanode =>是具體檔案系統的工作節點,當我們需要某個資料,NameNode告訴我們去哪裡找,就直接和那個DataNode對應的伺服器的後臺程序進行通訊,由DataNode進行資料的檢索,然後進行具體的讀/寫操作。
(3)、secondarynamenode =>一個守護程序,相當於一個NameNode的後設資料的備份機制,定期的更新,和NameNode進行通訊,將NameNode上的image和edits進行合併,可以作為NameNode的備份使用。
(4)、resourcemanager =>是yarn平臺的守護程序,負責所有資源的分配與排程,client的請求由此負責,監控nodemanager。
(5)、nodemanager => 是單個節點的資源管理,執行來自resourcemanager的具體任務和命令
(6)、DFSZKFailoverController高可用時它負責監控NN的狀態,並及時的把狀態資訊寫入ZK。它透過一個獨立執行緒週期性的呼叫NN上的一個特定介面來獲取NN的健康狀態。FC也有選擇誰作為Active NN的權利,因為最多隻有兩個節點,目前選擇策略還比較簡單(先到先得,輪換)。
(7)、JournalNode 高可用情況下存放NameNode的editlog檔案。
1.4、Hadoop主要的配置檔案
(1)、hadoop-env.sh
檔案中設定的是Hadoop執行時需要的環境變數。JAVA_HOME是必須設定的,即使我們當前的系統中設定了JAVA_HOME,它也是不認識的,因為Hadoop即使是在本機上執行,它也是把當前的執行環境當成遠端伺服器。
(2)、core-site.xml
設定Hadoop的檔案系統地址
<property> <name>fs.defaultFS</name> <value>hdfs://node-1:9000</value> </property>
(3)、hdfs-site.xml
1) 指定HDFS副本的數量
2) secondary namenode 所在主機的ip和埠
<property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.secondary.http-address</name> <value>node-2:50090</value> </property>
(4)、mapred-site.xml
指定mr執行時框架,這裡指定在yarn上,預設是local
<property> <name>mapreduce.framework.name</name> <value>yarn</value> </property>
(5)、yarn-site.xml
指定YARN的主角色(ResourceManager)的地址
<property> <name>yarn.resourcemanager.hostname</name> <value>node-1</value> </property>
1.5、Hadoop叢集重要命令
(1)、初始化
hadoop namenode -format
(2)、啟動dfs
start-dfs.sh
(3)、啟動yarn
start-yarn.sh
4)、啟動任務歷史伺服器
mr-jobhistory-daemon.sh start historyserver
(5)、一鍵啟動
start-all.sh
(6)、啟動成功後:
NameNode http://nn_host:port/ 預設50070.
ResourceManagerhttp://rm_host:port/ 預設 8088
選項名稱 |
使用格式 |
含義 |
-ls |
-ls <路徑> |
檢視指定路徑的當前目錄結構 |
-lsr |
-lsr <路徑> |
遞迴檢視指定路徑的目錄結構 |
-du |
-du <路徑> |
統計目錄下個檔案大小 |
-dus |
-dus <路徑> |
彙總統計目錄下檔案(夾)大小 |
-count |
-count [-q] <路徑> |
統計檔案(夾)數量 |
-mv |
-mv <源路徑> <目的路徑> |
移動 |
-cp |
-cp <源路徑> <目的路徑> |
複製 |
-rm |
-rm [-skipTrash] <路徑> |
刪除檔案/空白資料夾 |
-rmr |
-rmr [-skipTrash] <路徑> |
遞迴刪除 |
-put |
-put <多個linux上的檔案> <hdfs路徑> |
上傳檔案 |
-copyFromLocal |
-copyFromLocal <多個linux上的檔案> <hdfs路徑> |
從本地複製 |
-moveFromLocal |
-moveFromLocal <多個linux上的檔案> <hdfs路徑> |
從本地移動 |
-getmerge |
-getmerge <源路徑> <linux路徑> |
合併到本地 |
-cat |
-cat <hdfs路徑> |
檢視檔案內容 |
-text |
-text <hdfs路徑> |
檢視檔案內容 |
-copyToLocal |
-copyToLocal [-ignoreCrc] [-crc] [hdfs源路徑] [linux目的路徑] |
從本地複製 |
-moveToLocal |
-moveToLocal [-crc] <hdfs源路徑> <linux目的路徑> |
從本地移動 |
-mkdir |
-mkdir <hdfs路徑> |
建立空白資料夾 |
-setrep |
-setrep [-R] [-w] <副本數> <路徑> |
修改副本數量 |
-touchz |
-touchz <檔案路徑> |
建立空白檔案 |
-stat |
-stat [format] <路徑> |
顯示檔案統計資訊 |
-tail |
-tail [-f] <檔案> |
檢視檔案尾部資訊 |
-chmod |
-chmod [-R] <許可權模式> [路徑] |
修改許可權 |
-chown |
-chown [-R] [屬主][:[屬組]] 路徑 |
修改屬主 |
-chgrp |
-chgrp [-R] 屬組名稱 路徑 |
修改屬組 |
-help |
-help [命令選項] |
幫助 |
1.6、HDFS的垃圾桶機制
修改core-site.xml
<property> <name>fs.trash.interval</name> <value>1440</value> </property>
這個時間以分鐘為單位,例如1440=24h=1天。HDFS的垃圾回收的預設配置屬性為 0,也就是說,如果你不小心誤刪除了某樣東西,那麼這個操作是不可恢復的。
1.7、HDFS寫資料流程
HDFS dfs -put a.txt /
詳細步驟:
1)客戶端透過Distributed FileSystem模組向namenode請求上傳檔案,namenode檢查目標檔案是否已存在,父目錄是否存在。
2)namenode返回是否可以上傳。
3)客戶端請求第一個 block上傳到哪幾個datanode伺服器上。
4)namenode返回3個datanode節點,分別為dn1、dn2、dn3。
5)客戶端透過FSDataOutputStream模組請求dn1上傳資料,dn1收到請求會繼續呼叫dn2,然後dn2呼叫dn3,將這個通訊管道建立完成。
6)dn1、dn2、dn3逐級應答客戶端。
7)客戶端開始往dn1上傳第一個block(先從磁碟讀取資料放到一個本地記憶體快取),以packet為單位(大小為64k),dn1收到一個packet就會傳給dn2,dn2傳給dn3;dn1每傳一個packet會放入一個應答佇列等待應答。
8)當一個block傳輸完成之後,客戶端再次請求namenode上傳第二個block的伺服器。
1.8、Hadoop讀資料流程
詳細步驟:
1)客戶端透過Distributed FileSystem向namenode請求下載檔案,namenode透過查詢後設資料,找到檔案塊所在的datanode地址。
2)挑選一臺datanode(就近原則,然後隨機)伺服器,請求讀取資料。
3)datanode開始傳輸資料給客戶端(從磁碟裡面讀取資料輸入流,以packet為單位來做校驗,大小為64k)。
4)客戶端以packet為單位接收,先在本地快取,然後寫入目標檔案。
checkpoint流程
在hdfs-site.xml檔案指定secondarynamenode部署在哪個機器上
<property> <name>dfs.namenode.secondary.http-address</name> <value>hdp02:50090</value> </property>
1.sn提醒nn做checkpoint
2.nn上的edit_inprogress檔案滾動生成edit_sum檔案
3.將edit_sum檔案傳給sn
4.sn更新fsimage檔案
5.sn將檔案寫入磁碟
6.將檔案傳給nn
作用講解:
edit:NameNode在本地操作hdfs系統的檔案都會儲存在Edits日誌檔案中。也就是說當檔案系統中的任何後設資料產生操作時,都會記錄在Edits日誌檔案中(增刪改查)
fsimage:存放的是一份最完整的後設資料資訊,內容比較大
edit一直記錄後設資料操作記錄的話,也會慢慢膨脹的比較大,也會造成操作起來比較困難
為了控制edits不會膨脹太大,引入secondaryNameNode機制。
ss主要作用合併edits和fsimage檔案,清空edit
後設資料
後設資料的分類
按形式分類:記憶體後設資料和後設資料檔案;它們的存在的位置分別為:記憶體和磁碟上。其中記憶體後設資料主要是hdfs檔案目錄的管理;後設資料檔案則用於持久化儲存。
按型別分,後設資料主要包括:
1、檔案、目錄自身的屬性資訊,例如檔名,目錄名,修改資訊等。
2、檔案記錄的資訊的儲存相關的資訊,例如儲存塊資訊,分塊情況,副本個數等。
3、記錄HDFS的Datanode的資訊,用於DataNode的管理。
1.9、SecondaryNameNode的作用
NameNode職責是管理後設資料資訊,DataNode的職責是負責資料具體儲存,那麼SecondaryNameNode的作用是什麼?
答:它的職責是合併NameNode的edit logs到fsimage檔案。
每達到觸發條件 [達到一個小時,或者事物數達到100萬],會由secondary namenode將namenode上積累的所有edits和一個最新的fsimage下載到本地,並載入到記憶體進行merge(這個過程稱為checkpoint),如下圖所示:
1.10、HDFS的擴容、縮容(面試)
(1)、動態擴容
隨著公司業務的增長,資料量越來越大,原有的datanode節點的容量已經不能滿足儲存資料的需求,需要在原有叢集基礎上動態新增新的資料節點。也就是俗稱的動態擴容。
有時候舊的伺服器需要進行退役更換,暫停服務,可能就需要在當下的叢集中停止某些機器上hadoop的服務,俗稱動態縮容。
1)、基礎準備
在基礎準備部分,主要是設定hadoop執行的系統環境
修改新機器系統hostname(透過/etc/sysconfig/network進行修改)
修改hosts檔案,將叢集所有節點hosts配置進去(叢集所有節點保持hosts檔案統一)
設定NameNode到DataNode的免密碼登入(ssh-copy-id命令實現)
修改主節點slaves檔案,新增新增節點的ip資訊(叢集重啟時配合一鍵啟動指令碼使用)
在新的機器上上傳解壓一個新的hadoop安裝包,從主節點機器上將hadoop的所有配置檔案,scp到新的節點上。
2)、新增datanode
在namenode所在的機器的
/export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop目錄下建立dfs.hosts檔案
cd /export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop
vim dfs.hosts
新增如下主機名稱(包含新服役的節點)
node-1
node-2
node-3
node-4
在namenode機器的hdfs-site.xml配置檔案中增加dfs.hosts屬性
cd /export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop
vim hdfs-site.xml
<property> <name>dfs.hosts</name> <value>/export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop/dfs.hosts</value> </property>
dfs.hosts屬性的意義:命名一個檔案,其中包含允許連線到namenode的主機列表。必須指定檔案的完整路徑名。如果該值為空,則允許所有主機。相當於一個白名單,也可以不配置。
在新的機器上單獨啟動datanode: hadoop-daemon.sh start datanode
重新整理頁面就可以看到新的節點加入進來了
3)、datanode負載均衡服務
新加入的節點,沒有資料塊的儲存,使得叢集整體來看負載還不均衡。因此最後還需要對hdfs負載設定均衡,因為預設的資料傳輸頻寬比較低,可以設定為64M,即hdfs dfsadmin -setBalancerBandwidth 67108864即可。
預設balancer的threshold為10%,即各個節點與叢集總的儲存使用率相差不超過10%,我們可將其設定為5%。然後啟動Balancer,
sbin/start-balancer.sh -threshold 5,等待叢集自均衡完成即可。
4)、新增nodemanager
在新的機器上單獨啟動nodemanager:
yarn-daemon.sh start nodemanager
在ResourceManager,透過yarn node -list檢視叢集情況
(2)、動態縮容
1)、新增退役節點
在namenode所在伺服器的hadoop配置目錄etc/hadoop下建立dfs.hosts.exclude檔案,並新增需要退役的主機名稱。
注意:該檔案當中一定要寫真正的主機名或者ip地址都行,不能寫node-4
node04.hadoop.com
在namenode機器的hdfs-site.xml配置檔案中增加dfs.hosts.exclude屬性
cd /export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop
vim hdfs-site.xml
<property> <name>dfs.hosts.exclude</name> <value>/export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop/dfs.hosts.exclude</value> </property>
dfs.hosts.exclude屬性的意義:命名一個檔案,其中包含不允許連線到namenode的主機列表。必須指定檔案的完整路徑名。如果值為空,則不排除任何主機。
2)、重新整理叢集
在namenode所在的機器執行以下命令,重新整理namenode,重新整理resourceManager。
hdfs dfsadmin -refreshNodes
yarn rmadmin –refreshNodes
等待退役節點狀態為decommissioned(所有塊已經複製完成),停止該節點及節點資源管理器。注意:如果副本數是3,服役的節點小於等於3,是不能退役成功的,需要修改副本數後才能退役。
node-4執行以下命令,停止該節點程序
cd /export/servers/hadoop-2.6.0-cdh5.14.0
sbin/hadoop-daemon.sh stop datanode
sbin/yarn-daemon.sh stop nodemanager
namenode所在節點執行以下命令重新整理namenode和resourceManager
hdfs dfsadmin –refreshNodes
yarn rmadmin –refreshNodes
namenode所在節點執行以下命令進行均衡負載
cd /export/servers/hadoop-2.6.0-cdh5.14.0/
sbin/start-balancer.sh
1.11、HDFS安全模式
安全模式是HDFS所處的一種特殊狀態,在這種狀態下,檔案系統只接受讀資料請求,而不接受刪除、修改等變更請求,是一種保護機制,用於保證叢集中的資料塊的安全性。
在NameNode主節點啟動時,HDFS首先進入安全模式,叢集會開始檢查資料塊的完整性。DataNode在啟動的時候會向namenode彙報可用的block資訊,當整個系統達到安全標準時,HDFS自動離開安全模式。
手動進入安全模式
hdfs dfsadmin -safemode enter
手動離開安全模式
hdfs dfsadmin -safemode leave
1.12、機架感知
hadoop自身是沒有機架感知能力的,必須透過人為的設定來達到這個目的。一種是透過配置一個指令碼來進行對映;另一種是透過實現DNSToSwitchMapping介面的resolve()方法來完成網路位置的對映。
1、寫一個指令碼,然後放到hadoop的core-site.xml配置檔案中,用namenode和jobtracker進行呼叫。
#!/usr/bin/python #-*-coding:UTF-8 -*- import sys rack = {"hadoop-node-31":"rack1", "hadoop-node-32":"rack1", "hadoop-node-33":"rack1", "hadoop-node-34":"rack1", "hadoop-node-49":"rack2", "hadoop-node-50":"rack2", "hadoop-node-51":"rack2", "hadoop-node-52":"rack2", "hadoop-node-53":"rack2", "hadoop-node-54":"rack2", "192.168.1.31":"rack1", "192.168.1.32":"rack1", "192.168.1.33":"rack1", "192.168.1.34":"rack1", "192.168.1.49":"rack2", "192.168.1.50":"rack2", "192.168.1.51":"rack2", "192.168.1.52":"rack2", "192.168.1.53":"rack2", "192.168.1.54":"rack2" } if __name__=="__main__": print "/" + rack.get(sys.argv[1],"rack0")
2、將指令碼賦予可執行許可權chmod +x RackAware.py,並放到bin/目錄下。
3、然後開啟conf/core-site.html
<property> <name>topology.script.file.name</name> <value>/opt/modules/hadoop/hadoop-1.0.3/bin/RackAware.py</value> <!--機架感知指令碼路徑--> </property> <property> <name>topology.script.number.args</name> <value>20</value> <!--機架伺服器數量,由於我寫了20個,所以這裡寫20--> </property>
4、重啟Hadoop叢集
namenode日誌
2012-06-08 14:42:19,174 INFO org.apache.hadoop.hdfs.StateChange: BLOCK* NameSystem.registerDatanode: node registration from 192.168.1.49:50010 storage DS-1155827498-192.168.1.49-50010-1338289368956
2012-06-08 14:42:19,204 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /rack2/192.168.1.49:50010
2012-06-08 14:42:19,205 INFO org.apache.hadoop.hdfs.StateChange: BLOCK* NameSystem.registerDatanode: node registration from 192.168.1.53:50010 storage DS-1773813988-192.168.1.53-50010-1338289405131
2012-06-08 14:42:19,226 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /rack2/192.168.1.53:50010
2012-06-08 14:42:19,226 INFO org.apache.hadoop.hdfs.StateChange: BLOCK* NameSystem.registerDatanode: node registration from 192.168.1.34:50010 storage DS-2024494948-127.0.0.1-50010-1338289438983
2012-06-08 14:42:19,242 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /rack1/192.168.1.34:50010
2012-06-08 14:42:19,242 INFO org.apache.hadoop.hdfs.StateChange: BLOCK* NameSystem.registerDatanode: node registration from 192.168.1.54:50010 storage DS-767528606-192.168.1.54-50010-1338289412267
2012-06-08 14:42:49,492 INFO org.apache.hadoop.hdfs.StateChange: STATE* Network topology has 2 racks and 10 datanodes
2012-06-08 14:42:49,492 INFO org.apache.hadoop.hdfs.StateChange: STATE* UnderReplicatedBlocks has 0 blocks
2012-06-08 14:42:49,642 INFO org.apache.hadoop.hdfs.server.namenode.FSNamesystem: ReplicateQueue QueueProcessingStatistics: First cycle completed 0 blocks in 0 msec
2012-06-08 14:42:49,642 INFO org.apache.hadoop.hdfs.server.namenode.FSNamesystem: ReplicateQueue QueueProcessingStatistics: Queue flush completed 0 blocks in 0 msec processing time, 0 msec clock time, 1 cycles
1.13、其他
1.13.1、資料儲存在hdfs格式,使用的什麼壓縮方式,壓縮比多少
目前在Hadoop中用得比較多的有lzo ,gzip ,snappy , bzip2這4種壓縮格式,筆者根據實踐經驗介紹一 下這4種壓縮格式的優缺點和應用場景,以便大家在實踐中根據實際情況選擇不同的壓縮格式。
(1)、gzip壓縮
優點:
壓縮率比較高,而且壓縮/解壓速度也比較快;
hadoop本身支援,在應用中處理gzip格式的檔案就和直接處理文字一樣; 有hadoop native庫;
大部分linux系統都自帶gzip命令,使用方便。
缺點:不支援split。
應用場景:
當每個檔案壓縮之後在130M以內的(1個塊大小內),都可以考慮用gzip壓縮格式。譬如說 一天或者一個小時的日誌壓縮成一個gzip檔案,執行mapreduce程式的時候透過多個gzip文 件達到併發。
hive程式, streaming程式,和java寫的mapreduce程式完全和文字處理一樣,壓縮之後原來 的程式不需要做任何修改。
(2)、lzo壓縮
優點:
壓縮/解壓速度也比較快,合理的壓縮率;
支援split,是hadoop中最流行的壓縮格式;
支援hadoop native庫;
可以在linux系統下安裝lzop命令,使用方便。
缺點:
壓縮率比gzip要低一些;
hadoop本身不支援,需要安裝;
在應用中對lzo格式的檔案需要做一些特殊處理(為了支援split需要建索引,還需要指定
inputformat為lzo格式)。
應用場景:
一個很大的文字檔案,壓縮之後還大於200M以上的可以考慮,而且單個檔案越大, lzo優點越 明顯。
(3)、snappy壓縮
優點:
高速壓縮速度和合理的壓縮率;
支援hadoop native庫。
缺點:
不支援split;
壓縮率比gzip要低;
hadoop本身不支援,需要安裝;
linux系統下沒有對應的命令。
應用場景:
當mapreduce作業的map輸出的資料比較大的時候,作為map到reduce的中間資料的壓縮格 式;或者作為一個mapreduce作業的輸出和另外一個mapreduce作業的輸入。
(4)、bzip2壓縮
優點:
支援split;
具有很高的壓縮率,比gzip壓縮率都高;
hadoop本身支援,但不支援native;
在linux系統下自帶bzip2命令,使用方便。
缺點:
壓縮/解壓速度慢;
不支援native。
應用場景:
適合對速度要求不高,但需要較高的壓縮率的時候,可以作為mapreduce作業的輸出格式; 或者輸出之後的資料比較大,處理之後的資料需要壓縮存檔減少磁碟空間並且以後資料用得比 較少的情況;
或者對單個很大的文字檔案想壓縮減少儲存空間,同時又需要支援split,而且相容之前的應用 程式(即應用程式不需要修改)的情況。
1.13.2、高可用的叢集中namenode當機了,怎麼恢復的
首先進入安全模式:
hdfs dfsadmin -safemode enter
然後刷一下active節點的log到image
hdfs dfsadmin -saveNamespace
然後將active節點的image檔案全部複製到故障節點的相應目錄下
然後重啟故障namenode
最後hdfs namenode -bootstrapStandby
到此,故障解決。
後來還解決過一次hdfs的block丟失的問題,也是將原先的image全部複製回來搞定的。 所以說,即便有HA,定期備份image檔案還是很重要的。
1.13.3、hdfs的瓶頸
(1)、fsimage載入階段,主要耗時的操作:
1) FSDirectory.addToParent(),功能是根據路徑path生成節點INode,並加入目錄樹中,佔載入時間的73%;
2) FSImage.readString和PermissionStatus.read操作是從fsimage的檔案流中讀取資料(分別 是讀取String和short)的操作,分別佔載入時間的15%和8%;
最佳化方案:對fsimage的持久化操作採用多執行緒技術,為目錄列表中的每個目錄儲存開闢一個線 程,用來儲存fsimage檔案。主執行緒等待所有儲存的子執行緒完畢後完成對fsimage載入。這樣,存 儲時間將取決於儲存最慢的那個執行緒,達到了提高fsimage載入速度的目的,從而在一定程度上提 升了NameNode啟動速度。
(2)、blockReport階段, DataNode在啟動時透過RPC呼叫NameNode.blockReport()方法, 主要耗時操作:
1) FSNamesystem.addStoredBlock呼叫,對每一個彙報上來的block,將其於彙報上來的 datanode的對應關係初始化到namenode記憶體中的BlocksMap表中,佔用時間比為77%;此方法中主要耗時的兩個階段分別是FSNamesystem.countNode和DatanodeDescriptor.addBlock,後 者是java中的插表操作,而FSNamesystem.countNode呼叫的目的是為了統計在BlocksMap中, 每一個block對應的各副本中,有幾個是live狀態,幾個是decommission狀態,幾個是Corrupt狀態。
2) DatanodeDescriptor.reportDiff,主要是為了將該datanode彙報上來的blocks跟 namenode記憶體中的BlocksMap中進行對比,以決定那個哪些是需要新增到BlocksMap中的 block,哪些是需要新增到toRemove佇列中的block,以及哪些是新增到toValidate佇列中的 block,佔用時間比為20%;
(3)、鎖的競爭成為效能瓶頸
最佳化方案:其中鎖內耗時的操作有FSEditLog.logXXX方法,可以考慮將FSEditLog的logXXX操作放 到寫鎖外記錄,但會引起記錄的順序不一致,可以透過在寫鎖內生成SerialNumber,在鎖外按順 序輸出來解決;
(4)、UTF8/Unicode轉碼最佳化成為效能瓶頸
最佳化方案:用SIMD指令的JVM Intrinsic轉碼實現, 32bytes比目前Hadoop內pure Java實現快4 倍,64bytes快十幾倍。
(5)、RPC框架中, N個Reader將封裝的Call物件放入一個BlockingQueue , N個Handler從一個 BlockingQueue中提取Call物件,使得此BlockingQueue成為效能瓶頸
最佳化方案:採用多佇列,一個Handler一個BlockingQueue,按callid分配佇列。
(6)、sendHeartbeat階段,在DataNode呼叫namenode.sendHeartbeat()方法時會DF和DU兩個類, 它們透過Shell類的runComamnd方法來執行系統命令,以獲取當前目錄的 df, du 值,在 runComamnd方法中實質是透過java.lang. ProcessBuilder類來執行系統命令的。該類由JVM透過 Linux 核心來fork 子程序,子程序當然會完全繼承父程序的所有記憶體控制代碼, jstack看到JVM此時線 程狀態大部分處於WAITING , 這個過程會影響DFSClient寫入超時,或關閉流出錯。
1.13.4、hdfs存放檔案量過大怎麼最佳化
(1)、可以透過呼叫 HDFS 的 sync() 方法(和 append 方法結合使用),每隔一定時間生成一個大檔案。或者可以透過寫一個 MapReduce 程式來來合併這些小檔案。
(2)、HAR File
Hadoop Archives ( HAR files)是在 0.18.0 版本中引入到 HDFS 中的,它的出現就是為了緩解大量小檔案消耗 NameNode 記憶體的問題。 HAR 檔案是透過在 HDFS 上構建一個分層檔案系統來工 作。 HAR 檔案透過 hadoop archive 命令來建立,而這個命令實際上是執行 MapReduce 作業來將 小檔案打包成少量的 HDFS 檔案(譯者注:將小檔案進行合併成幾個大檔案)。對於客戶端來說, 使用 HAR 檔案系統沒有任何的變化:所有原始檔案都可見以及可以訪問(只是使用 har://URL,而 不是 hdfs://URL),但是在 HDFS 中中檔案個數卻減少了。
讀取 HAR 檔案不如讀取 HDFS 檔案更有效,並且實際上可能更慢,因為每個 HAR 檔案訪問需要讀 取兩個索引檔案以及還要讀取資料檔案本。
儘管 HAR 檔案可以用作 MapReduce 的輸入,但是 Map 沒有辦法直接對共同駐留在 HDFS 塊上的 HAR 所有檔案操作。可以考慮透過建立一種 input format,充分利用 HAR 檔案的區域性性優勢,但 是目前還沒有這種input format。需要注意的是: MultiFileInputSplit,即使在 HADOOP-4565 進 行了改進,選擇節點本地分割中的檔案,但始終還是需要每個小檔案的搜尋。在目前看來, HAR 可能最好僅用於儲存文件。
(3)、SequenceFile
通常解決”小檔案問題”的回應是:使用 SequenceFile。這種方法的思路是,使用檔名作為 key, 檔案內容作為 value
在實踐中這種方式非常有效。我們回到10,000個100KB大小的小檔案問題上,你可以編寫一個程式 將合併為一個 SequenceFile,然後你可以以流式方式處理(直接處理或使用 MapReduce) SequenceFile。這樣會帶來兩個優勢:
SequenceFiles 是可拆分的,因此 MapReduce 可以將它們分成塊,分別對每個塊進行操作;
與 HAR 不同,它們支援壓縮。在大多數情況下,塊壓縮是最好的選擇,因為它直接對幾個記錄組 成的塊進行壓縮,而不是對每一個記錄進行壓縮。
將現有資料轉換為 SequenceFile 可能會很慢。但是,完全可以並行建立一個一個的 SequenceFile 檔案。 Stuart Sierra 寫了一篇關於將 tar 檔案轉換為 SequenceFile 的文章,像這樣的工具是非常 有用的,我們應該多看看。向前看,最好設計好資料管道,如果可能的話,將源資料直接寫入 SequenceFile ,而不是作為中間步驟寫入小檔案。
與 HAR 檔案不同,沒有辦法列出 SequenceFile 中的所有鍵,所以不能讀取整個檔案。 Map File, 類似於對鍵進行排序的 SequenceFile,維護部分索引,所以他們也不能列出所有的鍵
1.13.5、HDFS小檔案問題及解決方案
小檔案是指檔案size小於HDFS上block大小的檔案,這樣的檔案會給hadoop的擴充套件性和效能帶來嚴重問題。
首先,在HDFS中,任何block、檔案或者目錄在記憶體中均以物件的形式儲存,每個物件約佔150byte,如果有1000 0000個小檔案,每個檔案佔用一個block,則namenode大約需要2G空間。如果儲存1億個檔案,則namenode需要20G空間。這樣namenode記憶體容量嚴重製約了叢集的擴充套件。
其次,訪問大量小檔案速度遠遠小於訪問幾個大檔案。HDFS最初是為流式訪問大檔案開發的,如果訪問大量小檔案,需要不斷的從一個datanode跳到另一個datanode,嚴重影響效能。
最後,處理大量小檔案速度遠遠小於處理同等大小的大檔案的速度。每一個小檔案要佔用一個task,而task啟動將耗費大量時間甚至大部分時間都耗費在啟動task和釋放task上。
對於小檔案問題,Hadoop本身也提供了幾個解決方案,分別為:Hadoop Archive,Sequence file和CombineFileInputFormat。
1)、Hadoop Archive: Hadoop Archive或者HAR,是一個高效地將小檔案放入HDFS塊中的檔案存檔工具,它能夠將多個小檔案打包成一個HAR檔案,這樣在減少namenode記憶體使用的同時,仍然允許對檔案進行透明的訪問。
HAR是在Hadoop file system之上的一個檔案系統,因此所有fs shell命令對HAR檔案均可用,只不過是檔案路徑格式不一樣
使用HAR時需要兩點,第一,對小檔案進行存檔後,原檔案並不會自動被刪除,需要使用者自己刪除;第二,建立HAR檔案的過程實際上是在執行一個mapreduce作業,因而需要有一個hadoop叢集執行此命令。
此外,HAR還有一些缺陷:第一,一旦建立,Archives便不可改變。要增加或移除裡面的檔案,必須重新建立歸檔檔案。第二,要歸檔的檔名中不能有空格,否則會丟擲異常,可以將空格用其他符號替換
2)、 sequence file :sequence file由一系列的二進位制key/value組成,如果為key小檔名,value為檔案內容,則可以將大批小檔案合併成一個大檔案。
3)、CombineFileInputFormat:CombineFileInputFormat是一種新的inputformat,用於將多個檔案合併成一個單獨的split
1.13.6、HDFS高可用(HA)
高可用原理
所謂HA,即高可用(7*24小時不中斷服務),實現高可用最關鍵的是消除單點故障。
HDFS 的 HA 就是透過雙namenode 來防止單點問題,一旦主NameNode出現故障,可以迅速切換至備用的 NameNode。兩個namenode有不同的狀態,狀態分別是Active和Standby。(備用) Namenode作為熱備份,在機器發生故障時能夠快速進行故障轉移,同時在日常維護的時候進行Namenode切換。
HDFS HA的幾大重點
1)保證兩個namenode裡面的記憶體中儲存的檔案的後設資料同步
->namenode啟動時,會讀映象檔案
2)變化的記錄資訊同步
3)日誌檔案的安全性
->分散式的儲存日誌檔案(cloudera公司提出來的)
->2n+1個,使用副本數保證安全性
->使用zookeeper監控
->監控兩個namenode,當一個出現了問題,可以達到自動故障轉移。
->如果出現了問題,不會影響整個叢集
->zookeeper對時間同步要求比較高。
4)客戶端如何知道訪問哪一個namenode
->使用proxy代理
->隔離機制
->使用的是sshfence
->兩個namenode之間無密碼登入
5)namenode是哪一個是active
->zookeeper透過選舉選出zookeeper。
->然後zookeeper開始監控,如果出現檔案,自動故障轉移。
HDFS HA的實現流程
主Namenode處理所有的操作請求,而Standby只是作為slave,主要進行同步主Namenode的狀態,保證發生故障時能夠快速切換,並且資料一致。為了兩個 Namenode資料保持同步,兩個Namenode都與一組Journal Node進行通訊。當主Namenode進行任務的namespace操作時,都會同步修改日誌到Journal Node節點中。Standby Namenode持續監控這些edit,當監測到變化時,將這些修改同步到自己的namespace。當進行故障轉移時,Standby在成為Active Namenode之前,會確保自己已經讀取了Journal Node中的所有edit日誌,從而保持資料狀態與故障發生前一致。
為了確保故障轉移能夠快速完成,Standby Namenode需要維護最新的Block位置資訊,即每個Block副本存放在叢集中的哪些節點上。為了達到這一點,Datanode同時配置主備兩個Namenode,並同時傳送Block報告和心跳到兩臺Namenode。
任何時刻叢集中只有一個Namenode處於Active狀態,否則可能出現資料丟失或者資料損壞。當兩臺Namenode都認為自己的Active Namenode時,會同時嘗試寫入資料。為了防止這種腦裂現象,Journal Nodes只允許一個Namenode寫入資料,內部透過維護epoch數來控制,從而安全地進行故障轉移。
1.13.7、Hadoop切片機制
一個超大檔案在HDFS上儲存時,是以多個Block儲存在不同的節點上,比如一個512M的檔案,HDFS預設一個Block為128M,那麼512M的檔案分成4個Block儲存在叢集中4個節點上。
Hadoop在map階段處理上述512M的大檔案時分成幾個MapTask進行處理呢?Hadoop的MapTask並行度與資料切片有有關係,資料切片是對輸入的檔案在邏輯上進行分片,對檔案切成多少份,Hadoop就會分配多少個MapTask任務進行並行執行該檔案,原理如下圖所示。
Block與Splite區別:Block是HDFS物理上把資料分成一塊一塊;資料切片只是在邏輯上對輸入進行分片,並不會在磁碟上將其切分成片進行儲存。如下圖所示,一個512M的檔案在HDFS上儲存時,預設一個block為128M,那麼該檔案需要4個block進行物理儲存;若對該檔案進行切片,假設以100M大小進行切片,該檔案在邏輯上需要切成6片,則需要6個MapTask任務進行處理。
(1)、TextInputFormat切片機制
切片方式:TextInputFormat是預設的切片機制,按檔案規劃進行切分。比如切片預設為128M,如果一個檔案為200M,則會形成兩個切片,一個是128M,一個是72M,啟動兩個MapTask任務進行處理任務。但是如果一個檔案只有1M,也會單獨啟動一個MapTask執行此任務,如果是10個這樣的小檔案,就會啟動10個MapTask處理小檔案任務。
讀取方式:TextInputFormat是按行讀取檔案的每條記錄,key代表讀取的檔案行在該檔案中的起始位元組偏移量,key為LongWritable型別;value為讀取的行內容,不包括任何行終止符(換行符/回車符), value為Text型別,相當於java中的String型別。
(2)、CombineTextInputFormat切片機制
如果要處理的任務中含有很多小檔案,採用預設的TextInputFormat切片機制會啟動多個MapTask任務處理檔案,浪費資源。CombineTextInputFormat用於處理小檔案過多的場景,它可以將多個小檔案從邏輯上切分到一個切片中。CombineTextInputFormat在形成切片過程中分為虛擬儲存過程和切片過程兩個過程。
1)虛擬儲存過程
將輸入目錄下所有檔案大小,依次和設定的setMaxInputSplitSize值比較,如果不大於設定的最大值,邏輯上劃分一個塊。如果輸入檔案大於設定的最大值且大於兩倍,那麼以最大值切割一塊;當剩餘資料大小超過設定的最大值且不大於最大值2倍,此時將檔案均分成2個虛擬儲存塊(防止出現太小切片)。
例如setMaxInputSplitSize值為4M,輸入檔案大小為8.02M,則先邏輯上分成一個4M。剩餘的大小為4.02M,如果按照4M邏輯劃分,就會出現0.02M的小的虛擬儲存檔案,所以將剩餘的4.02M檔案切分成(2.01M和2.01M)兩個檔案。
2)切片過程
判斷虛擬儲存的檔案大小是否大於setMaxInputSplitSize值,大於等於則單獨形成一個切片;
如果不大於則跟下一個虛擬儲存檔案進行合併,共同形成一個切片。