在這篇論文之前 — 它所降落的世界
到了 2003 年,Google 內部已有上百個特製運算,處理爬取的網頁、網站請求日誌與網頁連結圖,產出反向索引、各主機的頁面數統計,以及當日最熱門查詢的摘要。這些運算在概念上都很單純,但輸入資料大到必須分散到數百甚至數千台機器上才能在合理時間內跑完,於是每支程式都自己手寫一套切分、工作分配與故障復原的程式碼,把原本簡單的邏輯整個淹沒掉。硬體條件只讓事情更難:雙處理器的商用 x86 Linux 機器,2 至 4 GB 記憶體,掛的是便宜的 IDE 磁碟,單機網路是 100 Mb/s 到 1 Gb/s,但整體對分頻寬(bisection bandwidth)遠低於此,而叢集規模大到機器故障是家常便飯而非例外。當時可用的平行抽象,如 MPI、Bulk Synchronous Programming 與 parallel prefix 類模型,確實提高了表達層次,但多半只在小得多的規模上實作過,而且把機器故障丟回給程式設計師處理。前一年發表的 GFS 剛好讓人可以放心假設有一套複本化的檔案系統鋪在同一批本機磁碟上,這正是 MapReduce 賴以站立的底層。
術語 — 依本篇論文的用法
- Map
- 使用者撰寫的函式,接受一組輸入鍵值對,產生一批中間鍵值對,型別為 map (k1,v1) 到 list(k2,v2)。其輸出先緩衝在記憶體中,再落到該 worker 的本機磁碟。
- Reduce
- 使用者撰寫的函式,接受一個中間鍵與該鍵所有值的 iterator,把它們合併成一組可能更小的值。通常每次呼叫只產生零個或一個輸出值。
- Master
- 唯一一份不是 worker 的程式副本:負責把 map 與 reduce 任務指派給閒置的 worker、追蹤任務狀態,並把中間檔案區段的位置從 map 端轉送到 reduce 端。它同時也是故障偵測者與整個系統的單點故障。
- Worker
- 使用者程式的一般副本,執行 master 指派給它的 map 或 reduce 任務。master 會週期性地 ping 它們,一旦停止回應就把其任務重新指派出去。
- M 與 R
- 輸入分片數(即 map 任務數)與輸出分割數(即 reduce 任務數)。兩者都取得遠大於機器數,M 通常讓每個 map 任務涵蓋 16 至 64 MB,R 則取預期 worker 數的一個小倍數。
- 分割函式(partitioning function)
- 作用在中間鍵上、決定一筆記錄屬於 R 個 reduce 任務中哪一個的函式,預設為 hash(key) mod R。使用者可以自訂,例如只對 URL 鍵中的主機名稱做雜湊,讓同一主機的所有項目落在同一個輸出檔。
- Combiner
- 選用的函式,在 map 所在機器上先把相同鍵的中間記錄做部分合併,再送上網路。適用於 reduce 函式滿足交換律與結合律的情況,通常與 reducer 共用同一份程式碼。
- Straggler
- 在最後幾個 map 或 reduce 任務上花費異常久的機器,原因可能是磁碟即將損壞、機器上被排了其他任務造成資源競爭,或硬體與設定的錯誤。straggler 是拉長工作完成時間的主要元凶之一。
- 備份任務(backup task)
- 當運算接近完成時,master 為仍在進行中的任務額外排一份的重複執行。主要執行與備份執行誰先完成,該任務就算完成,代價經調校後只佔幾個百分點的額外資源。