1. Hadoop包含两核心部分
- hdfs
- Hadoop distribute file system -- hadoop分布式文件系统,存储数据
- Namenode、Datanode
- 常用命令形式:hadoop fs -ls / hadoop fs -mkdir
- MapReduce
- 分而治之;map:实现分治;reduce:实现合并
- 解决数据可分割的计算问题
- 编程接口:常用Streaming;组成:Job配置文件、map函数,reduce函数
2. hdfs结构图
- Namenode存储元数据,数据信息,数据备份信息
- Datanode 数据备份:本机架备份、异地备份
3. MapReduce调度框架
- JobClient: 负责根据用户指定参数生成一个mapreduce作业,提交到JobTracker
- JobTracker: 单Master节点,将Job所有task调度到TaskTracker
- TaskTracker: 部署在每台计算机节点的一个service
4. MapReduce 执行层
- Map阶段,读入数据,通过partition聚集相同key数据,并写到本机磁盘
- Reduce阶段,不同reduce,读入Map阶段各maps的相应输出
5. streaming 作业
- streaming mapper
- 先启动用户提交作业时指定的一个外部程序,一般是脚本
- 这个外部程序作为streaming mapper的子进程,streaming mapper读取用户输入后,不再是调用map函数处理,而是通过管道写到子进程的标准输入
- 从子进程的标准输出读取数据,写到磁盘上
- streaming 作业数据流向
- 父进程是Java,负责读取数据通过管道发送给子进程
- 通过管道把结果再读取回来
一份数据在两个不同进程中传递两次
6 mapreduce – shuffle
- map --> shuffle –> reduce
- shuffle 从多个节点传递到多个节点,而不是多个节点到一个节点
- shuffle 包含partition、combiner
- partition : 数据归并,分割map每个节点的结果,按照key分别映射给不同的reduce,默认是HashPartition,which reducer == (key.hashCode & Integer.MAX_VALUE)% numReduceTasks
- 作用:计算(key, value)所属分区 ; 把同一分区数据合并、聚集
- combiner: combiner属于优化方案,由于带宽限制,应该尽量map和reduce之间的数据传输数量。它在Map端把同一个key的键值对合并在一起并计算,计算规则与reduce一致,所以combiner也可以看作特殊的Reducer
7. streaming 常用配置项
- stream.map.output.field.separator // 该参数属于streaming作业参数,设置map输出的字段分隔符,默认为“\t”,该分隔符只对下面的stream.num.map.output.key.fields参数生效
- stream.num.map.output.key.fields // 设置map输出的前几个字段作为key,一般与第二项stream.map.output.field.separator 结合使用
- mapred.text.key.partitioner.options // 设置key内某个字段或者某个字段范围用做partition
- mapred.text.key.comparator.options // 设置key中需要比较的字段或字节范围
- partitioner // 主要用于对键值进行划分,负责将map的输出结果根据key进行分割。Key用于确定不同的key落到不同的reduce上,通常对key进行Hash以后对reduce取mod,该key对应的纪录最终将根据mod值落到对应的reduce上进行处理。HashPartitioner, IndexUpdatePartitioner, KeyFieldBasedPartitioner, SleepJob,默认的为 HashPartitioner,即对key直接进行hash分到对应的reduce,具体见第6部分。更高级一点的为 KeyFieldBasedPartitioner,该partitioner可以指定key中前几个字段用于分割
- HashPartition 最基本的Partitioner,如果不指定Partitioner的话则默认使用该类。输出格式为最基本的key”\t“value
- KeyFieldBasedPartitioner 可以看做HashPartitioner的扩展,他将原有的对单字段Key的hash扩展到可以灵活地对多字段key进行分桶并排序,对应配置参数如下:
- -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner // 该参数表示作业启用KeyFieldBasedParitioner
- -D map.ouput.key.field.separator // 该参数属于KeyFieldBasedPartitioner参数,只有当启用KeyFieldBasedParititioner时,该参数才会生效;该参数指明map输出结果中字段之间的分隔符(该分隔符只对下面的num.key.fields.for.partition参数生效);
- -D num.key.fields.for.partition // 该参数同样属于KeyFieldBasedPartitioner参数;该参数指明map输出结果的key按上述分隔符切分后,前几个字段将用来做partition;该参数不能与mapred.text.key.partitioner.options共用;
- -D mapred.text.key.partitioner.options // 该参数同样属于KeyFieldBasedPartitioner参数; 该参数指明map输出结果的key按上述分隔符切分后,使用哪些字段用来做partition;该参数不能与num.key.fields.for.partition共用,一起使用则以num.key.fields.for.partition为准;
- KeyFieldBaseComparator // 可以灵活设置比较位置的高级比较器,但是它和没有自己独有的比较逻辑,而是使用默认Text的基于字典序或者通过-n来基于数字比较,直观来说,partition指定key中的分区元素,KeyFieldBaseComparator用作指定key排序字段以及排序规则,参数配置如下:
- -D mapred.output.key.comparator.class=org.apache.hadoop.mapred.lib.KeyFieldBasedComparator // 该参数表示作业启用KeyFieldBasedComparator
- -D mapred.text.key.comparator.options="-k3,3 -k4nr" // 以key中第三个字段正序,第四个字段逆序比较排序
- stream.memory.limit // 任务内存限制
- mapred.reduce.tasks // 指定reducer个数
- cmdenv //传递给streaming命令的环境变量
- -input //HDFS目录或文件路径, Mapper的输入数据,文件要在任务提交前手动上传到HDFS
- -output // reducer输出结果的HDFS存放路径, 不能已存在,但脚本中一定要配置,多次-input,指定多个输入文件
- -mapper // 可执行命令,mapper程序
- -reducer // 可执行命令, reduce程序,不需要reduce处理就不指定
- -file //本地mapper、reducer程序文件、程序运行需要的其他文件,将本地文件分发给计算节点;文件作为作业的一部分,一起被打包并提交,所有分发的文件最终会被放置在datanode该job的同一个专属目录下:jobcache/job_xxx/jar
- -cacheFile //分发HDFS文件
- -cacheArchive // 分发HDFS压缩文件、压缩文件内部具有目录结构
- mapred.job.priority //作业优先级
- mapred.job.map.capacity // 最多同时运行map任务数
- mapred.job.reduce.capacity //最多同时运行reduce任务数
- mapred.job.name // job name
8. key-partition 实例
- key 不等于 partition,也就是说,分桶规则跟map阶段的key有可能不是一回事
- 假设,文件A中内容如下:
- 第一种作业方式(部分参数):
./hadoop streaming
-D stream.map.output.field.separator=.
-D stream.num.map.output.key.fields=2
只是将map的输出结果按两个字段切分成了key和value;再分桶上我们可以看出,它是以前两个字段作为一个整体来进行分桶的,e.5与e.9没有分在一个reduce
- 第二种作业:
./hadoop streaming
-D stream.map.output.field.separator=.
-D stream.num.map.output.key.fields=2
-D map.output.key.field.separator=.
-D num.key.fields.for.partition=1
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner\
这里启用了KeyFieldBasedPartitioner,并且制定分桶以key的第一个字段为准;我们可以看出mapred依然采用的是前两个字段为key,但是在分桶上只对第一个字段做了哈希函数,因此这次e.5和e.9分到了一个reducer内
- 第三种作业
./hadoop streaming
-D stream.map.output.field.separator=.
-D stream.num.map.output.key.fields=3
-D map.output.key.field.separator=.
-D num.key.fields.for.partition=1
-D mapred.text.key.partitioner.options=-k2,3
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner
这次将key的长度改成3个字段,分桶标准也变成key的第2、3个字段,可以看出e.5.1被分在了一起,而第三个字段不同的e.5.9被分到了其他的reducer中
- 第四种作业
./hadoop streaming \
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \
-D mapred.output.key.comparator.class=org.apache.hadoop.mapred.lib.KeyFieldBasedComparator \
-D stream.num.map.output.key.fields=4 \
-D stream.map.output.field.separator=. \
-D map.output.key.field.separator=. \
-D mapred.text.key.partitioner.options=-k1,2 \
-D mapred.text.key.comparator.options="-k3,3 -k4nr" \
这次key的长度为4,分桶标准为key的第一、二个字段,可以看出,e.5被分到一个桶内,而输出结果,按照key的第三个字段的正序,第四个字段的逆序排列输出