Hadoop学习笔记02_MapReduce练习

搭建好环境之后 ,就来跑个简单的Mapreduce试试看吧。这个比第一课难多了,需要多多练习并熟练掌握。

需要编写py脚本以及shell脚本, 所以需要学习Python和Linux的Shell编程。

虽然现在可以照抄老师的代码,但是代码有些方面还不太懂,更不能够理解透彻。所以,需要继续学习python

以下是笔记,  bash shell和py都写在同一个文本文件中,仅以空行隔开:

# MapReduce基础
# 海量都能处理, GTP级都能处理,理论上是没有瓶颈的.
# 一次性同时处理整个数据集, 数据必须一次全进来,
# 批处理方式, 大数据输入,大批数据输出.
#之所以可以用多种语言来开发,主要是有Hadoop Streaming的存在
#使用python来MR比较方便.
############################ word count 第一版############################
vim map.py
#!/etc/bin/python
import sys

for line in sys.stdin:
    ss = line.strip().split(' ')
    for word in ss:
        print '\t'.join([word.strip(),"1"])
        
cat 1.txt | python map.py 

cat word2 | grep --color -w this # 查找并显示文件中this
cat word2 | grep --color -wo this # 只显示this
cat word2 | grep --color -wo this | wc -l # 显示this数量

vim reduce.py
#!/etc/bin/python
import sys

cur_word = None
sums = 0

for line in sys.stdin:
    ss = line.strip().split('\t')
    if len(ss) !=2:
        continue
    word = ss[0].strip()
    cnt = ss[1].strip()
    
    if cur_word == None:
        cur_word = word
    if cur_word != word:
        print '\t'.join([cur_word, str(sums)])
        
        cur_word = word
        sums = 0
    sums += int(cnt)
    
print '\t'.join([cur_word, str(sums)])

# 本地测试程序流程, 通过再继续
cat word2 | python map.py | sort -k 1 | python reduce.py

下面把它放到hdfs上试试:

可以先把文本文件上传到hdfs, 如果脚本执行时发生错误:-bash: ./run.sh: /bin/bash^M: bad interpreter: No such file or directory 

原因是windows编辑的sh文件,复制到Linux中执行,则格式不对。可用 vim打开,然后  :set ff=unix 再保存退出

####################### 为Hadoop 准备shell脚本 
vim run.sh
#!/bin/bash 
#引入 streaming Jar包
STREAM_JAR_PATH="/usr/local/src/hadoop-2.7.5/share/hadoop/tools/lib/hadoop-streaming-2.7.5.jar"
# HDFS上要处理的文件 
INPUT_FILE_PATH_1="/word2"
OUTPUT_PATH="/output"
#先删除原有的输出路径,如果存在,会报错
/usr/local/src/hadoop-2.7.5/bin/hdfs dfs -rm -r $OUTPUT_PATH

# 创建源文件路径
/bigdata/hadoop-2.7.5/bin/hdfs dfs -mkdir /test
# 上传文本文件
/bigdata/hadoop-2.7.5/bin/hdfs dfs -put 1.txt /test

/usr/local/src/hadoop-2.7.5/bin/hadoop jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \
    -output $OUTPUT_PATH \
    -mapper "python map.py" \
    -reducer "python reduce.py" \
    -jobconf "mapred.reduce.tasks=2" \
    -file ./map.py \
    -file ./reduce.py 
# mapred.reduce.tasks=2 指定reduce个数, 默认为1,这个参数只是参考,不是很准. 受到数据源大小以及分片的影响 .
# -jobconf mapred.job.name="Digital_log_count" 为job指定名称 
# -cacheFile 向计算节点分发HDFS文件
# -cacheArchive 向计算节点分发HDFS压缩文件
######################### 执行上面的脚本 , 等待结果. 
hdfs dfs -get /output/part-00000
cat word2 | python map.py | sort -k1 |python reduce.py > local.data 
#对比一下结果
cat local.data | sort > local.data.1
cat part-00000 | sort > cluster.data
md5sum local.data.1
md5sum cluster.data
#或者使用cmp命令, 结果是0就表示一致.
cmp local.data.1 cluster.data
echo $?

 

另一个版本的,白名单版的mapreduce

