前言
近几年,大数据,云计算,机器学习成为了非常热门的话题,这些技术运用在了很多的领域,也是在未来很有发展前景的技术。自己最近在接触一些大数据的东西,学习大数据的话自然很有必要先学习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