從範例來理解 outer baginner bag 的差異

先來看一段 pig 官網上的解釋

Relations, Bags, Tuples, Fields
Pig Latin statements work with relations. A relation can be defined as follows:
* A relation is a bag (more specifically, an outer bag).
* A bag is a collection of tuples.
* A tuple is an ordered set of fields.
* A field is a piece of data.
A Pig relation is a bag of tuples. A Pig relation is similar to a table in a relational database, where the tuples in the bag correspond to the rows in a table. Unlike a relational table, however, Pig relations don’t require that every tuple contain the same number of fields or that the fields in the same position (column) have the same type.
Also note that relations are unordered which means there is no guarantee that tuples are processed in any particular order. Furthermore, processing may be parallelized in which case tuples are not processed according to any total ordering.

這段解說特別強調, relation 是一個 “outer bag”, 所以, 既然有 outer bag ,那一定就有 inner bag。
那麼 bag 特地分成 innerouter, 到底有什麼差別呢?

先從定義上來解答


我們可以參考 stackoverflow 上的這篇討論:

what is the distinction between an ‘outer bag’ and an ‘inner bag’ in pigLatin?

簡單總結一下, outer bag is relation,就如同 pig 官網對於 relation 的解釋一樣,bag 就是一堆 tuple集合。說是集合,是要強調 tuplebag 內是<沒有順序>地被放在 bag 內。

那 inner bag 呢?

inner bag 在資料結構上就是一般的 bag,和 outer bag 一樣,也是由一堆無序的 tuple 構成。 一個 bag 之所以會是 inner bag 就在於它是存在於 relation 內, 就這麼單純。

所以, bag 都是用來描述一堆 tuple 資料, 當 bag 在最外層, 它就是 outer bag, 當 bag 被當成一個 field 放在 relation 內的話, 它就是 inner bag

ok… 就這樣嗎?

當然不可能, outer baginner bag 可不只是因為 bag 角色不一樣而給出不一樣的代名詞而已。在實際的 pig latin 應用場景上, outer baginner bag 有時可是讓人很頭疼的….

應用場景的差別


讓我們先參考 codelast 的這一段有趣範例 …

http://www.codelast.com/?p=3621 請參考(3)怎样统计数据行数

大意是,如果我們想計數一個 hadoop 上的檔案有幾筆資料(幾行),最直覺得方式就是用 COUNT()

A = load ‘a.txt’ ;
CNT = COUNT(A) ;

但, 這句卻 pig latin 會報錯誤訊息。令人不解的是, COUNT() 宣稱它可以用來計數 bag 內的 tuple 個數, 而 A 是個 relation, 也就是 outer bag, 應該要沒問題的…

但如果多一行 code, 先把 A 用 group 整理過, 就行了….

A = load ‘a.txt’ ;                                                 B = group A ALL ;                                                 CNT = foreach B generate COUNT(A) ;

上面這幾行可以再精簡成

A = load ‘a.txt’ ;                                               CNT = foreach ( group A ALL ) generate COUNT(A) ;
參考自 http://stackoverflow.com/questions/9900761/pig-how-to-count-a-number-of-rows-in-alias 

我想, 這個差異正好說明了 outer baginner bag 的不同。 outer bag (relation)pig 內是用於描述一份檔案, 一個資料源

A = load ‘a.txt’ ;

pig 執行階段, 它是 mapper

COUNT() 雖然是作用在 bag 上, 但請注意它的使用場景有個限制 —COUNT() 僅作用在 reducer 階段,而 inner bag 就是在 reducer 階段。說穿了, COUNT() 僅適用於一筆 row 上的某個 bag field。對應回 pig 執行階段, COUNT() 僅適用於處理 reducer 的產出。

A = load ‘a.txt’ ;  -- (1)                                          B = group A ALL ;  -- (2)                                                                  CNT = foreach B generate COUNT(A) ; -- (3)
(1) mapper, 將檔案分 block 載入, 產出 key 為 all, value 為 a.txt 的某一行送給 reducer
(2) reducer, 把所有 mapper 的產出導到同一個檔, mapper所有的產出都套用同一個 key = all, 所以 reducer 只會有一個檔
(3)COUNT() 只能作用在同一個 reducer 上.

從語言設計上, 不允許如下的程式片段其實是頗為合理的。

A = load ‘a.txt’ ;
CNT = COUNT(A) ;

怎麼說呢?

假設 count 要支援上面的與法, 那麼 COUNT() 的實作就必需橫跨 mapper/reducer,實作如下:

A = load ‘a.txt’ -- (1)
CNT = COUNT(A) ; -- (2)
(1) mapper 階段, 每個 mapper 都產出 “all” “1” 代表讀到一行
(2) 所有 key 為 “all” 的資料都導到同一個 reducer, 在這個 reducer 內可以把所有的資料掃過一次得知次數…

但身為設計者, COUNT() 是個 build-in function, 它一定只會作用特定的資料結構上, 這種橫跨 mapper/reducer 的方式是不切實際的, 我想這就是為什麼 pig 宣稱 COUNT() 做用在 bag 上但卻又不允許 COUNT(outer_bag) 這種用法的原因了..