MapReduce(期末速成版)

起初在B站看3分鐘的速成視頻,感覺很多細節沒聽懂。


具體例子解析(文件內容去重)

對于兩個輸入文件,即文件A 和文件B,請編寫MapReduce 程序,對兩個文件進行合并,并剔除
其中重復的內容,得到一個新的輸出文件C。

📂 一、輸入數據文件

文件 A:

20150101 x
20150102 y
20150103 x
20150104 y
20150105 z
20150106 x

文件 B:

20150101 y
20150102 y
20150103 x
20150104 z
20150105 y

🧠 二、MapReduce 執行流程和中間結果

MapReduce 分為三個主要階段:

  1. Map 階段

  2. Shuffle(分組 & 排序)階段

  3. Reduce 階段


🔹2.1 Map 階段(映射階段)

我們先來看下 Mapper 的代碼邏輯:

public static class Map extends Mapper<Object, Text, Text, Text> {private static Text text = new Text();public void map(Object key, Text value, Context context) {text = value;context.write(text, new Text(""));}
}
🔍 Mapper 做了什么?
  • 每行文本被視為一個輸入記錄(value),key 是字節偏移量(無關緊要)。

  • Mapper 不對數據做任何處理,直接原樣輸出 value 作為 key,并給定空字符串作為 value

  • 這樣,相同行的數據(A、B 中相同的行)會生成相同的 key,從而可以在 Shuffle 階段合并。


🔢 Map 輸出結果(中間鍵值對)

我們對 A、B 兩個文件的所有行執行一次 map() 操作,得到如下中間結果(<key, value> 形式):

來源key(Text)value(Text)
A20150101 x""
A20150102 y""
A20150103 x""
A20150104 y""
A20150105 z""
A20150106 x""
B20150101 y""
B20150102 y""
B20150103 x""
B20150104 z""
B20150105 y""

🔹2.2 Shuffle 階段(分組 & 排序)

MapReduce 框架自動完成以下操作:

  • 將所有 Mapper 輸出結果根據 key 進行哈希分區、排序、去重分組

  • 每一個唯一的 key 會被送入一次 Reducer。

🎯 分組結果(Reducer 接收到的 key 和 values):
key(唯一行)values("" 的列表)
20150101 x["",]
20150101 y["",]
20150102 y["", ""]
20150103 x["", ""]
20150104 y["",]
20150104 z["",]
20150105 y["",]
20150105 z["",]
20150106 x["",]

?? 注意:

  • 20150102 y20150103 x 都在兩個文件中出現了,所以它們的 values 有兩個空字符串。

  • 但 Reducer 并不關心這些 values,它只輸出唯一的 key


🔹2.3 Reduce 階段(歸約階段)

看一下 Reducer 的代碼:

public static class Reduce extends Reducer<Text, Text, Text, Text> {public void reduce(Text key, Iterable<Text> values, Context context) {context.write(key, new Text(""));}
}
🔍 Reducer 做了什么?
  • 對于每一個唯一的 key,Reducer 被調用一次。

  • 忽略 values,直接輸出 key 和空的 Text("")

  • 實際效果是:只輸出不重復的唯一行內容


? 最終輸出文件 C 的內容:

20150101 x
20150101 y
20150102 y
20150103 x
20150104 y
20150104 z
20150105 y
20150105 z
20150106 x

? 總結

步驟說明
Map對輸入的每一行輸出 <Text(該行), Text("")>
Shuffle根據行內容去重、分組、排序
Reduce忽略 values,只輸出唯一的 key(行內容)
輸出文件文件 A 和 B 合并去重后的內容

問題一:Reduce端是如何輸出文件的?

? Reduce中context.write(key, value) 的行為

在 Hadoop MapReduce 中:

context.write(new Text("s"), new Text("a"));

的輸出行為是:

  • 每一行輸出格式為:

    key \t value
    

    即,key 和 value 之間用一個制表符(Tab 字符 \t)分隔


🔍 所以你舉的例子

context.write(new Text("s"), new Text("a"));

最終輸出文件中的一行會是:

s	a

不是 sa,而是 sa 之間有一個 Tab 分隔符。


🔧 那么在你的代碼中:

context.write(key, new Text(""));

由于 value 是空字符串,所以每一行就是:

key

即沒有顯示的 value,只輸出 key 的內容,所以:

20150101 x

這行實際上是 key 的原樣內容,不是 key + "" 的拼接結果,而是 key 后面雖然有個空字符串作為 value,但由于 value 是空的,輸出就是 key 自身。


?? 補充:可以自定義 key 和 value 的分隔符