############################ word count 第二版只统计白名单 (white_list)############################
vim map.py
#!/etc/bin/python
import sys 

def read_local_file_func(f):
    word_set = set()
    file_in = open(f, 'r')
    for line in file_in:
        word = line.strip()
        word_set.add(word)
    return word_set

def mapper_func(white_list_fd):
    word_set = read_local_file_func(white_list_fd)

    for line in sys.stdin:
        ss = line.strip().split(' ')
        for s in ss: 
            word = s.strip()
            if word != "" and (word in word_set):
                print "%s\t%s" % (s, 1)

if __name__ == "__main__":
    module = sys.modules[__name__]
    func = getattr(module, sys.argv[1])
    args = None
    if len(sys.argv) > 1:
        args = sys.argv[2:]
    func(*args)


vim reduce.py
#!/etc/bin/python
import sys

def reduer_func():
    current_word =None
    count_pool = []
    sum = 0 

    for line in sys.stdin:
        word, val = line.strip().split('\t')
    
        if current_word ==None:
            current_word = word

        if current_word != word:
            for count in count_pool:
                sum += count
            print "%s\t%s" % (current_word, sum)
            current_word = word
            count_pool = []
            sum = 0 
    
        count_pool.append(int(val))
        
    for count in count_pool:
        sum += count
    print "%s\t%s" % (current_word, str(sum))

if __name__ == "__main__":
    module = sys.modules[__name__]
    func = getattr(module, sys.argv[1])
    args = None
    if len(sys.argv) > 1:
        args = sys.argv[2:]
    func(*args)


vim run.sh
#!/bin/bash 
STREAM_JAR_PATH="/usr/local/src/hadoop-2.7.5/share/hadoop/tools/lib/hadoop-streaming-2.7.5.jar"

INPUT_FILE_PATH_1="/word2"
OUTPUT_PATH="/output"
#先删除原有的输出路径,如果存在,会报错
/usr/local/src/hadoop-2.7.5/bin/hdfs dfs -rm -r $OUTPUT_PATH

#step 1
/usr/local/src/hadoop-2.7.5/bin/hadoop jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \
    -output $OUTPUT_PATH \
    -mapper "python map.py mapper_func white_list" \
    -reducer "python reduce.py reduer_func" \
    -jobconf "mapred.reduce.tasks=2" \
    -file ./map.py \
    -file ./reduce.py \ 
    -file ./white_list

#执行本地测试与hdfs测试

############################ 结束: word count 第二版只统计白名单 (white_list)############################

 

老师在课堂上提到的2个知识点:

# Streaming 优点: 开发效率高, 程序运行效率高, 便于平台进行资源控制
    #Streaming框架中通过limit等方式可以灵活地限制应用程序使用的内存资源

# Streaming缺点: 两次数据copy和解析(分割),带来一定的开销
    #Streaming不能直接处理二进制数据,如果要处理二进制的数据,比较好的方法是将二进制的key和value进行base64的编码转化成文本
python
import base64
base64.b64encode('abcdefg')
base64.b64decode('YWJjZGVmZw==')


### jieba 中文分词
# wget --no-check-certificate https://pypi.python.org/packages/71/46/c6f9179f73b818d5827202ad1c4a94e371a29473b7f043b736b4dab6b8cd/jieba-0.39.zip
file jieba-0.39.zip
unzip jieba-0.39.zip
### 压缩解压命令
gzip 1.txt 
gunzip 1.txt.gz

 

两种文件的分发与打包:

#############################    文件分发与打包(-cacheFile)
#如果本地配置和数据很大的时候,需要提前上传到HDFS目录上.
#如果文件(如字典文件)存放在HDFS中,希望计算时在每个计算节点上将文件当作本地文件处理,可以使用-cacheFile hdfs://host:port/path/to/file#linkname选项在计算节点缓存文件.
# Streaming程序通过./linkname访问文件 

vim run.sh
HADOOP_CMD="/usr/local/src/hadoop-2.7.5/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.7.5/share/hadoop/tools/lib/hadoop-streaming-2.7.5.jar"

INPUT_FILE_PATH_1="/The_Man_of_property.txt"
OUTPUT_PATH="/output_cachefile_broadcast"

$HADOOP_CMD fs -rm -r -skipTrash $OUTPUT_PATH

