0-8 MapReduce

MapReduce

框架#

  1. Input

  2. Split

  3. Map

  4. 传输整理

  5. Reduce

  6. Output

Map Reduce 函数的输入输出接口?

Map Reduce 函数的输入输出接口必须是 key, value 形式.

分片
分片
分片
磁盘外排序(分片排序 + k 路归并)
磁盘外排序(分片排序 + k 路归并)
file
Map
Map
Map
Reduce
Reduce
END

单词计数#

MapReduce 方法来处理单词, Map 过程就相当于把任务打散, 分配到各个机器上. Reduce 过程就是把 Map 结果合并.

比如单词计数这道题, Map 过程就是利用多台机器, 将文件切分分配到各个 Map 机器上来进行排序. 而 Reduce 过程有两种:

一种是按照机器数目来进行合并, 比如 1~10 号 Map 机器结果交给 1 号 Reduce 机器来完成, 11~20 号 Map 机器结构交给 2 号 Reduce 机器来完成.

一种是按照单词来合并, 通过将单词哈希, 所有相同哈希值的单词都被同一台机器合并, 不同 Reduce 机器负责不同范围的哈希值.

哪种更好呢?

第二种按照哈希值来合并更好, 因为第一种按照机器数目的合并就像神经网络一样, 会有很多层, Map-Reduce-Reduce.. 这样就会导致下一层的任务必须等待上一层任务结束后再开始. 而且合并的任务也更加繁重.

而第二种方式 Reduce 任务不需要等待 Map 任务完全结束再开始, 可以一边进行 Map 任务, 一边将 Map 任务的结果交给 Reduce 任务, 这样节约很多时间. 而且最后合并任务只需要最后归并排序即可, 不需要像第一种那样最后还需要再合并一次才能归并排序.

输入输出

type output struct {
    word string
    times int
}

// 结果通过网络传输给 Reduce 机器
func Map(key fileaddress, value content) []output {
    for {
        ... // 处理, 存放到内存
        // 内存快存到上限了
        return outputs
    }
}

func Reduce(mapOp []output) []output {
    for {
        ... // 合并
    }   
    return outputs
}

机器越多越好么?

key 的数目就是 Reduce 机器的上限.

倒排索引#

倒排索引就好像搜索引擎关键词搜索. 一般来说一个文章会有自己的关键词, 叫正排索引. 倒排索引就是通过关键词来找文章.

MapReduce 任务: 给定文章的正排索引, 输出文章的倒排索引.

func Map(fileid string, words []string) (word, fileid string) {
    ...
}

type reduceHelper struct {
    output map[string][]string
}

func Reducee(word, fileid string) {
    output[word] = append(output[word], fileid)
}

Top K 问题#

type pair struct {
    Word  string
    Times int
}

type PairHeap []pair

// 构建小顶堆
func (h PairHeap) compare(a, b pair) bool {
    if a.Times != b.Times {
        return a.Times < b.Times
    }
    return a.Word < b.Word
}

func (h PairHeap) Len() int {
    return len(h)
}

func (h PairHeap) Less(i, j int) bool {
    // 左<右, 小顶堆; 左>右, 大顶堆
    return h.compare(h[i], h[j])
}

func (h PairHeap) Swap(i, j int) {
    h[i], h[j] = h[j], h[i]
}

func (h *PairHeap) Push(x interface{}) {
    *h = append(*h, x.(pair))
}

func (h *PairHeap) Pop() interface{} {
    x := (*h)[len(*h)-1]
    *h = (*h)[:len(*h)-1]
    return x
}

func Map(fileid int, words []string) []pair {
    var (
        res   = make(map[string]int)
        pairs = make([]pair, 0)
    )
    for _, word := range words {
        res[word]++
    }
    for word, times := range res {
        pairs = append(pairs, pair{Word: word, Times: times})
    }
    return pairs
}

// return Top K pairs
func Reduce(pairs []pair, k int) []pair {
    h := &PairHeap{}
    heap.Init(h) // initialize
    for i := 0; i < len(pairs); i++ {
        if h.Len() < k {
            heap.Push(h, pairs[i])
            continue
        }
        // compare to the top element of heap
        if !h.compare(pairs[i], pairs[0]) {
            heap.Pop(h)
            heap.Push(h, pairs[i])
        }
    }
    return []pair(*h) // 返回值为从小到大
}

首先使用 Map 来从文件中提取单词, 并提取词频(可选)

接着将相同 key 的单词交给同一个 Reduce, 然后让 Reduce 维护一个小顶堆. 如果当前值小于堆顶值, 那么直接丢弃. 如果当前值大于堆顶值, 那么先将堆顶值弹出(堆顶与堆尾交换 + 堆顶下沉), 然后再将当前值推入堆中(放入堆尾 + 堆尾上浮).

这样用小顶堆 Reduce 可以减少内存使用.

分片
分片
分片
小顶堆取 Top K
小顶堆取 Top K
file
Map
Map
Map
Reduce
Reduce
END

MapReduce 框架设计#

见 MIT 6.824 的课程.

Reduce 一个机器 Key 数目特别多怎么办

加一个随机数前缀, 类似 Sharding Key. 最后合并的时候写一个脚本来去掉前缀就可以.

作者:Kohn

出处:https://www.cnblogs.com/geraldkohn/p/17091093.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   kohn  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示