這篇文章我以前是拜讀過的,今天閒來沒事,就想拿來當作MapReduce的練習。 java
MapReduce這把刀太大,刀大了問題就抵不住這刀鋒了,事實上一開始我想着,這麼多些題目,當是要花很多功夫的,但當我作完一題繼續看下面的題目的時候,才發現這些題目在MapReduce模型下顯得大同小異了,看來拿大刀的人是無論砍的是木頭仍是人頭的,而是直接抽象成柱形物而後掄起刀一刀就下去了。 程序員
直入主題: 面試
一、海量日誌數據,提取出某日訪問百度次數最多的前K個IP。[稍微改變] 算法
說明:每一次訪問網頁就在日誌中記錄1次訪問者的IP,獨佔一行,一個小數據能夠在這裏下載。 apache
實在是想不出如何能在一個Job中解決這個問題,因此仍是把它拉扯成了兩個Job來解決。
Job1:將相同IP的記錄合併,造成<ip,count>形式,其中count是對這個ip的計數。
Job2:按count排序<ip,count>並選擇前K個進行輸出。
這裏我寫了一個可序列化的類IPAndCount,若是稍微熟悉MapReduce或者看明白我以前寫的
關係型MapReduce模式:選擇、分組和組內排序
你就知道這是爲了排序而準備的。MapReduce有一個「Shuffle and sort」,這個階段是利用key來對tuple進行排序的,而排序時調用的即是key的compareTo()方法。事實上若是job1輸出的數據兩足夠小,咱們徹底能夠在內存中進行排序而利用MapReduce框架,這樣就能夠省下一個Reduce階段,可是對於這個問題顯然不行。
IPAndCount很直白,就是包裝了上述的<ip,count>。
- import java.io.DataInput;
- import java.io.DataOutput;
- import java.io.IOException;
-
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.io.WritableComparable;
-
-
- public class IPAndCount implements WritableComparable{
- Text ip;
- IntWritable count;
-
- public IPAndCount(){
- this.ip = new Text("");
- this.count = new IntWritable(1);
- }
-
- public IPAndCount(Text ip, IntWritable count){
- this.ip = ip;
- this.count = count;
- }
-
- public IPAndCount(String ip, int count){
- this.ip = new Text(ip);
- this.count = new IntWritable(count);
- }
-
- public void readFields(DataInput in) throws IOException {
- ip.readFields(in);
- count.readFields(in);
- }
-
- public void write(DataOutput out) throws IOException {
- ip.write(out);
- count.write(out);
- }
-
- public int compareTo(Object o) {
- return ((IPAndCount)o).count.compareTo(count) == 0?
- ip.compareTo(((IPAndCount)o).ip):((IPAndCount)o).count.compareTo(count);//若是隻比較count會丟失數據,應該是suffle階段的問題
- }
-
- public int hashCode(){
- return ip.hashCode();
- }
-
- public boolean equals(Object o){
- if(!(o instanceof IPAndCount))
- return false;
- IPAndCount other = (IPAndCount)o;
- return ip.equals(other.ip) && count.equals(other.count);
- }
-
- public String toString(){
- StringBuffer buf = new StringBuffer("[ip=");
- buf.append(ip.toString());
- buf.append(",count=");
- buf.append(count.toString());
- buf.append("]");
- return buf.toString();
- }
-
- public Text getIp() {
- return ip;
- }
- public void setIp(Text ip) {
- this.ip = ip;
- }
- public IntWritable getCount() {
- return count;
- }
- public void setCount(IntWritable count) {
- this.count = count;
- }
- }
下面對FindActiveIp進行說明:
SumUpIpMapper和SumUpIPReducer事實上就是一個MapReduce中最基礎的詞頻統計程序WordCount。你能夠加一個Combiner來優化一下,我遺漏了。
從配置中能夠看見兩個Job的配置:job 和job2。
依賴關係是job -> job2,代碼中使用了JobControl來解決做業間的依賴關係,JobControl.run()方法會在做業都運行完後才返回。
Job2的輸入路徑是Job1的輸出路徑,從參數中能夠看出這一點。
Job1的輸出在輸出文件中的表現是:ip,count
Job2再從文件中讀入,使用的是KeyValueTextInputFormat,它對應的是TextOutputFormat,咱們能夠從job1的配置中看出來。
BeforeSortIPMapper從job1的輸出中讀取數據幷包裝成IPAndCount類型,以便MapReduce框架在「shuffle and sort」階段利用它來排序。
最後SelectTopKIPReducer選出前K個進行輸出便可,在這裏咱們設置最後的reduce只有一個reduce task,以使全部數據匯聚到一臺機子上進行處理。
- import java.io.IOException;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.conf.Configured;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapred.lib.ChainMapper;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.Mapper;
- import org.apache.hadoop.mapreduce.Reducer;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
- import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
- import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
- import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
- import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
- import org.apache.hadoop.util.Tool;
- import org.apache.hadoop.util.ToolRunner;
-
-
- public class FindActiveIP extends Configured implements Tool{
-
- public static class SumUpIPMapper extends Mapper<LongWritable,Text,Text,IntWritable>{
- IntWritable one = new IntWritable(1);
- public void map(LongWritable key, Text value, Context context)
- throws IOException,InterruptedException{
- context.write(value, one);
- }
- }
-
- public static class SumUpIPReducer extends Reducer<Text,IntWritable,Text,IntWritable>{
- //這裏能夠選擇前k個進行輸出以優化
- public void reduce(Text key, Iterable<IntWritable> values, Context context)
- throws IOException, InterruptedException{
- int sum = 0;
- for(IntWritable v : values){
- sum += v.get();
- }
- context.write(key, new IntWritable(sum));
- }
- }
-
-
- public static class BeforeSortIPMapper extends Mapper<Text,Text,IPAndCount,Text>{
- public void map(Text key, Text value, Context context)
- throws IOException,InterruptedException{
- IPAndCount tmp = new IPAndCount(key,new IntWritable(Integer.valueOf(value.toString())));
- System.out.println(tmp);
- context.write(tmp,new Text());
- }
- }
-
-
- //set num of this reducer to one
- public static class SelectTopKIPReducer extends Reducer<IPAndCount,Text,IPAndCount,Text>{
- int counter = 0;
- int K = 10;
- public void reduce(IPAndCount key, Iterable<Text> values, Context context)
- throws IOException, InterruptedException{
- System.out.println(key);
- if(counter < K){
- context.write(key, null);
-
- counter++;
- }
-
- }
- }
- public int run(String[] args) throws Exception {
- Configuration conf = new Configuration();
- Job job = new Job(conf,"SumUpIP");
- job.setJarByClass(FindActiveIP.class);
- job.setInputFormatClass(TextInputFormat.class);
- job.setOutputFormatClass(TextOutputFormat.class);
- job.getConfiguration().set("mapred.textoutputformat.separator", ",");
- Path in = new Path(args[0]);
- Path out = new Path(args[1]);
- FileInputFormat.setInputPaths(job, in);
- FileOutputFormat.setOutputPath(job, out);
- job.setMapperClass(SumUpIPMapper.class);
- job.setReducerClass(SumUpIPReducer.class);
- job.setMapOutputKeyClass(Text.class);
- job.setMapOutputValueClass(IntWritable.class);
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(IntWritable.class);
- job.setNumReduceTasks(7);
-
- Configuration conf2 = new Configuration();
- Job job2 = new Job(conf2,"SortAndFindTopK");
- job2.setJarByClass(FindActiveIP.class);
- job2.setInputFormatClass(KeyValueTextInputFormat.class);
- job2.getConfiguration().set("mapreduce.input.keyvaluelinerecordreader.key.value.separator", ",");
- job2.setOutputFormatClass(TextOutputFormat.class);
- Path in2 = new Path(args[1]);
- Path out2 = new Path(args[2]);
- FileInputFormat.setInputPaths(job2,in2);
- FileOutputFormat.setOutputPath(job2, out2);
- job2.setMapperClass(BeforeSortIPMapper.class);
- job2.setReducerClass(SelectTopKIPReducer.class);
- job2.setMapOutputKeyClass(IPAndCount.class);
- job2.setMapOutputValueClass(Text.class);
- job2.setOutputKeyClass(IPAndCount.class);
- job2.setOutputValueClass(Text.class);
- job2.setNumReduceTasks(1);
-
- JobControl jobControl = new JobControl("FindTopKIP");
- ControlledJob cJob1 = new ControlledJob(conf);
- cJob1.setJob(job);
- ControlledJob cJob2 = new ControlledJob(conf2);
- cJob2.setJob(job2);
- jobControl.addJob(cJob1);
- jobControl.addJob(cJob2);
- cJob2.addDependingJob(cJob1);
- jobControl.run();
- return 0;
- }
-
- public static void main(String args[]) throws Exception{
- int res = ToolRunner.run(new FindActiveIP(), args);
- System.exit(res);
- }
-
- }
大刀拿習慣了,從前的大刀就成了如今的繡花針,不是繡花針很差,只是用着不順手。當你聽着歌用java寫着MapReduce,忽然有人在你耳邊喊了一句:Pig~Pig~Pig~
你很難不心動!程序員愛偷懶堪比女人愛逛街,都是爲了快樂啊~
下面是用Pig來處理上述的問題:
- grunt> records = LOAD 'input/ipdata' AS (ip:chararray);
- grunt> grouped_records = GROUP records BY ip;
- grunt> counted_records = FOREACH grouped_records GENERATE group, COUNT(records);
- grunt> sorted_records = ORDER counted_records BY $1 DESC;
- grunt> topK = LIMIT sorted_records 10;
- grunt> DUMP topK;
你看,數數,沒暈數一數,6行!僅僅6行就解決了。
行1:將文件裝入
行2:按ip分組
行3:組內計數
行4:組間按ip訪問計數排序
行5:選擇前10個數據
行6:運行並輸出。
雖然咱們的Pig方法實際上跑了3個job才完成任務,相比於java寫的MapReduce多了一個job,但Pig顯然更愉快些。
這是最後結果:
(192.168.0.1,1559)
(192.168.0.21,7)
(192.168.0.14,4)
(192.168.0.10,4)
(192.168.0.12,4)
(192.168.0.32,4)
(192.168.0.13,3)
(192.168.0.3,3)
(192.168.0.2,2)
(192.168.0.11,1)
這個算法對帶寬的壓力仍是比較大的,除了加一個Combiner以外,代碼中還提到了另外一個小小的優化在進入第一個Reduce階段的一個reduce task中的數據足以裝入內存時,這是很容易解決的。這不是多好的優化,應當有更加好的優化方式能過濾更多的數據,不然。。。這不科學~
二、尋找熱門查詢:搜索引擎會經過日誌文件把用戶每次檢索使用的全部檢索串都記錄下來,每一個查詢串的長度爲1-255字節。
假設目前有100億個記錄(這些查詢串的重複度比較高,雖然總數是100億,但若是除去重複後,不超過10億個。一個查詢串的重複度越高,說明查詢它的用戶越多,也就是越熱門),請你統計最熱門的10個查詢串。[我仍是稍微修改了題目]
看見這個題目,我以爲我寫下去會對不起July費了萬千腦細胞辛苦的寫做成果,儘管我心裏十分但願它跟上面那題同樣,但這多麼讓人不甘心又不盡興~
如今暫且不考慮優化的問題:這個題目無非就是統計查詢串的計數,而後排序,而後取出前10個。事實上,這個問題在不考慮細節上徹底能夠用上面的pig腳原本處理。
不寫了,以如今的水平繼續寫實在是不優美:
3題:有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。
4題:海量數據分佈在100臺電腦中,想個辦法高效統計出這批數據的TOP10。
5題:有10個文件,每一個文件1G,每一個文件的每一行存放的都是用戶的query,每一個文件的query均可能重複。要求你按照query的頻度排序。
7題:怎麼在海量數據中找出重複次數最多的一個?
8題:上千萬或上億數據(有重複),統計其中出現次數最多的前N個數據。
9題:一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞。
1,2,3,4,5,7,8,9題思路基本一致,值得注意的是,有時候咱們徹底能夠肯定咱們須要的數據的一些特徵,好比上面的熱門查詢中熱門串必定被查詢超過1000次,那麼咱們就可使用FILTER來進行過濾以減小處理的數據(從而減小對帶寬的壓力)[filted_records = FILTER grouped_records BY SIZE(records) > 1000;]。
6題: 給定a、b兩個文件,各存放50億個url,每一個url各佔64字節,內存限制是4G,讓你找出a、b文件共同的url?
見
[Hadoop]使用DistributedCache進行復制聯結
以及
使用hadoop的datajoin包進行關係型join操做,你也能夠參考Data-Intensive Text Processing with MapReduce看看原生態的join操做是怎麼進行的。
- grunt> A = LOAD 'input/url1' AS (url:chararray);
- grunt> B = LOAD 'input/url2' AS (url:chararray);
- grunt> grouped_A = GROUP A BY url;
- grunt> non_duplicated_A = FOREACH grouped_A GENERATE group; --去重
- grunt> grouped_B = GROUP B BY url;
- grunt> non_duplicated_B = FOREACH grouped_B GENERATE group; --B去重
- grunt> C = JOIN non_duplicated_B BY group, non_duplicated_A BY group; --A 、B 內聯結
- grunt> D = FOREACH C GENERATE $0; //生成重複url
- grunt> DUMP D;
10題: 1000萬字符串,其中有些是重複的,須要把重複的所有去掉,保留沒有重複的字符串。
使用pig:
- grunt> records = LOAD 'input/retrived_strings' AS (str:chararray);
- grunt> grouped_records = GROUP records BY str;
- grunt> filted_records = FILTER grouped_records BY SIZE(records) <= 1;
- grunt> DUMP filted_records;
今日在CSDN看再次碰見July的這篇博文:教你如何迅速秒殺掉:99%的海量數據處理面試題。 app
這篇文章我以前是拜讀過的,今天閒來沒事,就想拿來當作MapReduce的練習。 框架
MapReduce這把刀太大,刀大了問題就抵不住這刀鋒了,事實上一開始我想着,這麼多些題目,當是要花很多功夫的,但當我作完一題繼續看下面的題目的時候,才發現這些題目在MapReduce模型下顯得大同小異了,看來拿大刀的人是無論砍的是木頭仍是人頭的,而是直接抽象成柱形物而後掄起刀一刀就下去了。 grunt
直入主題: oop
一、海量日誌數據,提取出某日訪問百度次數最多的前K個IP。[稍微改變] 優化
說明:每一次訪問網頁就在日誌中記錄1次訪問者的IP,獨佔一行,一個小數據能夠在這裏下載。
實在是想不出如何能在一個Job中解決這個問題,因此仍是把它拉扯成了兩個Job來解決。
Job1:將相同IP的記錄合併,造成<ip,count>形式,其中count是對這個ip的計數。
Job2:按count排序<ip,count>並選擇前K個進行輸出。
這裏我寫了一個可序列化的類IPAndCount,若是稍微熟悉MapReduce或者看明白我以前寫的
關係型MapReduce模式:選擇、分組和組內排序
你就知道這是爲了排序而準備的。MapReduce有一個「Shuffle and sort」,這個階段是利用key來對tuple進行排序的,而排序時調用的即是key的compareTo()方法。事實上若是job1輸出的數據兩足夠小,咱們徹底能夠在內存中進行排序而利用MapReduce框架,這樣就能夠省下一個Reduce階段,可是對於這個問題顯然不行。
IPAndCount很直白,就是包裝了上述的<ip,count>。
- import java.io.DataInput;
- import java.io.DataOutput;
- import java.io.IOException;
-
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.io.WritableComparable;
-
-
- public class IPAndCount implements WritableComparable{
- Text ip;
- IntWritable count;
-
- public IPAndCount(){
- this.ip = new Text("");
- this.count = new IntWritable(1);
- }
-
- public IPAndCount(Text ip, IntWritable count){
- this.ip = ip;
- this.count = count;
- }
-
- public IPAndCount(String ip, int count){
- this.ip = new Text(ip);
- this.count = new IntWritable(count);
- }
-
- public void readFields(DataInput in) throws IOException {
- ip.readFields(in);
- count.readFields(in);
- }
-
- public void write(DataOutput out) throws IOException {
- ip.write(out);
- count.write(out);
- }
-
- public int compareTo(Object o) {
- return ((IPAndCount)o).count.compareTo(count) == 0?
- ip.compareTo(((IPAndCount)o).ip):((IPAndCount)o).count.compareTo(count);//若是隻比較count會丟失數據,應該是suffle階段的問題
- }
-
- public int hashCode(){
- return ip.hashCode();
- }
-
- public boolean equals(Object o){
- if(!(o instanceof IPAndCount))
- return false;
- IPAndCount other = (IPAndCount)o;
- return ip.equals(other.ip) && count.equals(other.count);
- }
-
- public String toString(){
- StringBuffer buf = new StringBuffer("[ip=");
- buf.append(ip.toString());
- buf.append(",count=");
- buf.append(count.toString());
- buf.append("]");
- return buf.toString();
- }
-
- public Text getIp() {
- return ip;
- }
- public void setIp(Text ip) {
- this.ip = ip;
- }
- public IntWritable getCount() {
- return count;
- }
- public void setCount(IntWritable count) {
- this.count = count;
- }
- }
下面對FindActiveIp進行說明:
SumUpIpMapper和SumUpIPReducer事實上就是一個MapReduce中最基礎的詞頻統計程序WordCount。你能夠加一個Combiner來優化一下,我遺漏了。
從配置中能夠看見兩個Job的配置:job 和job2。
依賴關係是job -> job2,代碼中使用了JobControl來解決做業間的依賴關係,JobControl.run()方法會在做業都運行完後才返回。
Job2的輸入路徑是Job1的輸出路徑,從參數中能夠看出這一點。
Job1的輸出在輸出文件中的表現是:ip,count
Job2再從文件中讀入,使用的是KeyValueTextInputFormat,它對應的是TextOutputFormat,咱們能夠從job1的配置中看出來。
BeforeSortIPMapper從job1的輸出中讀取數據幷包裝成IPAndCount類型,以便MapReduce框架在「shuffle and sort」階段利用它來排序。
最後SelectTopKIPReducer選出前K個進行輸出便可,在這裏咱們設置最後的reduce只有一個reduce task,以使全部數據匯聚到一臺機子上進行處理。
- import java.io.IOException;
-
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.conf.Configured;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapred.lib.ChainMapper;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.Mapper;
- import org.apache.hadoop.mapreduce.Reducer;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
- import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
- import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
- import org.apache.hadoop.mapreduce.lib.jobcontrol.JobControl;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
- import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
- import org.apache.hadoop.util.Tool;
- import org.apache.hadoop.util.ToolRunner;
-
-
- public class FindActiveIP extends Configured implements Tool{
-
- public static class SumUpIPMapper extends Mapper<LongWritable,Text,Text,IntWritable>{
- IntWritable one = new IntWritable(1);
- public void map(LongWritable key, Text value, Context context)
- throws IOException,InterruptedException{
- context.write(value, one);
- }
- }
-
- public static class SumUpIPReducer extends Reducer<Text,IntWritable,Text,IntWritable>{
- //這裏能夠選擇前k個進行輸出以優化
- public void reduce(Text key, Iterable<IntWritable> values, Context context)
- throws IOException, InterruptedException{
- int sum = 0;
- for(IntWritable v : values){
- sum += v.get();
- }
- context.write(key, new IntWritable(sum));
- }
- }
-
-
- public static class BeforeSortIPMapper extends Mapper<Text,Text,IPAndCount,Text>{
- public void map(Text key, Text value, Context context)
- throws IOException,InterruptedException{
- IPAndCount tmp = new IPAndCount(key,new IntWritable(Integer.valueOf(value.toString())));
- System.out.println(tmp);
- context.write(tmp,new Text());
- }
- }
-
-
- //set num of this reducer to one
- public static class SelectTopKIPReducer extends Reducer<IPAndCount,Text,IPAndCount,Text>{
- int counter = 0;
- int K = 10;
- public void reduce(IPAndCount key, Iterable<Text> values, Context context)
- throws IOException, InterruptedException{
- System.out.println(key);
- if(counter < K){
- context.write(key, null);
-
- counter++;
- }
-
- }
- }
- public int run(String[] args) throws Exception {
- Configuration conf = new Configuration();
- Job job = new Job(conf,"SumUpIP");
- job.setJarByClass(FindActiveIP.class);
- job.setInputFormatClass(TextInputFormat.class);
- job.setOutputFormatClass(TextOutputFormat.class);
- job.getConfiguration().set("mapred.textoutputformat.separator", ",");
- Path in = new Path(args[0]);
- Path out = new Path(args[1]);
- FileInputFormat.setInputPaths(job, in);
- FileOutputFormat.setOutputPath(job, out);
- job.setMapperClass(SumUpIPMapper.class);
- job.setReducerClass(SumUpIPReducer.class);
- job.setMapOutputKeyClass(Text.class);
- job.setMapOutputValueClass(IntWritable.class);
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(IntWritable.class);
- job.setNumReduceTasks(7);
-
- Configuration conf2 = new Configuration();
- Job job2 = new Job(conf2,"SortAndFindTopK");
- job2.setJarByClass(FindActiveIP.class);
- job2.setInputFormatClass(KeyValueTextInputFormat.class);
- job2.getConfiguration().set("mapreduce.input.keyvaluelinerecordreader.key.value.separator", ",");
- job2.setOutputFormatClass(TextOutputFormat.class);
- Path in2 = new Path(args[1]);
- Path out2 = new Path(args[2]);
- FileInputFormat.setInputPaths(job2,in2);
- FileOutputFormat.setOutputPath(job2, out2);
- job2.setMapperClass(BeforeSortIPMapper.class);
- job2.setReducerClass(SelectTopKIPReducer.class);
- job2.setMapOutputKeyClass(IPAndCount.class);
- job2.setMapOutputValueClass(Text.class);
- job2.setOutputKeyClass(IPAndCount.class);
- job2.setOutputValueClass(Text.class);
- job2.setNumReduceTasks(1);
-
- JobControl jobControl = new JobControl("FindTopKIP");
- ControlledJob cJob1 = new ControlledJob(conf);
- cJob1.setJob(job);
- ControlledJob cJob2 = new ControlledJob(conf2);
- cJob2.setJob(job2);
- jobControl.addJob(cJob1);
- jobControl.addJob(cJob2);
- cJob2.addDependingJob(cJob1);
- jobControl.run();
- return 0;
- }
-
- public static void main(String args[]) throws Exception{
- int res = ToolRunner.run(new FindActiveIP(), args);
- System.exit(res);
- }
-
- }
大刀拿習慣了,從前的大刀就成了如今的繡花針,不是繡花針很差,只是用着不順手。當你聽着歌用java寫着MapReduce,忽然有人在你耳邊喊了一句:Pig~Pig~Pig~
你很難不心動!程序員愛偷懶堪比女人愛逛街,都是爲了快樂啊~
下面是用Pig來處理上述的問題:
- grunt> records = LOAD 'input/ipdata' AS (ip:chararray);
- grunt> grouped_records = GROUP records BY ip;
- grunt> counted_records = FOREACH grouped_records GENERATE group, COUNT(records);
- grunt> sorted_records = ORDER counted_records BY $1 DESC;
- grunt> topK = LIMIT sorted_records 10;
- grunt> DUMP topK;
你看,數數,沒暈數一數,6行!僅僅6行就解決了。
行1:將文件裝入
行2:按ip分組
行3:組內計數
行4:組間按ip訪問計數排序
行5:選擇前10個數據
行6:運行並輸出。
雖然咱們的Pig方法實際上跑了3個job才完成任務,相比於java寫的MapReduce多了一個job,但Pig顯然更愉快些。
這是最後結果:
(192.168.0.1,1559)
(192.168.0.21,7)
(192.168.0.14,4)
(192.168.0.10,4)
(192.168.0.12,4)
(192.168.0.32,4)
(192.168.0.13,3)
(192.168.0.3,3)
(192.168.0.2,2)
(192.168.0.11,1)
這個算法對帶寬的壓力仍是比較大的,除了加一個Combiner以外,代碼中還提到了另外一個小小的優化在進入第一個Reduce階段的一個reduce task中的數據足以裝入內存時,這是很容易解決的。這不是多好的優化,應當有更加好的優化方式能過濾更多的數據,不然。。。這不科學~
二、尋找熱門查詢:搜索引擎會經過日誌文件把用戶每次檢索使用的全部檢索串都記錄下來,每一個查詢串的長度爲1-255字節。
假設目前有100億個記錄(這些查詢串的重複度比較高,雖然總數是100億,但若是除去重複後,不超過10億個。一個查詢串的重複度越高,說明查詢它的用戶越多,也就是越熱門),請你統計最熱門的10個查詢串。[我仍是稍微修改了題目]
看見這個題目,我以爲我寫下去會對不起July費了萬千腦細胞辛苦的寫做成果,儘管我心裏十分但願它跟上面那題同樣,但這多麼讓人不甘心又不盡興~
如今暫且不考慮優化的問題:這個題目無非就是統計查詢串的計數,而後排序,而後取出前10個。事實上,這個問題在不考慮細節上徹底能夠用上面的pig腳原本處理。
不寫了,以如今的水平繼續寫實在是不優美:
3題:有一個1G大小的一個文件,裏面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。
4題:海量數據分佈在100臺電腦中,想個辦法高效統計出這批數據的TOP10。
5題:有10個文件,每一個文件1G,每一個文件的每一行存放的都是用戶的query,每一個文件的query均可能重複。要求你按照query的頻度排序。
7題:怎麼在海量數據中找出重複次數最多的一個?
8題:上千萬或上億數據(有重複),統計其中出現次數最多的前N個數據。
9題:一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞。
1,2,3,4,5,7,8,9題思路基本一致,值得注意的是,有時候咱們徹底能夠肯定咱們須要的數據的一些特徵,好比上面的熱門查詢中熱門串必定被查詢超過1000次,那麼咱們就可使用FILTER來進行過濾以減小處理的數據(從而減小對帶寬的壓力)[filted_records = FILTER grouped_records BY SIZE(records) > 1000;]。
6題: 給定a、b兩個文件,各存放50億個url,每一個url各佔64字節,內存限制是4G,讓你找出a、b文件共同的url?
見
[Hadoop]使用DistributedCache進行復制聯結
以及
使用hadoop的datajoin包進行關係型join操做,你也能夠參考Data-Intensive Text Processing with MapReduce看看原生態的join操做是怎麼進行的。
- grunt> A = LOAD 'input/url1' AS (url:chararray);
- grunt> B = LOAD 'input/url2' AS (url:chararray);
- grunt> grouped_A = GROUP A BY url;
- grunt> non_duplicated_A = FOREACH grouped_A GENERATE group; --去重
- grunt> grouped_B = GROUP B BY url;
- grunt> non_duplicated_B = FOREACH grouped_B GENERATE group; --B去重
- grunt> C = JOIN non_duplicated_B BY group, non_duplicated_A BY group; --A 、B 內聯結
- grunt> D = FOREACH C GENERATE $0; //生成重複url
- grunt> DUMP D;
10題: 1000萬字符串,其中有些是重複的,須要把重複的所有去掉,保留沒有重複的字符串。
使用pig:
- grunt> records = LOAD 'input/retrived_strings' AS (str:chararray);
- grunt> grouped_records = GROUP records BY str;
- grunt> filted_records = FILTER grouped_records BY SIZE(records) <= 1;
- grunt> DUMP filted_records;