#step 1
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \
    -output $OUTPUT_PATH \
    -mapper "python map.py mapper_func ABC" \
    -reducer "python red.py reducer_func" \
    -jobconf "mapred.reduce.tasks=2" \
    -jobconf "mapred.job.name=cachefile_demo" \
    -cacheFile "hdfs://master:9000/white_list.txt#ABC" \
    -file "./map.py" \
    -file "./red.py" \

#ABC是文件别名,代替white_list.txt  另外,map.py和reduce.py可以只用上面第一例的,不必更改, 因为只是文件分发方式变了.
# tasks=2的时候,也可能在结果产生空文件, 因为hash到key的时候,分到一个文件里了.
    
    # map的输出也就是中间结果启用压缩, 主要是为了减少shuffle过程中的网络传输数据量
    -jobconf "mapred.compress.map.output=true" \
    -jobconf "mapred.map.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec" \
    # 输出时压缩, 减少输出结果占用HDFS存储
    -jobconf "mapred.output.compress=true" \
    -jobconf "mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec" \


#############################  文件分发与打包(-cacheArchive)
#如果要分发的文件有目录结构,可以先将整个目录打包,上传到HDFS,再用-cacheArchive hdfs://host:port/path/to/archivefile#linkname 分发压缩包
#有2个白名单文件, white_list_1, white_list_2 可以打包到一起
tar zcvf w.tar.gz white_list_*  #可以复制到别的目录解压验证
hdfs dfs -put w.tar.gz /

vim run.sh
HADOOP_CMD="/usr/local/src/hadoop-2.7.5/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.7.5/share/hadoop/tools/lib/hadoop-streaming-2.7.5.jar"

INPUT_FILE_PATH_1="/The_Man_of_property.txt"
OUTPUT_PATH="/output_cachefile_broadcast"

$HADOOP_CMD fs -rm -r -skipTrash $OUTPUT_PATH

#step 1
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \
    -output $OUTPUT_PATH \
    -mapper "python map.py mapper_func WH.gz" \
    -reducer "python red.py reducer_func" \
    -jobconf "mapred.reduce.tasks=2" \
    -jobconf "mapred.job.name=cachefile_demo" \
    -cacheArchive "hdfs://master:9000/w.tar.gz#WH.gz" \
    -file "./map.py" \
    -file "./red.py" \


vim map.py
#!/usr/bin/python
import os
import sys
import gzip

def get_file_handler(f):
    file_in = open(f, 'r')
    return file_in

def get_cachefile_handlers(f):
    f_handlers_list = []
    if os.path.isdir(f):
        for fd in os.listdir(f):
            f_handlers_list.append(get_file_handler(f + '/' +fd))
    return f_handlers_list

def read_local_file_func(f):
    word_set = set()
    for cachefile in get_cachefile_handlers(f):
        for line in cachefile:
            word = line.strip()
            word_set.add(word)
    return word_set    
    
def mapper_func(white_list_fd):
    word_set = read_local_file_func(white_list_fd)
    
    for line in sys.stdin:
        ss = line.strip().split(' ')
        for s in ss:
            word = s.strip()
            if word != "" and (word in word_set):
                print "%s\t%s" % (s,1)

if __name__ == "__main__":
    module = sys.modules[__name__]
    func = getattr(module, sys.argv[1])
    args = None
    if len(sys.argv) > 1:
        args = sys.argv[2:]
    func(*args)
    

vim reduce.py
#!/etc/bin/python
import sys

def reduer_func():
    current_word =None
    count_pool = []
    sum = 0 

    for line in sys.stdin:
        word, val = line.strip().split('\t')
    
        if current_word ==None:
            current_word = word

        if current_word != word:
            for count in count_pool:
                sum += count
            print "%s\t%s" % (current_word, sum)
            current_word = word
            count_pool = []
            sum = 0 
    
        count_pool.append(int(val))
        
    for count in count_pool:
        sum += count
    print "%s\t%s" % (current_word, str(sum))

if __name__ == "__main__":
    module = sys.modules[__name__]
    func = getattr(module, sys.argv[1])
    args = None
    if len(sys.argv) > 1:
        args = sys.argv[2:]
    func(*args)

 

