Hadoop
0.Hadoop相关简介
1.Hadoop集群的搭建
0.环境:
0.服务器环境
bt0 1.0.0.130 伪分布式
bt1 1.0.0.131
bt2 1.0.0.132 => 全分布式
bt3 1.0.0.133
1.jdk
JDK:jdk-1.8_241.tar.gz
2.hadoop
Hadoop:apache-hadoop-2.10.0.tar.gz
3.目录规划:
当前用户:kevin
0.软件的安装目录:/home/用户名/bigdata/softwares
1.软件产生的数据的存放目录:/home/用户名/bigdata/data
mkdir -p ~/bigdata/softwares ~/bigdata/data
1.安装JDK:
0.将jdk程序拷贝至Ubuntu操作系统中;
1.解压:
tar -zxvf jdk-8u241-linux-x64.tar.gz -C ~/bigdata/softwares
2.配置环境变量【用户级别】:
vi ~/.bashrc
export JAVA_HOME=/home/kevin/bigdata/softwares/jdk1.8.0_241
export JRE_HOME=$JAVA_HOME/jre
export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
source ~/.bashrc
3.测试:
java -version
javac -version
java
javac
2.安装Hadoop:
0.将hadoop程序拷贝至Ubuntu中;
1.解压:
tar zxvf hadoop-2.10.0.tar.gz -C /home/kevin/bigdata/softwares
2.配置环境变量:
####__HADOOP_CONF__####
export HADOOP_HOME=/home/kevin/bigdata/softwares/hadoop-2.10.0
export HADOOP_USER_NAME=kevin
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
source ~/.bashrc
3.测试:
hadoop version
4.说明:
1.原则上需要创建不同的用户来管理不同的hadoop集群;
3.集群的搭建:
0.单机模式:了解
1.伪分布式模式:
0.编辑/etc/hosts文件,做主机名到IP地址的映射;
sudo vi /etc/hosts
127.0.0.1 localhost
1.0.0.130 bt0
1.配置HDFS集群:
0.编辑$HADOOP_HOME/etc/hadoop/hadoop-env.sh
第25行改为:export JAVA_HOME=JDK的绝对路径
1.编辑$HADOOP_HOME/etc/hadoop/core-site.xml【common】
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://bt0:9000</value>
</property>
<property>
<name>hadoop.proxyuser.kevin.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.kevin.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.http.staticuser.user</name>
<value>kevin</value>
</property>
</configuration>
2.编辑$HADOOP_HOME/etc/hadoop/hdfs-site.xml【hdfs】
<configuration>
<property>
<name>dfs.nameservices</name>
<value>bt0-hdfs-cluster</value>
</property>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>64M</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///home/kevin/bigdata/data/nn</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///home/kevin/bigdata/data/dn</value>
</property>
<property>
<name>dfs.namenode.checkpoint.dir</name>
<value>file:///home/kevin/bigdata/data/snn</value>
</property>
<property>
<name>dfs.namenode.checkpoint.edits.dir</name>
<value>file:///home/kevin/bigdata/data/snn</value>
</property>
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
<property>
<name>dfs.web.ugi</name>
<value>kevin,kevin</value>
</property>
<property>
<name>fs.permissions.umask-mode</name>
<value>000</value>
</property>
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
<property>
<name>dfs.permissions.superusergroup</name>
<value>kevin</value>
</property>
<property>
<name>dfs.namenode.safemode.threshold-pct</name>
<value>0f</value>
</property>
<property>
<name>dfs.namenode.name.dir.restore</name>
<value>true</value>
</property>
<property>
<name>dfs.cluster.administrators</name>
<value>*</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>bt0:9001</value>
</property>
</configuration>
3.启动HDFS集群:
0.格式化HDFS集群,该操作只进行一次,并且只在主节点上执行:
hdfs namenode -format
1.启动、停止集群:
0.常规方式:
在主节点上启动主节点进程【服务】:hadoop-daemon.sh start/stop namenode
在从节点上启动从节点进程【服务】:hadoop-daemon.sh start/stop datanode
1.快捷方式【一键启动主节点和所有的从节点】:
前提:
0.做免密登录;
1.在$HADOOP_HOME/etc/hadoop/slaves文件中添加从节点的主机名或者IP;如果是全分布式集群,则该文件中只需要添加所有从节点的主机名或者IP,主节点的不用添加;***********主4从2
启动:start-dfs.sh
停止:stop-dfs.sh
说明:
0.一键启动方式只能在主节点上执行;
1.启动所有的进程的方式都是通过免密登录之后进行启动;
2.检测是否启动成功:
0.通过jps命令检测;
1.通过web浏览器页面检测;
http://1.0.0.130:50070
http://bt0:50070
说明:
1、访问第二个链接的前提是需要在windows中的hosts文件做主机名到IP地址的映射;
2.该文件在 C:\Windows\System32\drivers\etc\hosts
3.先在该文件的属性中将“只读”取消,或者赋予管理员权限;
4.在该文件中加入:
1.0.0.130 bt0
4.问题及其说明:
0.NameNode和DataNode进程都不存在,或者只存在其中某一个;
1.集群在格式化、启动、运行的过程中如果出错,则需要查看日志来排查错误,日文件在$HADOOP_HOME/logs目录中;
2.多次格式化导致的数据状态不一致的问题;可以多次格式化,但是在每次做格式化之前,需要将数据目录删除,即在格式化之前执行:sudo rm -r ~/bigdata/data
3.xml编写错误;hosts文件中的IP到主机名映射错误;
2.配置YARN集群:
0.编辑配置$HADOOP_HOME/etc/hadoop/mapred-site.xml文件:
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobhistory.address</name>
<value>bt0:10020</value>
</property>
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>bt0:19888</value>
</property>
<property>
<name>mapreduce.job.ubertask.enable</name>
<value>true</value>
</property>
</configuration>
1.编辑配置$HADOOP_HOME/etc/hadoop/yarn-site.xml文件:
<configuration>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>bt0</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>bt0:18040</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>bt0:18030</value>
</property>
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value>bt0:18025</value>
</property>
<property>
<name>yarn.resourcemanager.admin.address</name>
<value>bt0:18141</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.auxservices.mapreduce.shuffle.class</name>
<value>org.apache.hadoop.mapred.ShuffleHandler</value>
</property>
<property>
<name>yarn.nodemanager.local-dirs</name>
<value>file:///home/kevin/bigdata/data/nm</value>
</property>
<property>
<name>yarn.log.server.url</name>
<value>htpp://bt0:19888/jobhistory/logs</value>
</property>
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<property>
<name>yarn.web-proxy.address</name>
<value>bt0:20000</value>
</property>
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>-1</value>
</property>
<property>
<name>yarn.nodemanager.remote-app-log-dir</name>
<value>/logs</value>
</property>
</configuration>
2.启动或者停止YARN集群:
0.常规方式:
0.在启动YARN集群之前必须要先启动HDFS集群;
1.在主节点上启动、停止主节点服务:
yarn-daemon.sh start/stop resourcemanager
2.在主节点上启动、停止作业执行历史服务:
mr-jobhistory-daemon.sh start/stop historyserver
3.在主节点上启动、停止代理服务:
yarn-daemon.sh start proxyserver
4.在从节点上启动、停止从节点服务:
yarn-daemon.sh start/stop nodemanager
1.一键启动:
启动:start-yarn.sh
停止:stop-yarn.sh
2.测试是否启动成功:
0.查看有没有ResouceManager、NodeManager、WebAppProxyServer、JobHistoryServer进程,如果有,则表示成功,如果都没有或者只有一部分,则为启动出错,需要查看日志解决;
1.在浏览器中通过访问YARN的WEB页面:
http://1.0.0.130:8088
http://bt0:8088
3.集群管理:
0.一键启动/停止所有集群:
启动:start-all.sh #start-yarn.sh里最后一行放开webproxy sbin/中
停止:stop-all.sh
2.全分布式模式:
0.环境:
bt1 1.0.0.131 主节点:NameNode、ResourceManager、WebAppProxyServer、JobHistoyServer
bt2 1.0.0.132 从节点:DataNode、NodeManager
bt3 1.0.0.133 从节点:DataNode、NodeManager
1.分别给三个虚拟机安装JDK和Hadoop程序:
0.解压:
1.配置环境变量:
2.集群配置:
说明:
0.全分布式集群的配置和伪分布式集群的配置完全一致【唯一不一致的就slaves(3.03为workers)文件】(里面填从节点的主机名或者ip),并且在全分布式集群的每个节点上的配置也都一模一样; ***主4从2
1.需要做bt1到bt1、bt2、bt3【主节点到主节点,主节点到左右从节点】的免密登录;
2.HDFS,Hadoop Distributed File System,Hadoop分布式文件系统
0.HDFS中的相关概念:
0.DataBlock,数据块,也叫数据分块:
0.HDFS默认的数据块大小是128M;
1.HDFS中备份数据块的副本系数默认是3;
2.通过命令“hdfs fsck 文件路径 -files -blocks -locations”可以查看文件属性
1.NameNode,名字节点,主节点:
0.HDFS集群中的主节点,一个HDFS集群中可以有多个NN,但是只能有一个NN对外提供服务,另外的NN做备用主节点;【高可用集群,Zookeeper】
1.NN的主要作用是维护着集群中文件或者目录的状态以及协调客户端的访问;
2.FSImage和Edits文件:
0.FSImage文件存储当前的整个集群中的文件或者目录的状态【文件系统树】;
1.Edits文件中保存的某一次针对于集群中文件的操作;
2.DataNode,数据节点,从节点:
0.HDFS集群中的从节点,DN可以有任意个;
1.DN的主要作用:
0.存储和查询数据块;
1.接受客户端和NN的调度;
2.周期性的向NN发送自身所存储的数据块列表以报告当前DN的状态;
1.操作HDFS集群的客户端命令:
0.命令语法:
hdfs dfs 命令
hadoop dfs 命令
1.相关命令:
hdfs dfs -mkdir -p 目录路径名称
例1:创建家目录:hdfs dfs -mkdir -p /user/zzh
例2:在根目录下创建data目录,用于存放数据:hdfs dfs -mkdir -p /data
hdfs dfs -put ~/123note.txt ./
hdfs dfs -ls 目录/文件路径名
说明:
0.如果跟的是目录,则是查看指定目录下的所有文件的信息【元数据】;
1.如果后面跟的是文件,则是查看指定文件的元数据;
2.如果后面什么都不跟或者跟的是“.”,表示查看“家目录”下的所有文件的元数据;
例1:查看跟目录下的文件信息:hdfs dfs -ls /
例2:查看家目录下的文件信息:
hdfs dfs -ls /user/kevin
hdfs dfs -ls
hdfs dfs -ls .
hdfs dfs -put 本地文件路径 集群文件路径
说明:将本地文件上传至集群中;
例1:将本地文件“/etc/passwd”上传至集群的家目录中:
hdfs dfs -put /etc/passwd /user/kevin
hdfs dfs -put /etc/passwd
hdfs dfs -put /etc/passwd .
例2:将weather.tgz【tgz是tar.gz的简写】、patent.tgz、test.tgz上传至Linux【Ubuntu】中,然后解压,然后将解压之后的目录上传至集群的“/data”目录中;
0.上传;
1.解压:
tar zxvf weather.tgz
tar zxvf test.tgz
tar zxvf patent.tgz
2.解压之后删除tgz文件:
rm -r ~/*.tgz
3.上传刚才解压之后的三个目录【weather、test、patent】到集群的“/data”目录中:
hdfs dfs -put weather test patent /data
注意:磁盘空间不够,在vmware中扩展硬盘大小:
0.关闭虚拟系统【Ubuntu】;
1.点“编辑虚拟机设置”;
2.选中要进行扩展的硬盘,点击右侧的“扩展”按钮;
3.输入想要扩展的大小,100G,然后点击扩展,等待扩展完成;
4.此时只是完成了硬盘容量的扩展,然后Ubuntu系统中的分区的大小还是没有被扩展,所以需要手动创建分区,然后将其挂载至某个目录【~/bigdata】中;
0.创建分区:
sudo fdisk /dev/sda
sudo reboot
1.建立文件系统:
sudo mkfs -t ext4 /dev/sda3
2.永久挂载:
0.删除/home/kevin/bigdata/data目录中的所有内容:
rm -r /home/kevin/bigdata/data
mkdir -p /home/kevin/bigdata/data
1.挂载:
sudo vi /etc/fstab
新增:
/dev/sda3 /home/kevin/bigdata/data ext4 defaults 0 0
2.执行挂载:
sudo mount -a
3.由于在建立分区的时候使用的root用户,所以挂载之后的data目录也只能用root用户操作,将data目录的所有者和所属组修改为当前用户:
sudo chown -R kevin:kevin /home/kevin/bigdata/data
4.重新格式化HDFS集群:
hdfs namenode -format
5.如果是在解压的时候显示容量不足,将家目录下的weather、patent、test目录删除,压缩包不要删除,然后再解压压缩包的时候,使用“-C”选项重新指定解压目录为“~/bigdata/data”:
tar zxvf weather.tgz -C ~/bigdata/data
tar zxvf test.tgz -C ~/bigdata/data
tar zxvf patent.tgz -C ~/bigdata/data
6.启动HDFS集群,然后重新上传那三个文件;
hdfs dfs -put ~/bigdata/data/weather /data
hdfs dfs -put ~/bigdata/data/test /data
hdfs dfs -put ~/bigdata/data/patent /data
hdfs dfs -rm -r 目录/文件路径
例1:删除“/data”目录下的所有文件:
hdfs dfs -rm -r /data/*
hdfs dfs -get 集群文件路径 本地文件路径
说明:将集群中的文件下载至本地
hdfs dfs -get ./passwd1.txt ~/passwd2.txt
hdfs dfs -cp 原文件路径 目标文件路径
说明:
0.该命令只能用户将集群上的文件拷贝至集群上的另一个位置;
1.该命令可以跨集群拷贝;
例1:hdfs dfs -cp ./passwd1.txt ./passwd3.txt
例2:hdfs dfs -cp hdfs://bt0:9000/user/kevin/passwd1.txt hdfs://bt1:9000/user/kevin/passwd4.txt
例3:hdfs dfs -cp hdfs://bt0:9000/user/kevin/passwd1.txt hdfs://bt0:9000/user/kevin/passwd4.txt
hdfs dfs -mv 原文件路径 目标文件路径
说明:在集群上移动文件;
hdfs dfs -cat 常规文件
hdfs dfs -text 常规文件
说明:查看文件内容
例1:hdfs dfs -cat ./passwd1.txt
例2:hdfs dfs -text ./passwd1.txt
2.HDFS中命令或者路径的说明:
0.HDFS的命令和Linux文件系统中的命令基本相同;
1.HDFS中有“家目录”的概念,用“.”表示,HDFS中家目录的路径是“/user/用户名”;
2.HDFS中没有“当前目录”的概念,也没有“~”符号;
2.基于JavaAPI的HDFS编程:
0.说明:
0.如果要在windows中进行编程并且在windows中执行HDFS客户端的程序,则需要在windows中安装hadoop并且配置环境变量:
HADOOP_HOME
HADOOP_USER_NAME
PATH
1.将文件hadoop.dll和winutils.exe放在Hadoop安装目录的bin目录中;【MacOS忽略此步】
2.在windows中的hosts文件中配置主机名到IP地址的映射关系;
1.编程需要用到的类:
org.apache.hadoop.fs.FileSystem:对HDFS中文件系统的抽象描述;
org.apache.hadoop.fs.Path:对HDFS中文件的路径进行封装;
org.apache.hadoop.conf.Configuration:对Hadoop中的所有配置信息的封装;
org.apache.hadoop.fs.FSDataOutputStream:用于将数据写入至HDFS集群文件中的输出字节流;
org.apache.hadoop.fs.FSDataInputStream:用于从HDFS集群文件中读取数据的输入字节流;
练习1:尝试使用JavaAPI编程实现cp、mv、rm、ls等命令;
练习2:计算专利数据:
0.计算每个被引用的专利被引用的次数;
1.计算每个专利都引用了哪些专利;
3.HDFS的读写原理【重点】:
0.读:
FSDataInputStream在将所携带的客户端要读取的文件的分块信息返回给客户端之后,客户端通过这些信息就从相应的DN上读取数据,在读取数据时候,会由FSDataInputStream产生一个新的流对象DFSInputStream去和DN交互读取数据;
1.写
FSDataOutoutStream携带客户端要写入集群的数据被分割的数据块信息;该流产生新的流对象DFSOutputStream用于将客户端要写入的数据按照分块信息封装成数据包发送至相应的DN,在DN上产生DataStreamer流对象接受该数据包进行持久化保存;
3.Hadoop IO,Hadoop中的序列化:
0.序列化和反序列化:
序列化:将对象或者数据转化成字节流,方便与网络传输或者持久化保存;
反序列化:将字节流转化成对象;
1.数据一致性校验
2.压缩和解压缩、编解码器
压缩:实际上是对文件的输出流进行压缩;
解压缩:对文件的输入流进行解压缩;
3.序列化和反序列化
0.Java中的基本数据类型:
boolean char float double
byte short int long
1 2 4 8
1.Hadoop中的数据类型:
0.Hadoop中的数据类型都是根据Java中的基本数据类型封装之后的引用类型;
1.Hadoop中没有char类型和String类型,在Hadoop中用Text类型来表示Java中的char类型和String类型;
2.Hadoop中除了上述两种数据类型之外,其他的数据类型都是Java中对应的数据类型名加上Writable所产生的引用数据类型;这些数据类型都实现了Writable接口;
2.自定义Hadoop中的数据类型:
0.如果该数据类型需要作为MR程序的Key,则需要实现Writable接口和Comparable接口,或者只需要实现WritableComparable接口;
1.如果该数据类型只需要作为MR程序中的Value,则只需要实现Writable接口即可;
2.如果该数据类型实现了Comparable接口,一般情况下需要重写equals和hashCode方法;在重写这两个方法时,有哪些属性参与了compareTo方法,则也让这些属性参与equals方法和hashCode方法的重写;
3.反序列化和序列化的顺序要一致;
4.对于属性的赋值要使用值赋值或者值复制的形式,不能使用引用赋值;
5.思考:在自定义的数据类型中出现了Java中的基本数据类型或者集合数据类型该如何序列化和反序列化?
1.对于基本数据类型,在序列化的时候先转化成Hadoop中的数据类型,然后进行序列化,在反序列化的时候,先通过Hadoop中的数据类型从输入流中读取到数据,然后再获取到对应的基本数据类型的值进行赋值;
2.对于集合类型,先序列化集合的大小,再序列化集合中每个元素;先反序列化集合的大小,再反序列化集合中的每个元素;
== 和 equals 在比较引用类型的数据时的区别?
1.在没有重写equals方法时候,这俩没区别,都是用于比较对象的地址是否相等;
2.如果重写了equals方法,则按照重写规则进行比较两个对象是否相等;
4.SequenceFile,序列文件
练习1:编写程序,在本地生成1000个小文件;
练习2:将上述的1000个小文件存储到一个序列文件中;
思路:使用每个小文件的绝对路径作为Key,使用文件内容作为Value;
Hadoop中的MapReduce:
并发:多线程;
并行:多进程;
(k1,v1)-->Map阶段-->(k2,v2)-->Shuffle-->(k2,[v2])-->Reduce阶段-->(k3,v3)
数据分片:把一个Map任务所处理的所有的数据称之为一个数据分片;近似的认为一个数据分块就是一个数据分片;所以Map任务的个数和数据块的个数一致;
Shuffle阶段:分区、分组、排序;
MR例1:计算专利统计和专利引用;
Mapper:Map阶段的任务必须继承自该类,并且重写其中的map方法,在Map阶段所要执行的任务在map方法中编写即可;
Reducer:Reduce阶段的任务必须继承自该类,并且重写其中的reduce方法,在Reduce阶段所要执行的任务在reduce方法中编写;
Hadoop V1版本的API在org.apache.hadoop.mapred包中;
Hadoop V2版本的API在org.apache.hadoop.mapreduce包中;
(k1,v1) k2 v2 k2 [v2] k3 v3
3858241,956203 956203 1 956203 [1,1] 956203 2
3858241,1324234 1324234 1 1324234 [1] 1324234 1
3858241,3398406 3398406 1 3398406 [1,1] 3398406 2
3858241,3557384 3557384 1 3557384 [1] 3557384 1
3858241,3634889 --Map--> 3634889 1 --Shuffle--> 3634889 [1,1,1] --Reduce--> 3634889 3
3858241,956203 956203 1
3858241,3398406 3398406 1
3858241,3634889 3634889 1
3858241,3634889 3634889 1
(k1,v1) k2 v2 k2 [v2]
3858241,956203 3858241 956203 3858241 [956203,1324234,3398406,3557384,3634889]
3858241,1324234 3858241 1324234 3858242 [1515701,3319261,3668705,3707004]
3858241,3398406 3858241 3398406 3858243 [2949611]
3858241,3557384 3858241 3557384 |
3858241,3634889 3858241 3634889 Reduce阶段
3858242,1515701 --map--> 3858242 1515701 -Shuffle-> k3 | v3
3858242,3319261 3858242 3319261 3858241 956203:1324234:3398406:3557384:3634889
3858242,3668705 3858242 3668705 3858242 1515701:3319261:3668705:3707004
3858242,3707004 3858242 3707004 3858243 2949611
3858243,2949611 3858243 2949611
练习1:词频统计,根据HDFS集群中存储的数据,计算/data/test/wordcount目录中所有文件中的每个单词所出现的次数;
练习2:计算成绩数据/data/test/score目录中,得到以下结果:
姓名 语文:成绩,数学:成绩,英语:成绩,总成绩:成绩,平均成绩:成绩
张三 语文:80,数学:70,英语:80,总成绩:230,平均成绩:76.6
原始数据--map-->姓名,学科标识+成绩--Reduce-->结果
原始数据--map-->姓名,原始数据--Reduce-->结果
练习3:提取天气数据中的三列:
存储成两份:一份格式为文本文件,一份格式为序列文件;
年份 气象站编号 温度
1990 003099999999999 +0110
1990 004099999999999 +0210
练习4:编程计算练习题中的1,3,5
MapReduce程序的优化——Combiner的使用
0.使用combiner的目的是减少数据从map的输出到reduce的输入的传输量;
1.在MR程序中是否使用Combiner不能影响正常的Map到Reduce的数据传输,即Combiner输入的数据类型和输出的数据类型一定是一致的;
2.Combiner的本质就是Reducer,意味着在MR程序中加入Combiner就会在Map端执行一次Reduce操作;
3.如果MR程序中的Reducer的输入和输出的数据类型一致,并且Reducer的逻辑和Combiner的逻辑一致,则该Reducer可以直接作为Combiner来使用;否则需要自定义Combiner;
Map<IntWritable,Text,Text,DoubleWritable>
Combiner<Text,DoubleWritable,Text,DoubleWritable>
Reduce<Text,DoubleWritable,Text,DoubleWritable>
需求:计算1992年每个气象站的最高温度;
需求:计算1992年每个气象站的平均温度;
思考:是否可以使用Combiner?
如果可以使用Combiner,是否可以将Reducer直接作为Combiner来使用?
92年的原始数据:
k1,v1 k2 v2 k2 [v2]
1,10 1 10
1,20 1 20 1 [10,20,21]
1,21 map 1 21 shuffle 2 [20,10]
2,20 2 20 3 [20]
2,10 2 10
3,20 3 20
1 [10,20,21,30,31] 1,31
1,30 1 30 2 [20,10,5,40,50] 2,50
1,31 1 31 1 [30,31] 3 [20,100,40] reduce 3,100
2,5 map 2 5 shuffle 2 [5,40,50] 4 [50] 4,50
2,40 2 40 5 [20] 5,20
2,50 2 50
3,100 3 100 3 [100,40]
3,40 3 40 shuffle 4 [50]
4,50 map 4 50 5 [20]
5,20 5 20
---------------------------------
k1,v1 k2 v2
1,10 1 10
1,20 1 20 1 [21]
1,21 map 1 21 combiner 2 [20]
2,20 2 20 3 [20]
2,10 2 10
3,20 3 20
1 [21,31] 1,31
1,30 1 30 2 [20,50] 2,50
1,31 1 31 shuffle 3 [20,100] reduce 3,100
2,5 map 2 5 combiner 1 [31] 4 [50] 4,50
2,40 2 40 2 [50] 5 [20] 5,20
2,50 2 50
3,100 3 100 3 [100]
3,40 3 40 combiner 4 [50]
4,50 map 4 50 5 [20]
5,20 5 20
=============================================
92年的原始数据:
k1,v1 k2 v2
1,10 1 <10,1> 1 <17,3>
1,20 1 <20,1>
1,21 map 1 <21,1> Combiner 2 <15,2>
2,20 2 <20,1>
2,10 2 <10,1> 3 <20,1>
3,20 3 <20,1> 1 [<17,3>,<30.5,2>] avg1=(17*3+30.5*2)/(3+2)
1,30 1 <30,1> Shuffle 2 [<15,2>,<32,3>] reduce avg2=(15*2+32*3)/(2+3)
1,31 1 <31,1>
2,6 map 2 <6,1> Combiner 1 <30.5,2> 3 [<20,1>,<70,2>] avg3=(20*1+70*2)/(1+2)
2,40 2 <40,1> 2 <32,3>
2,50 2 <50,1>
3,100 3 <100,1>
3,40 3 <40 ,1> 3 <70,2>
4,50 map 4 <50 ,1> Combiner 4 <50,1>
5,20 5 <20 ,1> 5 <20,1>
============================================
在TextInputFormat读取数据的时候,执行Map任务的个数是有数据块的个数决定的;不论使用哪个格式控制读取数据,执行Reduce任务的个数默认是1,可以进行配置;
0.在mapred-site.xml文件中进行配置:
<property>
<name>mapreduce.job.reduces</name>
<value>5</value>
</property>
1.在MR程序代码中进行配置:
job.setNumReduceTasks(10);
2.在执行MR程序的命令行中通过参数配置:
-Dmapreduce.job.reduces=3
MR程序中的Shuffle阶段:
分区【Map之后】:决定从map阶段出来的数据(k2,v2)应该进入哪个Reduce【如果Reduce的个数大于1个的情况】,算法是:默认使用k2的哈希值和Reduce的个数取余,余数就是该数据要进入的Reduce的编号;
(1,x)
(1,y) 0 (3,a),(3,x)
(2,z)
(3,a)
(3,x) 1 (1,x),(1,y),(1,a),(4,x)
(5,y)
(1,a)
(2,b) 2 (2,z),(2,b),(5,y)
(4,x)
分组【Reduce之前】:将进入同一个Reduce的所有k2相同的v2分到一起,形成v2s;
(1,x)
(1,y) 0:(3,a),(3,x) --分组--> (3,[a,x])
(2,z)
(3,a)
(3,x) 1:(1,x),(1,y),(1,a),(4,x) --分组--> (1,[x,y,a]),(4,[x])
(5,y)
(1,a)
(2,b) 2:(2,z),(2,b),(5,y) --分组--> (2,[z,b]),(5,y)
(4,x)
排序【Reduce之前】:将进入同一个Reduce的多组数据按照每一组数据中的k2的哈希值进行排序,决定哪一组【哈希值小的】数据被Reduce先处理;
练习1:计算dupdata数据,数据去重;去掉重复的好友关系,a,b表示a和b互为好友关系,则b,a为重复数据;
练习2:计算cooccurence数据,计算好友关系共同出现的次数;
练习3:计算index数据,倒排索引/倒置索引;计算每个单词在每个文件中出现的次数;
MapReduce file_1:1,file_2:1,file_3:1,file_4:2
专利引用 自定义Mapper 1\t2 3 4 5 6 2 3 4
TokenCounterMapper 2 1
2 1
3 1
3 1
4 1
4 1
5 1
6 1
IntSumReducer 2 2
3 2
4 2
5 1
6 1
InverseMapper 2 2
2 3
2 4
...
MR中的排序:
0.局部排序:当一个MR程序中的Reduce个数有多个的时候,每个Reduce处理的结果文件的数据在该文件内部按照k3进行了排序;但是在所有的文件中,k3无序;
1.全局排序:不管有多少个Reduce工作,每个Reduce所产生的结果文件不只是局部有序,而且全局也有序;
0.桶排序:
1.控制分区阶段;不能再使用哈希值分区方式,Hadoop提供了用于做全局排序分区方式;
2.进行随机抽样,生成分区文件;
3.存在的问题:
0.在编写MR程序时不要轻易使用DoubleWritable作为Key;
1.一般不要针对于文本文件做全局排序;如果对文本文件做全局排序,对MR程序有要求:要求Mapper读取数据的k1的数据类型和Mapper输出数据的k2的数据类型一致,并且必须得是LongWritable【例如:自定义类 exts Mapper<LongWritable,Text,LongWritable,任意>】,否则程序报异常,提示Key类型不匹配;即使按照如上要求做了,但是MR程序处理的最终结果只会有一个Reducer工作并产生数据,其他的Reducer不工作;【2.9.2】
2.最好使用序列文件做全局排序,Mapper输入的Key【k1】和输出的Key【k2】的数据类型必须一致;
例子:使用./yst.seq中的数据,计算每一年的最大温度;
2.二次排序:
需求1:计算每一年每个气象站的最大温度,要求reduce个数大于1个,在全局层面上先按照年份排序,再按照气象站编号排序;
需求2:提取year、sid、temp三列数据;
0.控制Shuffle的键,自定义复合键;
1.控制按照自然键【年份】让该自然键相同的数据进入同一个reduce,即控制分区阶段;【MR程序使用的默认的分区方式是:HashPartitioner,在全局排序的时候用的TotalOrderPartitioner;】自定义分区器,分区规则是:在分区的时候只和复合键中的自然键相关;
自定义类继承Partitioner类,重写getPartition方法,重写规则只和复合键中的自然键相关;
2.控制按照自然键【年份】让该自然键相同的数据分到同一组中,即控制分组阶段;默认的分组比较器是使用复合键或者k2中的compareTo方法进行分区比较;需要自定义分组比较器,让比较逻辑只和自然键相关;
自定义类继承WritableComparator,在构造器中调用父类构造器传递的参数是要比较的类【YearStation.class,true】,重写compare方法,重写规则只和复合键中的自然键相关;
3.排序比较器默认使用的也是k2中的compareTo方法;
说明:
0.如果reduce的个数只有1个,则产生的结果局部排序,全局排序,二次排序都符合;
1.如果reduce的个数大于1个,产生的结果符合局部排序;全局排序需要使用TotalOrderPartitioner以采样器控制;二次排序的做法是先不考虑二次排序,根据需要完成数据的处理,再编写二次排序程序,对根据需求处理完成的数据做二次排序即可;
练习1:计算每一年每个气象站的平均温度,设定reduce个数为5,利用二次排序;考虑是否可以使用Combiner;
练习2:提取year、sid、temp三列数据;先按照年份排序,再按照气象站编号排序;考虑能否再次按照温度进行排序;
MR中的join:
需求:连接计算成绩数据,将多个文件连接成一个文件;
chinese.txt a|张三|100
a|李四|90
math.txt b|90|张三
b|95|李四
english.txt 张三|c|90
李四|c|100
张三 a:100,b:90,c:90
Map端的Join:使用特殊的InputFormat在读取数据的时候就已经做好了连接;
说明:对要进行连接的数据集有要求:
0.连接的所有的数据集必须按照连接条件局部有序;【预处理】
1.连接的所有的数据集必须有相同的数据分片;【要设置相同的reduce个数】
2.连接的所有的数据集不能再被分割;【对预处理的数据进行压缩】
预处理之后的数据:
chinese.txt 张三 a:100
李四 a:90
math.txt 张三 b:90
李四 b:95
english.txt 张三 c:90
李四 c:100
CompositeInputFormat<K,TupleWritable>:主要用于做连接;在使用该类时需要再指定一个InputFormat的子类【TextInputFormat、KeyValueTextInputFormat】读取数据,该类会使用所指定的InputFormat读取的key【k1】作为连接条件进行连接,将key相同的数据存放在一个集合中【TupleWritable】;
TupleWritable:类似于集合,该集合中存放是的按照连接条件连接之后的value值;
连接条件:张三 TupleWritable:[a:100,b:90,c:90]
连接条件:李四 TupleWritable:[a:90,b:95,c:100]
遍历集合,将集合中的数据持久化到HDFS集群中;
Reduce端的Join【二次排序】:利用二次排序完成连接;
/data/test/score
Map ---> <姓名,flag1>,学科标识+成绩
Shuffle ---> <姓名,flag1>,[学科标识+成绩(s)]
Reduce ---> 姓名 学科标识1+成绩1,学科标识2+成绩2,...
需要使用多输入技术:要针对不同的数据集添加不同的标识,则需要定义多个Mapper类,但是这多个Mapper类的处理中间结果数据会被一个Reducer处理;
InputFormat:
FileInputFormat:从文件中读取数据;
TextInputFormat:一个数据块是一个数据分片;
KeyValueTextInputFormat:同上;
SequenceFileInputFormat:同上;
NLineInputFormat:读取N行作为一个数据分片;
CombineInputFormat:将所有的数据都看成是一个数据分片;
DBInputFormat:从数据库表中读取数据;
OutputFormat:
FileOutputFormat:将数据写入到HDFS集群的文件中;
TextoutputFormat:将数据写入到文本文件中;
SequenceFileOutputFormat:将数据写入到序列文件中;
DBOutputFormat:将数据写入到数据库中;
工作流:JobControl,可以将多个MR作业配置到一起,自动提交执行;
0.需要将普通的Job对象先转化成ControlledJob对象;
Job job1=Job.getInstance(conf,"");
ControlledJob cj1=new ControlledJob(conf);
cj1.setJob(job1);
Job job2=Job.getInstance(conf,"");
ControlledJob cj2=new ControlledJob(conf);
cj1.setJob(job2);
Job job3=Job.getInstance(conf,"");
ControlledJob cj3=new ControlledJob(conf);
cj1.setJob(job3);
Job job4=Job.getInstance(conf,"");
ControlledJob cj4=new ControlledJob(conf);
cj1.setJob(job4);
1.使用ControlledJob对象添加每个作业之间的依赖关系;
cj2.addDependency(cj1);
cj3.addDependency(cj2);
cj3.addDependency(cj4);
2.使用JobControl提交作业:
JobControl jc=new JobControl();
jc.add(cj1);
jc.add(cj2);
jc.add(cj3);
jc.add(cj4);
new Thread(jc).start();