實例講解hadoop中的map/reduce查詢(python語言實現)

條件,假設你已經裝好了hadoop集羣,配好了hdfs並可以正常運行。

$hadoop dfs -ls /data/dw/explorer
Found 1 items
drwxrwxrwx     - rsync supergroup                    0 2011-11-30 01:06 /data/dw/explorer/20111129


$ hadoop dfs -ls /data/dw/explorer/20111129
Found 4 items
-rw-r--r--     3 rsync supergroup     12294748 2011-11-29 21:10 /data/dw/explorer/20111129/explorer_20111129_19_part-00000.lzo
-rw-r--r--     3 rsync supergroup             1520 2011-11-29 21:11 /data/dw/explorer/20111129/explorer_20111129_19_part-00000.lzo.index
-rw-r--r--     3 rsync supergroup     12337366 2011-11-29 22:09 /data/dw/explorer/20111129/explorer_20111129_20_part-00000.lzo
-rw-r--r--     3 rsync supergroup             1536 2011-11-29 22:10 /data/dw/explorer/20111129/explorer_20111129_20_part-00000.lzo.index

數據格式如下

20111129/23:59:54 111.161.25.184 182.132.25.243 <Log_Explorer ProductVer="5.05.1026.1111" UUID="{C9B80A9B-704E-B106-9134-1ED3581D0123}"><UserDoubleClick FileExt="mp3" AssociateKey="Audio.mp3" Count="1"/></Log_Explorer>



1.map腳本取數據explorer_map.py

#!/usr/bin/python
#-*-coding:UTF-8 -*-
import sys
import cElementTree

debug = False#設置lzo文件偏移位
if debug:
        lzo = 0
else:
        lzo = 1

for line in sys.stdin:
        try:
                flags = line[:-1].split('\t')
#hadoop查詢走標準輸入,數據以\t分隔,去掉每行中的\n
                if len(flags) == 0:
                        break
                if len(flags) != 11+lzo:
#hadoop採用lzo則偏移位+1,lzo設置爲False則+1
                        continue
                stat_date=flags[0+lzo]#日期
                stat_date_bar = stat_date[:4]+"-"+stat_date[4:6]+'-'+stat_date[6:8]#拼成2011-11-29格式
                version = flags[4+lzo]
                xmlstr = flags[10+lzo]
                #xmlstr=line
                dom = cElementTree.fromstring(xmlstr)
#xml字段對象,以下均爲取值操作
                uuid = dom.attrib['UUID']
                node = dom.find('UserDoubleClick')
                associateKey=node.get('AssociateKey')
                associateKeys=associateKey.split('.')
                player = associateKeys[0]
                fileext=node.get('FileExt')
                count=node.get('Count')
                print stat_date_bar+','+version+','+fileext+','+player+','+associateKey+'\t'+count
#輸出map後的數據,這裏map不對數據做任何處理,只做取值,拼接操作
#將\t前的字符串作爲key輸入reduce,\t後的count作爲reduce計算用的value
except Exception,e:
print e
#拋出異常        

2.reduce腳本計算結果並輸出explorer_red.py

#!/usr/bin/python
#-*-coding:UTF-8 -*-
import sys
import cElementTree
import os
import string

res = {}

for line in sys.stdin:
        try:
                flags = line[:-1].split('\t')
#拆分\t以獲得map傳過來的key和value
                if len(flags) != 2:
#\t切割後,如果數據有問題,元素多於2或者少於2則認爲數據不合法,跳出繼續下一行
                        continue
                skey= flags[0]
#取出第一個元素作爲key
                count=int(flags[1])
#取出第二個元素作爲value
                if res.has_key(skey) == False:
                        res[skey]=0
                res[skey] += count
#計算count總和
        except Exception,e:
                pass
#不拋出,繼續執行

for key in res.keys():
        print key+','+'%s' % res[key]
#格式化輸出,以放入臨時文件

3.放入crontab執行的腳本

#!/bin/sh

[ $1 ] && day=$1 DATE=`date -d "$1" +%Y%m%d`
[ $1 ] || day=`date -d "1 day ago" +%Y%m%d`     DATE=`date -d "1 day ago" +%Y%m%d`
#取昨天日期

cd /opt/modules/hadoop/hadoop-0.20.203.0/
#進入hadoop工作目錄
bin/hadoop jar contrib/streaming/hadoop-streaming-0.20.203.0.jar -file /home/rsync/explorer/explorer_map.py -file /home/rsync/explorer/explorer_red.py -mapper /home/rsync/explorer/explorer_map.py -reducer /home/rsync/explorer/explorer_red.py -inputformat com.hadoop.mapred.DeprecatedLzoTextInputFormat -input /data/dw/explorer/$DATE -output /tmp/explorer_$DATE
#執行map/reduce,並將排序完結果放入hdfs:///tmp/explorer

bin/hadoop fs -copyToLocal /tmp/explorer_$DATE /tmp
#將m/r結果從hdfs://tmp/explorer_$DATE 保存到本地/tmp下
bin/hadoop dfs -rmr /tmp/explorer_$DATE
#刪除hdfs下臨時文件夾

cd
#返回自身目錄
cd explorer
#進入explorer文件夾
./rm.py $DATE
執行入庫和刪除臨時文件夾腳本


4.將/tmp生成的結果入庫並刪除臨時文件夾

#!/usr/bin/python

import os
import sys
import string

if len(sys.argv) == 2:
                date = sys.argv[1:][0] #取腳本參數
                os.system ("mysql -h192.168.1.229 -ujobs -p223238 -P3306    bf5_data    -e \"load data local infile '/tmp/explorer_"+date+"/part-00000' into table explorer FIELDS TERMINATED
BY '\,' (stat_date,ver,FileExt,player,AssociateKey,count)\"")#執行入庫sql語句,並用load方式將數據加載到統計表中
                os.system ("rm -rf /tmp/explorer_"+date)#刪除map/reduce過的數據
else:
                print "Argv error"

#因爲沒有安裝MySQLdb包,所以用運行腳本的方式加載數據。

原始數據和最後完成的輸出數據對比,紅色爲原數據,綠色爲輸出數據

20111129/23:59:54 111.161.25.184 182.132.25.243 <Log_Explorer ProductVer="5.05.1026.1111" UUID="{C9B80A9B-704E-B106-9134-1ED3581D0123}"><UserDoubleClick FileExt="mp3" AssociateKey="Audio.mp3" Count="1"/></Log_Explorer>

-----------------------------------------------------------

2011-11-29,5.05.1026.1111,mp3,Audio,Audio.mp3,1


5.調試技巧

因爲這種方式比較抽象,所以你很難得到一個直觀的調試過程。建議調試如下

#將hadoop中的數據文本copy出來一個,lzo需要解壓縮,然後將map中的debug模式置爲True,也就是不加hadoop中的lzo偏移量。
#用head輸入hadoop裏的文件,通過管道操作放入map/reduce中執行,看輸出結果

$head explorer_20111129 | explorer_map.py | explorer_red.py

一天的數據大概幾十個G,以前用awk和perl腳本跑需要至少半小時以上,改用map/reduce方式後,大概20幾秒跑完,效率還是提高了很多的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章