在 Hadoop 中默認的 key/value 分隔符是 Tab(\t),但你可以通過設置:

-D mapreduce.output.textoutputformat.separator=","

來更改分隔符,比如改成逗號,則輸出會變成:

s,a


? 是不是有幾個 Reducer 就會產生幾個輸出文件?

是的,完全正確。

在 Hadoop MapReduce 中:

  • 如果你設置了 N 個 Reducer 任務(比如 job.setNumReduceTasks(N)),

  • 那么就會產生 N 個輸出文件

這些輸出文件的名稱通常是:

part-r-00000
part-r-00001
...
part-r-00(N-1)

每個文件由一個 Reducer 任務寫出。


? 那這些輸出文件的內容一致嗎?

不一致!每個文件的內容不同!

? 原因:

  • MapReduce 框架會按照 key 的 hash 值把數據**分區(Partition)**給不同的 Reducer。

  • 每個 Reducer 只處理自己分到的 key 分區。

  • 所以:

    • 每個輸出文件包含的是不同部分的 key-value 對

    • 輸出文件之間是不重合的,也就是說每個 key 只會出現在一個 Reducer 的輸出文件中


🧠 舉個例子(比如有 2 個 Reducer):

假設你有以下中間 key:

20150101 x
20150101 y
20150102 y
20150103 x
20150104 y
20150104 z
20150105 y
20150105 z
20150106 x
Hadoop 會通過 partitioner(比如默認的 HashPartitioner)決定:
  • Reducer 0 處理:

    20150101 x
    20150103 x
    20150105 z
    20150106 x
    
  • Reducer 1 處理:

    20150101 y
    20150102 y
    20150104 y
    20150104 z
    20150105 y
    

然后產生:

  • part-r-00000 ← Reducer 0 寫入

  • part-r-00001 ← Reducer 1 寫入

兩個文件中的內容互不重復,但合起來是完整的結果。



? 1. 默認是不是只有一個 Reducer?

是的,默認情況下 Hadoop MapReduce 只使用 1 個 Reducer。

也就是說,如果你沒有顯式調用:

job.setNumReduceTasks(N);

則默認 N = 1,最終只會生成一個輸出文件:part-r-00000


? 2. 可以設置多個 Reducer 嗎?

當然可以,而且非常常見。

你可以在驅動代碼中顯式設置 Reducer 個數,例如設置為 3:

job.setNumReduceTasks(3);

這樣 Hadoop 會啟用 3 個 Reducer 并行處理數據,輸出三個文件:

part-r-00000
part-r-00001
part-r-00002

問題二:Shuffle過程的輸出結果與Combiner函數本質是?


? 一、Shuffle 輸出是啥?

默認情況下:
Map 階段的輸出會經過 Shuffle(排序 + 分區 + 組裝) 后變成:

key1 → [v1, v2, v3, ...]
key2 → [v1, v2, ...]
...

這些最終被送入 Reducer。


? 問題:為什么會有重復的 value?

因為 同一個 key 可能在同一個 Mapper 中出現多次,比如:

hello → 1
hello → 1
hello → 1

這些數據在傳輸前就可以局部聚合,先加一加再傳過去,不用浪費網絡帶寬


? 二、Combiner 是什么?

Combiner 就是一個 “局部 Reduce”,在 Mapper 端執行,用來提前聚合。

它的作用是:

  • 在 Mapper 本地 就先對 key 進行累加(或合并),

  • 減少大量重復的 <key, 1> 傳給 Reducer,

  • 降低網絡傳輸壓力,提升性能。


? 三、怎么寫一個 Combiner?

👉 很簡單,其實你可以直接 復用 Reducer 邏輯,只要滿足:聚合操作是可交換和結合的(比如加法)

? 1. 定義 Combiner 類(和 Reducer 一樣):