老师给的不想开发代码,直接套模板的MR方案:

######################### 不想开发代码,只做单reduce, 如下是配置文件以及2个py脚本。
vim run.sh
set -e -x

HADOOP_CMD="/usr/local/src/hadoop-2.7.5/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.7.5/share/hadoop/tools/lib/hadoop-streaming-2.7.5.jar"

INPUT_FILE_PATH_A="/a.txt"
INPUT_FILE_PATH_B="/b.txt"

OUTPUT_SORT_PATH="/output_sort"

$HADOOP_CMD fs -rm -r -skipTrash $OUTPUT_SORT_PATH

#step 3
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_A,$INPUT_FILE_PATH_B \
    -output $OUTPUT_SORT_PATH \
    -mapper "python map_sort.py" \
    -reducer "python red_sort.py" \
    -file "./map_sort.py" \
    -file "./red_sort.py" \
    -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \
    -jobconf mapred.output.key.comparator.class=org.apache.hadoop.mapred.lib.KeyFieldBasedComparator \
    -jobconf stream.num.map.output.key.fields=1 \
    -jobconf stream.map.output.field.separator='^I' \
    -jobconf map.output.key.field.separator='^I' \
    -jobconf mapred.text.key.partitioner.options="-k1,1" \
    -jobconf mapred.text.key.comparator.options="-k1,1n" \
    -jobconf mapred.reduce.tasks=1

#上面的几行参数缺一个都不行.
#下面这2行按第1列的数值去partition, 保证它分到哪个桶,保证它这数据能分到哪个reduce上处理. key和partition是不相等的,效果是按第1列数值去partition,按第2列去排序.
# -jobconf stream.num.map.output.key.fields=2 \ 
# -jobconf num.key.fields.for.partition=1 \ 
#而下面这一行代替了partition
# -jobconf mapred.text.key.partitioner.options="-k1,1" \ #第1列开始,1列结束,也就是将第1列作为partition
# -jobconf mapred.text.key.comparator.options="-k1,1n" \ #按第1到1的列排序, n是将其转换为数值型.

### 最少代码的自然数排序,map只是将字符串解析一下。基本没做处理。
vim map_sort.py
#!/etc/bin/python
import sys

for line in sys.stdin:
    ss = line.strip().split('\t')
    key = ss[0]
    val = ss[1]
    
    print "%s\t%s" % (key, val)
    

### reduce读什么出什么,基本没做处理。
vim red_sort.py
#!/etc/bin/python
import sys

for line in sys.stdin:
    print line.strip()
    

 

以及MR join

########################## MapReduce Join   三个map文件, 前2个处理完成,合并到第3个mapreduce中
vim map_a.py
#!/usr/bin/python

import sys

for line in sys.stdin:
    ss = line.strip().split('^I')
    
    key = ss[0]
    val = ss[1]
    
    print "%s\t1\t%s" % (key,val)
    
# 示例数据 a.txt
aaa1    123
aaa2    123
aaa3    123
aaa4    123
aaa5    123
aaa6    123
aaa7    123
aaa8    123
aaa9    123
aaa10   123

vim map_b.py
#!/usr/bin/python

import sys

for line in sys.stdin:
    ss = line.strip().split('^I')
    
    key = ss[0]
    val = ss[1]
    
    print "%s\t2\t%s" % (key,val)
    
# 示例数据 b.txt
aaa1    hadoop
aaa2    hadoop
aaa3    hadoop
aaa4    hadoop
aaa5    hadoop
aaa6    hadoop
aaa7    hadoop
aaa8    hadoop
aaa9    hadoop
aaa10   hadoop

#### reduce_join脚本 这段代码可能有问题。
vim red_join.py
#!/usr/bin/env python

import sys

val_1 = []
for line in sys.stdin:
    key, flag, val = line.strip().split('\t')
    
    if flag =='1':
        val_1.append(val)
    elif flag == '2' and val_1 != "" :
        val_2 = val
        
        for v in val_1:
            print "%s\t%s\t%s" % (key, v, val_2)
        val_1 = []
        
# 上述这段代码可能有问题,要求对输入的数据符合格式

 

posted @ 2018-02-19 14:16  枫若雪  阅读(1194)  评论(0编辑  收藏  举报