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

fondtiger發表於2021-09-09


條件,假設你已經裝好了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幾秒跑完,效率還是提高了很多的。

©著作權歸作者所有:來自51CTO部落格作者Slaytanic的原創作品,謝絕轉載,否則將追究法律責任


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2508/viewspace-2820386/,如需轉載,請註明出處,否則將追究法律責任。

相關文章