前言
近幾年,大數據,云計算,機器學習成為了非常熱門的話題,這些技術運用在了很多的領域,也是在未來很有發展前景的技術。自己最近在接觸一些大數據的東西,學習大數據的話自然很有必要先學習Hadoop和Spark。這里我們就來一探Hadoop的究竟吧。
Hadoop是什么
Hadoop是一個由Apache基金會所開發的分布式系統基礎架構。
用戶可以在不了解分布式底層細節的情況下,開發分布式程序。充分利用集群的威力進行高速運算和存儲。
Hadoop實現了一個分布式文件系統(Hadoop Distributed File System),簡稱HDFS。HDFS有高容錯性的特點,并且設計用來部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)來訪問應用程序的數據,適合那些有著超大數據集(large data set)的應用程序。HDFS放寬了(relax)POSIX的要求,可以以流的形式訪問(streaming access)文件系統中的數據。
核心設計:
-
HDFS
: HDFS為海量的數據提供了存儲 -
MapReduce
: MapReduce為海量的數據提供了計算
Hadoop的作者是Doug Cutting,他受到谷歌的三篇論文的啟發GFS(分布式存儲系統),MapReduce(分布式運行模型), BigTable(大表),然后用Java去實現了這三個功能,然后就有了Hadoop,不得不感嘆,牛人真的是牛人啊
Hadoop是專為離線和大規模數據分析而設計的,并不適合那種對幾個記錄隨機讀寫的在線事務處理模式,數據來源可以來自任何的形式,無論數據采用什么形式,最終都會轉換成key-value
的形式,key/value
是基本數據單元
簡單總結來說,Hadoop是一種分布式計算的解決方案
解決了什么問題
Hadoop就是解決了大數據(大到一臺計算機無法進行存儲,一臺計算機無法在要求的時間內進行處理)的可靠存儲和處理的問題。
也就是兩個核心的設計,HDFS和MapReduce
HDFS
設計特點
1、大數據文件,非常適合上T級別的大文件或者一堆大數據文件的存儲,現在互聯網上的數據量非常龐大,動不動上T的數據,所以非常適合Hadoop。
2、文件分塊存儲,HDFS會將一個完整的大文件平均分塊存儲到不同計算器上,它的意義在于讀取文件時可以同時從多個主機取不同區塊的文件,多主機讀取比單主機讀取效率要高得多。
3、流式數據訪問,一次寫入多次讀寫,這種模式跟傳統文件不同,它不支持動態改變文件內容,而是要求讓文件一次寫入就不做變化,要變化也只能在文件末添加內容。
4、廉價硬件,HDFS可以應用在普通PC機上,這種機制能夠讓給一些公司用幾十臺廉價的計算機就可以撐起一個大數據集群。
5、硬件故障,HDFS認為所有計算機都可能會出問題,為了防止某個主機失效讀取不到該主機的塊文件,它將同一個文件塊副本分配到其它某幾個主機上,如果其中一臺主機失效,可以迅速找另一塊副本取文件。
關鍵元素
- Block:將一個文件進行分塊,通常是128M。
- NameNode:保存整個文件系統的目錄信息、文件信息及分塊信息,這是由唯一一臺主機專門保存,當然這臺主機如果出錯,NameNode就失效了。在Hadoop2.*開始支持activity-standy模式----如果主NameNode失效,啟動備用主機運行NameNode。
- DataNode:分布在廉價的計算機上,用于存儲Block塊文件。
MapReduce
MapReduce是一套從海量源數據提取分析元素最后返回結果集的編程模型,將文件分布式存儲到硬盤是第一步,而從海量數據中提取分析我們需要的內容就是MapReduce做的事了。
舉個例子吧,假如說你想統計一個巨大的文本文件存儲在HDFS上,你想要知道這個文本里各個詞的出現頻率。我們把我們要運算的邏輯分發到各個節點上,在每個節點上進行運算和統計,假如在各個節點上對這些單詞進行統計,我們輸入的格式是一行一行的文本,而統計的結果像key-value的形式,比如在第一個節點上(hello, 30), (world, 22), (hadoop, 60),第二個節點上(hello, 20), (world, 32), (spark, 70),也就是說將任何形式的數據轉換成key-value的形式,這個過程就是Map。
然后我們要統計整個文本的單詞出現的次數,就要對這些節點上的數據進行匯總,將這些節點上的數據按照key分組,合并,也就是(a, num1),(a, num2), (b, num3),(b, num4(合并后就變成(a, num1 + num2), (b, num3 + num4),按照上面的結果合并就是
(hello, 50), (world, 54), (hadoop, 60), spark(70),這個過程就是Reduce
適用場景
hadoop擅長離線日志分析,facebook就用Hive來進行日志分析,2009年時facebook就有非編程人員的30%的人使用HiveQL進行數據分析;淘寶搜索中的自定義篩選也使用的Hive;利用Pig還可以做高級的數據處理,包括Twitter、LinkedIn上用于發現您可能認識的人,可以實現類似Amazon.com的協同過濾的推薦效果。淘寶的商品推薦也是!在Yahoo!的40%的Hadoop作業是用pig運行的,包括垃圾郵件的識別和過濾,還有用戶特征建模。(2012年8月25新更新,天貓的推薦系統是hive,少量嘗試mahout!)
不過從現在企業的使用趨勢來看,Pig慢慢有點從企業的視野中淡化了。
Hadoop偽分布式的安裝
好了,我們了解和學習了Hadoop的概念之后就來學習一下如何安裝Hadoop吧,這里我們先來學習偽分布式的安裝,也就是NameNode和DataNoe都在同一臺服務器上而且salve也是自己
環境準備
- 虛擬機Vmware
- Centos 6.9
- Hadoop 2.7.3
- JDK 1.8
配置網絡
IP
我們首先先安裝好Centos,然后配置好網絡,虛擬機與主機的連接方式選擇NAT,然后cmd命令輸入ipconfig
,記錄下VMware Network Adapter VMnet8 下的IP,在虛擬機中輸入
vim /etc/sysconfig/network-scripts/ifcfg-eth0
IP地址要和VMware Network Adapter VMnet8 下的IP在同一個網段,我的IP是192.168.109.1,貼一個自己的配置
DEVICE=eth0
TYPE=Ethernet
ONBOOT=yes
NM_CONTROLLED=yes
BOOTPROTO=static
IPADDR=192.168.109.3
NETMASK=255.255.255.0
GATEWAY=192.168.109.2
DNS1=192.168.109.2
然后使用命令service network restart
重啟網絡
修改主機名
vim /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=hadoop1
修改主機名和IP的映射關系
vim /etc/hosts
192.168.109.3 hadoop1
關閉防火墻
這里我們需要關閉我們的防火墻,開啟防火墻會有訪問限制,在虛擬機局域網內我們也不需要做訪問限制,索性就把防火墻關了
#查看防火墻狀態
service iptables status
#關閉防火墻
service iptables stop
#查看防火墻開機啟動狀態
chkconfig iptables --list
#關閉防火墻開機啟動
chkconfig iptables off
創建用戶
我們一般不直接使用root用戶,會創建一個新的用戶來完成我們的實驗,這里我們新建一個hadoop用戶
user add hadoop
接下來為hadoop用戶設置密碼
passwd hadoop
然后我們為hadoop用戶授予root權限
vim /etc/sudoer
找到root ALL=(ALL) ALL 并下面加入以下
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
hadoop ALL=(ALL) ALL
切換用戶
現在我們切換到hadoop用戶下進行操作
su hadoop
安裝軟件
我們在根目錄下創建一個app文件夾, mkdir app
,然后我們將需要弄的文件都解壓到app文件夾里面
用winscp上傳JDK,Hadoop的文件,解壓JDK, 執行命令
tar -zxvf jdk-8u131-linux-x64.tar.gz -C app
解壓Hadoop
tar -zxvf hadoop-2.7.3.tar.gz -C app
這個時候我們將jdk和hadoop都解壓到app目錄下,接下來我們就開始配置環境了
配置環境
配置JDK
vim /etc/profile
在文件最后添加如下:
export JAVA_HOME=/home/hadoop/app/jdk1.8.0_131
export PATH=$PATH:$JAVA_HOME/bin
刷新配置
source /etc/profile
配置Hadoop
注意:hadoop2.x的配置文件$HADOOP_HOME/etc/hadoop,這里我們的目錄就是在/home/hadoop/app/hadoop-2.7.3/etc/下
偽分布式需要修改5個配置文件
-
hadoop-env.sh
這個文件表示hadoop運行環境的文件,找到25行,改成export JAVA_HOME=/home/hadoop/app/jdk1.8.0_131
這個值原來是${JAVA_HOME},但是有點問題,老是獲取不到正確的值,所以這里我們就直接將它寫死 -
core-site.xml
配置如下:
<configuration>
<!-- 指定HADOOP所使用的文件系統schema(URI),HDFS的老大(NameNode)的地址 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://hadoop1:9000</value>
</property>
<!-- 指定hadoop運行時產生文件的存儲目錄 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/home/hadoop/app/hadoop-2.7.3/tmp</value>
</property>
</configuration>
-
hdfs-site.xml
配置如下:
<configuration>
<!-- 指定HDFS副本的數量 -->
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>
-
mapred-site.xml
這里先執行一個命令,重命名模板文件mv mapred-site.xml.template mapred-site.xml
然后再修改vim mapred-site.xml
配置如下:
<configuration>
<!-- 指定mr運行在yarn上 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>
-
yarn-site.xml
配置如下:
<configuration>
<!-- Site specific YARN configuration properties -->
<!-- 指定YARN的老大(ResourceManager)的地址 -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>hadoop1</value>
</property>
<!-- reducer獲取數據的方式 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
</configuration>
5個文件都修改完成后,我們需要將hadoop添加到環境中
vim /etc/proflie
加上HADOOP_HOME, 修改文件內容如下:
export JAVA_HOME=/home/hadoop/app/jdk1.8.0_131
export PATH=$PATH:$JAVA_HOME/bin
export HADOOP_HOME=/home/hadoop/app/hadoop-2.7.3
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
格式化hdfs
第一次啟動的時候我們需要格式化namenode,對namenode進行初始化,執行以下命令
hdfs namenode -format (hadoop namenode -format)
成功的話,會看到 “successfully formatted” 和 “Exitting with status 0″ 的提示,若為 “Exitting with status 1″ 則是出錯。
啟動hadoop
先啟動HDFS: start-dfs.sh
再啟動YARN: start-yarn.sh
期間會讓你多次輸入密碼,我們在后面配置SSH免密登錄之后就不用輸入密碼了
驗證是否啟動成功,使用jps命令驗證
27408 NameNode
28218 Jps
27643 SecondaryNameNode
28066 NodeManager
27803 ResourceManager
27512 DataNode
看到以上進程的時候,就說明我們啟動成功了
配置SSH免登錄
生成ssh免登陸密鑰,進入到我的home目錄
cd ~/.ssh
,執行
ssh-keygen -t rsa (四個回車)
執行完這個命令后,會生成兩個文件id_rsa(私鑰)、id_rsa.pub(公鑰)
將公鑰拷貝到要免登陸的機器上
ssh-copy-id localhost
然后我們執行ssh localhost 就可以不用輸入密碼登錄這臺機器了
進入Web管理界面
我們在瀏覽器里面輸入http://192.168.109.3:50070/
可以看到如下圖片
說明我們的hadooop已經開啟成功了
運行mapreduce程序
好了,我們的環境也搭建成功了,現在來試著跑一下mapreduce程序,進入hadoop的share目錄下
cd /home/hadoop/app/hadoop-2.7.3/share/hadoop/mapreduce
看到有以下文件:
hadoop-mapreduce-client-app-2.7.3.jar
hadoop-mapreduce-client-common-2.7.3.jar
hadoop-mapreduce-client-core-2.7.3.jar
hadoop-mapreduce-client-hs-2.7.3.jar
hadoop-mapreduce-client-hs-plugins-2.7.3.jar
hadoop-mapreduce-client-jobclient-2.7.3.jar
hadoop-mapreduce-client-jobclient-2.7.3-tests.jar
hadoop-mapreduce-client-shuffle-2.7.3.jar
hadoop-mapreduce-examples-2.7.3.jar
lib
lib-examples
sources
運算PI圓周率
這里我們用hadoop-mapreduce-examples-2.7.3.jar
的例子跑一下,執行的命令為
hadoop jar hadoop-mapreduce-examples-2.7.3.jar pi 5 10
pi是運算圓周率,后面的兩個參數代表map的任務數量和map的取樣數,取樣數越大,運算的結果越精確,這里我們取了5和10作為參數,結果如下
Number of Maps = 5
Samples per Map = 10
Wrote input for Map #0
Wrote input for Map #1
17/07/20 22:34:51 WARN hdfs.DFSClient: Caught exception
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.endBlock(DFSOutputStream.java:370)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:546)
Wrote input for Map #2
17/07/20 22:34:51 WARN hdfs.DFSClient: Caught exception
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.endBlock(DFSOutputStream.java:370)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:546)
Wrote input for Map #3
17/07/20 22:34:51 WARN hdfs.DFSClient: Caught exception
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.endBlock(DFSOutputStream.java:370)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:546)
Wrote input for Map #4
Starting Job
17/07/20 22:34:51 INFO client.RMProxy: Connecting to ResourceManager at hadoop1/192.168.109.3:8032
17/07/20 22:34:52 WARN hdfs.DFSClient: Caught exception
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeInternal(DFSOutputStream.java:577)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:573)
17/07/20 22:34:52 INFO input.FileInputFormat: Total input paths to process : 5
17/07/20 22:34:52 WARN hdfs.DFSClient: Caught exception
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.endBlock(DFSOutputStream.java:370)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:546)
17/07/20 22:34:52 WARN hdfs.DFSClient: Caught exception
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.endBlock(DFSOutputStream.java:370)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:546)
17/07/20 22:34:52 INFO mapreduce.JobSubmitter: number of splits:5
17/07/20 22:34:52 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1500560388081_0003
17/07/20 22:34:53 INFO impl.YarnClientImpl: Submitted application application_1500560388081_0003
17/07/20 22:34:53 INFO mapreduce.Job: The url to track the job: http://hadoop1:8088/proxy/application_1500560388081_0003/
17/07/20 22:34:53 INFO mapreduce.Job: Running job: job_1500560388081_0003
17/07/20 22:35:08 INFO mapreduce.Job: Job job_1500560388081_0003 running in uber mode : false
17/07/20 22:35:08 INFO mapreduce.Job: map 0% reduce 0%
17/07/20 22:36:14 INFO mapreduce.Job: map 100% reduce 0%
17/07/20 22:36:28 INFO mapreduce.Job: map 100% reduce 100%
17/07/20 22:36:29 INFO mapreduce.Job: Job job_1500560388081_0003 completed successfully
17/07/20 22:36:29 INFO mapreduce.Job: Counters: 49
File System Counters
FILE: Number of bytes read=116
FILE: Number of bytes written=714243
FILE: Number of read operations=0
FILE: Number of large read operations=0
FILE: Number of write operations=0
HDFS: Number of bytes read=1320
HDFS: Number of bytes written=215
HDFS: Number of read operations=23
HDFS: Number of large read operations=0
HDFS: Number of write operations=3
Job Counters
Launched map tasks=5
Launched reduce tasks=1
Data-local map tasks=5
Total time spent by all maps in occupied slots (ms)=325021
Total time spent by all reduces in occupied slots (ms)=8256
Total time spent by all map tasks (ms)=325021
Total time spent by all reduce tasks (ms)=8256
Total vcore-milliseconds taken by all map tasks=325021
Total vcore-milliseconds taken by all reduce tasks=8256
Total megabyte-milliseconds taken by all map tasks=332821504
Total megabyte-milliseconds taken by all reduce tasks=8454144
Map-Reduce Framework
Map input records=5
Map output records=10
Map output bytes=90
Map output materialized bytes=140
Input split bytes=730
Combine input records=0
Combine output records=0
Reduce input groups=2
Reduce shuffle bytes=140
Reduce input records=10
Reduce output records=0
Spilled Records=20
Shuffled Maps =5
Failed Shuffles=0
Merged Map outputs=5
GC time elapsed (ms)=8989
CPU time spent (ms)=9260
Physical memory (bytes) snapshot=458428416
Virtual memory (bytes) snapshot=12371886080
Total committed heap usage (bytes)=624766976
Shuffle Errors
BAD_ID=0
CONNECTION=0
IO_ERROR=0
WRONG_LENGTH=0
WRONG_MAP=0
WRONG_REDUCE=0
File Input Format Counters
Bytes Read=590
File Output Format Counters
Bytes Written=97
Job Finished in 98.692 seconds
Estimated value of Pi is 3.28000000000000000000
我們看最后一行,得出結果為3.28,是我們的樣本數量太少了,要是樣本數量大一點,結果應該更接近3.14
還發現個問題,運行中出現了多次警告
17/07/20 22:34:52 WARN hdfs.DFSClient: Caught exception
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.endBlock(DFSOutputStream.java:370)
at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:546)
我google了一下,再hadoop issue里面找到了答案https://issues.apache.org/jira/browse/HDFS-10429,發現是bug,忽略就好了,換個hadoop版本也許就沒事了,或者修改日志的輸出級別
計算單詞數量wordcount
首先我們先新建一個words.txt文件,內容如下
hello world
hello tom
hello kevin
hello jerry
hello baby
tom and jerry
然后在hdfs里創建一個目錄
hadoop fs -mkdir -p /wordcount/input
把文件上傳到該目錄下
hadoop fs -put words.txt /wordcount/input
查看文件是否上傳上去了
hadoop fs -ls /wordcount/input
我們可以看到我們的文件已經成功上傳上去了
我們發現操作hdfs的命令和操作linux的命令大致都是一樣的,大家可以自行去看官方的文檔
回到剛才的share目錄下,繼續執行剛才的那個示例文件
hadoop jar hadoop-mapreduce-examples-2.7.3.jar wordcount /wordcount/input /wordcount/output
這里執行的方法是wordcount,第一個參數是輸入的文件位置,第二個參數是輸出的結果的文件位置
執行結束后,我們來看一下輸出目錄hadoop fs -ls /wordcount/output
,發下目錄下生成了兩個文件
Found 2 items
-rw-r--r-- 1 hadoop supergroup 0 2017-07-20 23:07 /wordcount/output/_SUCCESS
-rw-r--r-- 1 hadoop supergroup 51 2017-07-20 23:07 /wordcount/output/part-r-00000
查看一下part-r-00000這個文件
hadoop fs -cat /wordcount/output/part-r-00000
結果如下:
and 1
baby 1
hello 5
jerry 2
kevin 1
tom 2
world 1
可以看到,結果是正確的,大功告成
總結
接觸一個新的技術,安裝和配置環境的都是一件比較麻煩的事,可能你第一天就要花費很多時間在搭建環境上面了,可能期間你會遇到各種問題,不過也是鍛煉耐心的一個過程,有一個不錯的方法可以解決,那就是使用docker容器技術,使用別人搭建好的環境鏡像,直接拿來用就可以,這樣我們就可以不必花費太多時間在環境問題上,專心學我們的技術,有興趣的同學可以自行了解下。還有這次的演示例子也只是拿官方的例子來做演示,后面需要自己寫程序實現map-reduce,官網上也有很多的例子,所以我覺得看官方其實是最快了解一門技術的方法了,而且一些比較著名的開源項目的文檔都是寫的比較好的,基本上你看,然后照著demo敲一遍就可以上手了,而且那些資料還是最新的。還有這里也只是演示了偽分布的安裝,其實hadoop有三種安裝模式:
1.獨立式:Hadoop運行所有的東西在無后臺的單獨的JVM中,這種模式適合在開發階段測試與Debug MapReduce程序。
2.偽分布式:Hadoop做為后臺應用運行在本地機器,模擬小集群。
3.全分布式:Hadoop做為后臺應用運行真實的集群電腦中。
剩下的就留給讀者自己探索吧!
個人博客: http://blog.zgj12138.cn
CSDN: http://blog.csdn.net/zgj12138