海量数据处理(1):MapReduce

雨燕双飞 提交于 2020-01-10 03:48:35

海量数据处理(1):MapReduce

海量数据处理,就是基于海量数据的存储、删除、搜索等操作。由于数据量太大,导致要么无法在短时间内迅速处理,要么无法一次性装入内存。针对面试过程中的问题,主要有以下解决步骤

  • 针对时间,可以采用更加迅速的数据结构和算法,比如BloomFilter、Hash、堆、Bitmap等

  • 针对空间,无非就是大而化小,分而治之。

  • 算法方面:

    • 外排序算法(External Sorting)
    • Map Reduce
    • 非精确算法
    • 概率算法
    • 哈希算法与哈希函数(Hash Function)

    数据结构方面:

    • 哈希表(Hash Table)
    • 堆(Heap)
    • 布隆过滤器(BloomFilter)
    • 位图(Bitmap)

一、MapReduce简介

给定一个有海量单词的集合,统计每个词的出现次数,最简单的就是for循环,放进map,一台机器这样做,明显是不合适的,所以我们想到了用两台机器for循环,两台服务器去处理数据,但是到了merge环节,还是一台机器啊,这也是个瓶颈。

image-20200109140334227

合并时候也可以采用并行的方式,有两种选择

  • 一是以机器来划分(比如第i台到第j台由某台机器合并)
  • 二是根据key来划分(比如key由a、b、c、d,让一台机器合并key a和key b)

很显然以机器作为划分并不好,因为进行了第一轮合并了后,还要再进行合并…最后形成一个类似树状结构的,后面的机器得要等前面机器合并完了才能继续工作,依赖性较强。所以才用根据key来划分(没有前后依赖性,系统复杂度低),这就有了MapReduce。

image-20200109140833402

注意,上面的map中采用了合并,这实际上是不对的,如果要合并,需要hashmap,机器里开一个hashmap肯定是放不下的,map的主要任务是打乱,不做合并。

image-20200109141250219

二、Map函数和Reduce函数

Map函数和Reduce函数的输入输出必须是key-value的形式

  • Map 输入 key:文章存储地址,value:文章内容
  • Reduce 输入key:Map输出的key,Value:Map输出的value

LintCode 499.单词计数

实现map和reduce函数

//OutputCollector是辅助输出的工具
class OutputCollector<K, V> {
    public void collect(K key, V value) {

    }
}
public  class WordCount {

    public static class Map {
        //value是文章
        public void map(String key, String value, OutputCollector<String, Integer> output) {
            StringTokenizer tokenizer = new StringTokenizer(value);
            while (tokenizer.hasMoreTokens()) {
                String outputKey = tokenizer.nextToken();
                output.collect(outputKey, 1);   //不做merge
            }
        }
    }

    public static class Reduce {
        //key和value都是map输出的,某个key出现n此就有n个1,只要把Iterator中所有的1加起来即可
        public void reduce(String key, Iterator<Integer> values, OutputCollector<String, Integer> output) {
            int sum = 0;
            while (values.hasNext()) {
                sum += values.next();
            }
            output.collect(key, sum);
        }
    }
}

三、传输整理

在Map到Reduce的过程中有个传输整理,这个流程如图所示

image-20200109142649235

Partition和Sort就是传输整理,在这个过程中ab分为一组,cd分为一组,这个分组是由master的一致性hash来进行分组,由于分组存在硬盘上,采用外部排序。排序时key作为第一关键字,value作为第二关键字。接下来是Reduce的工作,通过Fetch把排好序的文件拿到自己的机器上,并对这些文件进行k路归并成Reduce的输入。

image-20200109143009331

下面介绍两个应用

四、利用MapReduce建立倒排索引

正排:序号->关键字,倒排:给定文章关键词,返回文章序号,就比如google上搜索关键词,返回网页。

image-20200109143417433

map输出的key是文章的关键词,value是文章的编号。在reduce中需要进行去重操作

image-20200109143559740

LintCode 504. 倒排索引

//OutputCollector是辅助输出的工具
class OutputCollector<K, V> {
    public void collect(K key, V value) {

    }
}
class Document {
    public int id;
    public String content;
}
public class InvertedIndex {

    public static class Map {
        // value是对应文章的id
        public void map(String _, Document value, OutputCollector<String, Integer> output) {
            StringTokenizer tokenizer = new StringTokenizer(value.content);
            while (tokenizer.hasMoreTokens()) {
                String word = tokenizer.nextToken();
                output.collect(word, value.id);
            }
        }
    }

    public static class Reduce {
        public void reduce(String key, Iterator<Integer> values, OutputCollector<String, List<Integer>> output) {
            ArrayList<Integer> res = new ArrayList<>();
            int left = -1;  //用于去重
            while (values.hasNext()) {
                Integer now = values.next();
                if (now != left) {
                    res.add(now);
                }
                left = now;
            }
            output.collect(key, res);   //key->包含该key的文章
        }
    }
}

五、利用MapReduce对Anagram进行分组

Anagram的特性就是能够随意改变单词中字母顺序,如果把内部字母排个序,那么只要是Anagram就会变成同一个单词。经过内部排序的单词就能够作为key,而原单词就是value。

image-20200109145006642

LintCode 503. 乱序字符串

//OutputCollector是辅助输出的工具
class OutputCollector<K, V> {
    public void collect(K key, V value) {

    }
}
public class Anagram {

    public static class Map {
        public void map(String key, String value, OutputCollector<String, String> output) {
            StringTokenizer tokenizer = new StringTokenizer(value);
            while (tokenizer.hasMoreTokens()) {
                String word = tokenizer.nextToken();
                char[] chs = word.toCharArray();
                Arrays.sort(chs);
                output.collect(new String(chs), word);
            }
        }
    }

    public static class Reduce {
        public void reduce(String key, Iterator<String> values, OutputCollector<String, List<String>> output) {
            List<String> res = new ArrayList<>();
            while (values.hasNext()) {
                res.add(values.next());
            }
            output.collect(key, res);
        }
    }
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!