public static class IntSumCombiner extends Reducer<Text, IntWritable, Text, IntWritable> {private IntWritable result = new IntWritable();public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {int sum = 0;for (IntWritable val : values) {sum += val.get();}result.set(sum);context.write(key, result);  // 輸出 key sum}
}

? 2. 在 Driver 中設置:

job.setCombinerClass(IntSumCombiner.class);

?? 注意:你也可以直接寫成:

job.setCombinerClass(IntSumReducer.class);

因為本質一樣(統計加法是符合條件的操作)。


? 四、添加了 Combiner 后的數據流是什么樣?

假設有兩個 Map 輸出如下:

Mapper1 輸出:

hello → 1
hello → 1
world → 1

經過 Combiner:

hello → 2
world → 1

Mapper2 輸出:

hello → 1
world → 1

經過 Combiner:

hello → 1
world → 1

最終 Shuffle 輸出給 Reducer:

hello → [2, 1]
world → [1, 1]

Reducer 再聚合:

hello → 3
world → 2

? 五、什么時候不要用 Combiner?

雖然 Combiner 很有用,但它不是 always-safe 的,只有在滿足可交換、可結合的前提下才可用。

操作類型適合使用 Combiner?示例
加法、計數、最大最小值? 可以用WordCount、MaxTemperature
求平均、TopN、排序? 不建議平均值不能分區計算后再平均

? 所以完整流程是:

Map → Combiner → Shuffle(聚合 + 分區)→ Reduce

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/82532.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/82532.shtml
英文地址,請注明出處:http://en.pswp.cn/web/82532.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java高級 | 【實驗四】Springboot 獲取前端數據與返回Json數據

隸屬文章&#xff1a; Java高級 | &#xff08;二十二&#xff09;Java常用類庫-CSDN博客 系列文章&#xff1a; Java高級 | 【實驗一】Spring Boot安裝及測試 最新-CSDN博客 Java高級 | 【實驗二】Springboot 控制器類相關注解知識-CSDN博客 Java高級 | 【實驗三】Springboot …

從零打造AI面試系統全棧開發

&#x1f916; AI面試系統開發完整教程 &#x1f4cb; 項目概述 本教程將帶你從零開始構建一個完整的AI面試系統&#xff0c;包含前端、后端、AI集成和部署的全流程。 源碼地址 技術棧 前端: React TypeScript Vite Vaadin Components后端: Spring Boot Spring Securi…

【硬件】PCIe協議 | 電腦的高速公路

文章目錄 PCIe | 外圍設備高速互聯通道&#xff08;peripheral component interconnect express&#xff09;的核心概念和應用 基礎概念 1.1 電腦內的”高速“&#xff0c;連接CPU、顯卡、SSD&#xff08;固態硬盤&#xff09;等核心組件&#xff1b;數據傳輸速度極快&#xff…

【 Redis | 完結篇 緩存優化 】

前言&#xff1a;本節包含常見redis緩存問題&#xff0c;包含緩存一致性問題&#xff0c;緩存雪崩&#xff0c;緩存穿透&#xff0c;緩存擊穿問題及其解決方案 1. 緩存一致性 我們先看下目前企業用的最多的緩存模型。緩存的通用模型有三種&#xff1a; 緩存模型解釋Cache Asi…

MySQL訪問控制與賬號管理:原理、技術與最佳實踐

MySQL的安全體系建立在精細的訪問控制和賬號管理機制上。本文基于MySQL 9.3官方文檔,深入解析其核心原理、關鍵技術、實用技巧和行業最佳實踐。 一、訪問控制核心原理:雙重驗證機制 連接驗證 (Connection Verification) 客戶端發起連接時,MySQL依據user_name@host_name組合進…

Go語言爬蟲系列教程4:使用正則表達式解析HTML內容

Go語言爬蟲系列教程4&#xff1a;使用正則表達式解析HTML內容 正則表達式&#xff08;Regular Expression&#xff0c;簡稱RegEx&#xff09;是處理文本數據的利器。在網絡爬蟲中&#xff0c;我們經常需要從HTML頁面中提取特定的信息&#xff0c;正則表達式就像一個智能的&quo…

筆記 | docker構建失敗

筆記 | docker構建失敗 構建報錯LOG1 rootThinkPad-FLY:/mnt/e/02-docker/ubunutu-vm# docker build -t ubuntu16.04:v1 . [] Building 714.5s (6/11) docker:default> [internal] load …

CentOS 7.9 安裝 寶塔面板

在 CentOS 7.9 上安裝 寶塔面板&#xff08;BT Panel&#xff09; 的完整步驟如下&#xff1a; 1. 準備工作 系統要求&#xff1a; CentOS 7.x&#xff08;推薦 7.9&#xff09;內存 ≥ 1GB&#xff08;建議 2GB&#xff09;硬盤 ≥ 20GBroot 權限&#xff08;需使用 root 用戶…

第 86 場周賽:矩陣中的幻方、鑰匙和房間、將數組拆分成斐波那契序列、猜猜這個單詞

Q1、[中等] 矩陣中的幻方 1、題目描述 3 x 3 的幻方是一個填充有 從 1 到 9 的不同數字的 3 x 3 矩陣&#xff0c;其中每行&#xff0c;每列以及兩條對角線上的各數之和都相等。 給定一個由整數組成的row x col 的 grid&#xff0c;其中有多少個 3 3 的 “幻方” 子矩陣&am…

【AI News | 20250604】每日AI進展

AI Repos 1、jaaz Jaaz是一款免費開源的AI設計代理&#xff0c;作為Lovart的本地替代品&#xff0c;它能實現圖像、海報、故事板的設計、編輯和生成。Jaaz集成了LLM&#xff0c;可智能生成提示并批量生成圖像&#xff0c;支持Ollama、Stable Diffusion等本地及API模型。用戶可…

Docker load 后鏡像名稱為空問題的解決方案

在使用 docker load命令從存檔文件中加載Docker鏡像時&#xff0c;有時會遇到鏡像名稱為空的情況。這種情況通常是由于在保存鏡像時未正確標記鏡像名稱和標簽&#xff0c;或者在加載鏡像時出現了意外情況。本文將介紹如何診斷和解決這一問題。 一、問題描述 當使用 docker lo…

SQL進階之旅 Day 14:數據透視與行列轉換技巧

【SQL進階之旅 Day 14】數據透視與行列轉換技巧 開篇 歡迎來到“SQL進階之旅”系列的第14天&#xff01;今天我們將探討數據透視與行列轉換技巧&#xff0c;這是數據分析和報表生成中的核心技能。無論你是數據庫開發工程師、數據分析師還是后端開發人員&#xff0c;行轉列或列…

haribote原型系統改進方向

在時鐘中斷、計時器和鍵盤輸入方面&#xff0c;一些創新性的改進方向&#xff1a; 時鐘中斷 (PIT / inthandler20) 動態節拍 (Tickless Kernel)&#xff1a;當前的 PIT 中斷以固定頻率&#xff08;約 100Hz&#xff09;觸發&#xff0c;即使系統空閑或沒有即將到期的計時器&…

LabVIEW基于 DataSocket從 OPC 服務器讀取數據

LabVIEW 中基于 DataSocket 函數從 OPC 服務器讀取數據的功能&#xff0c;為工業自動化等場景下的數據交互提供了解決方案。通過特定函數實現 URL 指定、連接建立與管理、數據讀取&#xff0c;相比傳統 Socket 通信和 RESTful API &#xff0c;在 OPC 服務器數據交互場景有適配…

SimpleDateFormat 和 DateTimeFormatter 的異同

在Java開發中Date類型轉String類型是比較常見的&#xff0c;其中最常用的是以下幾種方式&#xff1a; 1. 使用SimpleDateFormat&#xff08;Java 8之前&#xff09; import java.text.SimpleDateFormat; import java.util.Date;public class DateToStringExample {public sta…

《前端面試題:CSS對瀏覽器兼容性》

CSS瀏覽器兼容性完全指南&#xff1a;從原理到實戰 跨瀏覽器兼容性是前端開發的核心挑戰&#xff0c;也是面試中的高頻考點。查看所有css屬性對各個瀏覽器兼容網站&#xff1a;https://caniuse.com 一、瀏覽器兼容性為何如此重要&#xff1f; 在當今多瀏覽器生態中&#xff0c…

【stm32開發板】單片機最小系統原理圖設計

一、批量添加網絡標簽 可以選擇浮動工具中的N&#xff0c;單獨為引腳添加網絡標簽。 當芯片引腳非常多的時候&#xff0c;選中芯片&#xff0c;右鍵選擇扇出網絡標簽/非連接標識 按住ctrl鍵即可選中多個引腳 點擊將引腳名稱填入網絡名 就完成了引腳標簽的批量添加 二、電源引…

golang連接sm3認證加密(app)

文章目錄 環境文檔用途詳細信息 環境 系統平臺&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 文檔用途 golang連接安全版sm3認證加密數據庫,驅動程序詳見附件。 詳細信息 1.下載Linux golang安裝包 go1.17.3.linux-amd64.tar.gz 1.1. 解壓安…

node實例應用

打開vscode,創建node項目,直接進入一個干凈的文件夾&#xff0c;打開控制臺 一 項目初始化 1. 初始化包管理 npm init -y2. 安裝express npm install express4.17.1 3. 根目錄下創建app.js,引入express // 引入expree const express require(express)// 創建實例 const …

Springboot——整合websocket并根據type區別處理

文章目錄 前言架構思想項目結構代碼實現依賴引入自定義注解定義具體的處理類定義 TypeAWebSocketHandler定義 TypeBWebSocketHandler 定義路由處理類配置類&#xff0c;綁定point制定前端頁面編寫測試接口方便跳轉進入前端頁面 測試驗證結語 前言 之前寫過一篇類似的博客&…