Why Pig Latin
業(yè)界對(duì)于大數(shù)據(jù)的分析的需求越來(lái)越多,特別是那些依賴對(duì)TB級(jí)別的大數(shù)據(jù)進(jìn)行分析,從而進(jìn)行創(chuàng)新的互聯(lián)網(wǎng)公司。TeraData之類的公司雖然提供了解決方案,但是如果數(shù)據(jù)量級(jí)達(dá)到TB水平,價(jià)格就太貴了。另一方面,很多從事數(shù)據(jù)分析的人員都是傳統(tǒng)的“面向過(guò)程式編程”的程序員,對(duì)于他們來(lái)說(shuō)利用SQL這種工具來(lái)進(jìn)行數(shù)據(jù)分析會(huì)很不習(xí)慣。
但是說(shuō)實(shí)話,現(xiàn)在最新的理念是人人都可以做數(shù)據(jù)分析,人人都要有數(shù)據(jù)分析的能力。因此相比過(guò)程式的語(yǔ)言,SQL這種語(yǔ)言反而接受程度更高。原作者(們)之所以會(huì)有這樣的想法,跟這篇論文寫(xiě)作的時(shí)間有關(guān),這篇論文是寫(xiě)于2009年,那個(gè)時(shí)候從事數(shù)據(jù)分析的確實(shí)主要是“面向過(guò)程式編程”的程序員。
不過(guò)現(xiàn)在如果讓那些"面向過(guò)程式編程"的程序員來(lái)做數(shù)據(jù)分析,可能還是過(guò)程式的語(yǔ)言用起來(lái)更順手一點(diǎn)。也就是說(shuō)Pig應(yīng)該是面向?qū)I(yè)的數(shù)據(jù)分析用戶的。但是SQL也有它的市場(chǎng)。
但是現(xiàn)在最新的趨勢(shì)其實(shí)是讓用戶連SQL都不用會(huì),就可以直接進(jìn)行數(shù)據(jù)分析。只能說(shuō)時(shí)代在進(jìn)步,對(duì)于數(shù)據(jù)處理工具的要求在提高。當(dāng)數(shù)據(jù)真的變成經(jīng)濟(jì)發(fā)展的基本要素的時(shí)候,確實(shí)不能要求使用的人非要會(huì)一個(gè)什么Pig, 什么SQL之類的。
另一種選擇是直接使用類似map-reduce這種面向過(guò)程的分布式編程框架(Hadoop)來(lái)進(jìn)行數(shù)據(jù)分析。
這種方式的優(yōu)點(diǎn)就是“過(guò)程式”的,非常的自由,可以表達(dá)各種計(jì)算邏輯。缺點(diǎn)也在于它太自由了,導(dǎo)致它的表達(dá)力太差,即使對(duì)于那種非常常見(jiàn)的分析需求比如"join", 要用map-reduce這種底層的編程框架來(lái)做,也需要自己寫(xiě)很多的代碼,非常的冗雜。
因此Yahoo!內(nèi)部提出了Pig Latin這種平衡了上述兩種方式優(yōu)點(diǎn)的這么一種新的大數(shù)據(jù)處理的語(yǔ)言: 它既提供高階的、聲明式的查詢?cè)Z(yǔ),同時(shí)又提供低階的、過(guò)程式的編程風(fēng)格。
Pig Latin長(zhǎng)啥樣
假設(shè)說(shuō)我們有一個(gè)表urls:
(url, category, pagerank)
我們想計(jì)算: 那些足夠大的category,計(jì)算那些有足夠高的pagerank的url的平均pagerank是多少, 如果用SQL表達(dá),大概是這樣的:
SELECT category, AVG(pagerank)
FROM urls WHERE pagerank > 0.2
GROUP BY category HAVING COUNT(*) > 106
而如果用Pig Latin來(lái)表達(dá)呢,是這樣的:
good_urls = FILTER urls BY pagerank > 0.2;
groups = GROUP good_urls BY category;
big_groups = FILTER groups BY COUNT(good_urls)>106;
output = FOREACH big_groups GENERATE category, AVG(good_urls.pagerank);
從這個(gè)例子可以看出Pig Latin的兩個(gè)基本特點(diǎn):
- 首先它是由一系列的步驟組成的 -- 低階的、過(guò)程式的編程風(fēng)格。
- 而這里面的每一步說(shuō)用到的算子呢抽象級(jí)別都很高 -- 高階的、聲明式式的查詢?cè)Z(yǔ)。
Pig Latin其實(shí)就是一種讓程序員能夠進(jìn)行更細(xì)粒度控制的SQL。
這個(gè)例子沒(méi)有體現(xiàn)的其他一些特點(diǎn):
- 支持一個(gè)完全自由的、完全嵌套的數(shù)據(jù)模型
- 支持用戶自定義函數(shù)
- 支持對(duì)文本的、沒(méi)有schema信息的數(shù)據(jù)進(jìn)行直接處理
- 提供了一個(gè)Debug環(huán)境 -- 這個(gè)對(duì)于處理大數(shù)據(jù)非常的有用。
特性和動(dòng)機(jī)
Pig設(shè)計(jì)的主要設(shè)計(jì)目標(biāo)是:成為有經(jīng)驗(yàn)的程序員對(duì)大數(shù)據(jù)集進(jìn)行ad-hoc分析的工具。Pig的很多特性都是為這個(gè)設(shè)計(jì)目標(biāo)服務(wù)的。
它是一門(mén)Dataflow語(yǔ)言
Pig的每一步都是一個(gè)(而且只有一個(gè))高階的數(shù)據(jù)轉(zhuǎn)換。與此相對(duì)應(yīng)的,SQL里面則通過(guò)指定一系列的條件來(lái)對(duì)一份數(shù)據(jù)進(jìn)行過(guò)濾、聚合處理。因此看起來(lái)會(huì)比SQL的方式干凈一點(diǎn)、簡(jiǎn)單一點(diǎn), 看看Yahoo!內(nèi)部的用戶是怎么說(shuō)的:
“I much prefer writing in Pig [Latin] versus SQL.
The step-by-step method of creating a program
in Pig [Latin] is much cleaner and simpler to use
than the single block method of SQL. It is easier
to keep track of what your variables are, and
where you are in the process of analyzing your
data.”
– Jasmine Novak, Engineer, Yahoo!
但是需要注意的是,雖然Pig Latin通過(guò)讓用戶指定一些的數(shù)據(jù)轉(zhuǎn)換的步驟來(lái)進(jìn)行數(shù)據(jù)處理。但是這并不意味著,實(shí)際處理的邏輯必須按照用戶指定的順序來(lái),只要編譯器覺(jué)得調(diào)整一下執(zhí)行的順序可以獲得更好的性能,同時(shí)又可以得到一樣的結(jié)果,那么完全可以對(duì)用戶的數(shù)據(jù)程序進(jìn)行優(yōu)化。
而之所以編譯器可以進(jìn)行優(yōu)化就是因?yàn)?code>Pig Latin提供的原語(yǔ)足夠高階,使得編譯器能夠看到這些數(shù)據(jù)處理的邏輯。而如果直接使用map-reduce框架進(jìn)行編程的話,由于具體的數(shù)據(jù)處理邏輯都被隱含在map或者reduce函數(shù)里面,使得編譯器無(wú)法看到這些邏輯,優(yōu)化也就無(wú)從談起。
語(yǔ)言提供的原語(yǔ)越高階,那么編譯器可以進(jìn)行的優(yōu)化的可能也就越多。但是同時(shí)語(yǔ)言也就越受限,因?yàn)槟阋婚T(mén)語(yǔ)言越高階,那么就越具體,越針對(duì)某個(gè)具體的領(lǐng)域了(DSL)。所以我們可以基于map-reduce來(lái)做一個(gè)圖計(jì)算的任務(wù),但是你要基于
Pig Latin來(lái)做一個(gè)圖計(jì)算的任務(wù)就不可能了,因?yàn)镻ig Latin已經(jīng)特化了。
快速開(kāi)始以及互操作性
Pig的設(shè)計(jì)目標(biāo)是ad-hoc的數(shù)據(jù)分析,用戶可以針對(duì)任意一個(gè)文本文件進(jìn)行數(shù)據(jù)分析,而不需要先把它"導(dǎo)入"到Pig系統(tǒng)里面去,這樣可以節(jié)省很多的數(shù)據(jù)導(dǎo)入時(shí)間。傳統(tǒng)的數(shù)據(jù)庫(kù)都要求用戶把數(shù)據(jù)導(dǎo)入之后才能進(jìn)行分析、查詢,主要原因有三方面:
- 保證事務(wù)一致性
- 保證數(shù)據(jù)的快速查詢(通過(guò)索引之類的機(jī)制)
- 對(duì)數(shù)據(jù)進(jìn)行加工,然后用戶以及其它別的用戶可以基于這個(gè)數(shù)據(jù)進(jìn)行進(jìn)一步的處理。
而由于Pig只支持read-only的數(shù)據(jù)分析,而這種類型的分析基本都是要掃描所有數(shù)據(jù)的,因此事務(wù)啊,查詢效率啊之類的都不重要。在論文的作者看來(lái)用戶經(jīng)常需要分析一個(gè)臨時(shí)的數(shù)據(jù),然后就丟掉了,因此他覺(jué)得沒(méi)必要對(duì)數(shù)據(jù)加工以及管理。
而且由于Pig可以對(duì)一個(gè)外部的文件(而不需要導(dǎo)入)進(jìn)行分析,使得Pig可以很好的跟大數(shù)據(jù)生態(tài)系統(tǒng)里面的其它工具進(jìn)行協(xié)作。
對(duì)于這一部分其實(shí)不是很認(rèn)同,從今天的觀點(diǎn)來(lái)看大的公司基本都會(huì)把數(shù)據(jù)入庫(kù)到一個(gè)大的數(shù)據(jù)倉(cāng)庫(kù)里面,比如開(kāi)源的Hadoop, 或者是阿里巴巴的ODPS之類的。因此能夠?qū)ν獠康奈谋疚募M(jìn)行處理看起來(lái)很酷,但是可能實(shí)際用起來(lái)并沒(méi)有那么有用。
事務(wù)一致性對(duì)于大數(shù)據(jù)來(lái)說(shuō)確實(shí)沒(méi)有太大的意義,但是對(duì)數(shù)據(jù)的快速查詢還是需要的,還是前面說(shuō)的原因,一份數(shù)據(jù)進(jìn)入數(shù)倉(cāng)之后,不同的用戶會(huì)基于不同的查詢條件對(duì)數(shù)據(jù)進(jìn)行分析。如果每次都要掃描所有的數(shù)據(jù),那還怎么進(jìn)行大數(shù)據(jù)分析。
嵌套式的數(shù)據(jù)模型
其實(shí)程序員腦子里面的數(shù)據(jù)模型都是嵌套式的,比如我們要表達(dá)一個(gè)人的父親的工作單位的電話號(hào)碼, 可能會(huì)是這樣的:
person.father.company.phone
這是明顯的嵌套式的。
而我們主流的數(shù)據(jù)庫(kù)提供的數(shù)據(jù)結(jié)構(gòu)都是扁平式的,比如如果要存儲(chǔ)上述例子里面的信息在MySQL里,至少要設(shè)計(jì)person, company兩張表,把數(shù)據(jù)打平才能表達(dá)這個(gè)信息。但是這其實(shí)是不自然的,是強(qiáng)制讓人類用計(jì)算機(jī)的思維來(lái)思考事情。編寫(xiě)起來(lái)自然也就不是那么舒服了。
Pig Latin支持set, map, tuple等非原子數(shù)據(jù)類型作為一個(gè)表的字段, 它有如下的優(yōu)點(diǎn):
- 內(nèi)嵌的數(shù)據(jù)模型更接近程序員的思維方式,因此更自然。
- 數(shù)據(jù)經(jīng)常被以內(nèi)嵌的數(shù)據(jù)模型的方式保存在磁盤(pán)上
這個(gè)我倒是沒(méi)什么感覺(jué)。
- 內(nèi)嵌的數(shù)據(jù)模型使得我們實(shí)現(xiàn)一個(gè)代數(shù)語(yǔ)言成為可能。
因?yàn)橐粋€(gè)代數(shù)操作的結(jié)果就是一個(gè)內(nèi)嵌的結(jié)構(gòu)
- 一個(gè)內(nèi)嵌的數(shù)據(jù)模型使得程序員可以非常方便的編寫(xiě)各種UDF。
作為頭等公民的UDF
對(duì)大數(shù)據(jù)進(jìn)行分析的很大一部分工作是對(duì)數(shù)據(jù)進(jìn)行自定義的處理。為此Pig Latin為UDF提供了豐富的支持。
支持并行執(zhí)行
因?yàn)镻ig Latin是為了處理大規(guī)模數(shù)據(jù)而設(shè)計(jì)的,因此Pig Latin在設(shè)計(jì)的時(shí)候精心挑選了那些能夠并行化的原語(yǔ)到語(yǔ)言里面來(lái)。而那些無(wú)法并行化的語(yǔ)言Pig Latin就不做語(yǔ)言方面的支持, 比如非等join。
調(diào)試環(huán)境
在任何語(yǔ)言里面,要想把一個(gè)數(shù)據(jù)處理的程序?qū)憣?duì)都需要很多個(gè)迭代。所以寫(xiě)這類程序的方式基本是寫(xiě)一點(diǎn),試跑一下,再改一改。但是如果處理大數(shù)據(jù)也這么干太費(fèi)時(shí)間了。因此Pig Latin提供了調(diào)試的環(huán)境,讓你可以方便地對(duì)Pig Latin大數(shù)據(jù)處理程序進(jìn)行調(diào)試。
Pig Latin語(yǔ)法簡(jiǎn)介
數(shù)據(jù)類型
主要四種:
- 原子類型. 比如數(shù)字,字符串。
- Tuple. Tuple就是一系列的字段。每個(gè)字段可以是任意類型的數(shù)據(jù)。
- Bag. Bag是tuple的集合。而且Bag里面允許出現(xiàn)重復(fù)的元素。
- Map. map跟Java里面的map類似。但是key必須是原子類型,value可以是任意類型。
加載數(shù)據(jù): LOAD
queries = LOAD `query_log.txt`
USING myLoad()
AS (userId, queryString, timestamp);
從query_log.txt這個(gè)文件里面加載數(shù)據(jù),利用myLoad這個(gè)函數(shù)解析成三個(gè)字段。
對(duì)每個(gè)tuple進(jìn)行處理: FOREACH
expanded_queries = FOREACH queries GENERATE userId, expandQuery(queryString)
通過(guò)FOREACH, 把原來(lái)的三個(gè)字段,處理成新的兩個(gè)字段(其中expandQuery是一個(gè)UDF)。
對(duì)數(shù)據(jù)進(jìn)行過(guò)濾: FILTER
real_queries = FILTER queries BY NOT isBot(userId)
把相關(guān)的數(shù)據(jù)Group在一起: COGROUP
grouped_data = COGROUP results BY queryString,
revenue BY queryString;
COGROUP跟JOIN類似,這里不做展開(kāi)了。
實(shí)現(xiàn)
Pig Latin被Pig完全實(shí)現(xiàn)了。Pig被設(shè)計(jì)成可以對(duì)接各種不同的執(zhí)行平臺(tái),目前實(shí)現(xiàn)的平臺(tái)是Hadoop. 一個(gè)Pig Latin的執(zhí)行主要分兩步:
- 把一段Pig Latin的腳本編譯成邏輯執(zhí)行計(jì)劃。
- 把邏輯執(zhí)行計(jì)劃變異成實(shí)際的物理執(zhí)行計(jì)劃(Hadoop MapReduce)。
構(gòu)造邏輯計(jì)劃
對(duì)于客戶端輸入的Pig Latin指令,Pig的解析器首先對(duì)它進(jìn)行解析,并且驗(yàn)證一下輸入文件,輸入的bag都被定義過(guò)。
Pig會(huì)對(duì)每個(gè)bag都編譯對(duì)應(yīng)的邏輯計(jì)劃。而一個(gè)新的bag的邏輯計(jì)劃是它自己的邏輯計(jì)劃再加上它說(shuō)引用的bag的邏輯計(jì)劃。比如:
c = COGROUP a BY ..., b BY ...;
這里a, b, c都有自己的邏輯執(zhí)行計(jì)劃,而c的執(zhí)行計(jì)劃是它自己這個(gè)指令的執(zhí)行計(jì)劃再加上a和b的執(zhí)行計(jì)劃。
注意在執(zhí)行完這條命令之后,并不會(huì)有真正的數(shù)據(jù)處理邏輯被執(zhí)行,這些指令只是被記錄下來(lái),只有當(dāng)有STORE命令執(zhí)行的時(shí)候,才會(huì)真正觸發(fā)整個(gè)執(zhí)行計(jì)劃的執(zhí)行。
這其實(shí)也就是所謂的延遲執(zhí)行。延遲執(zhí)行使得內(nèi)存內(nèi)的流水線,指令重排等等成為可能。
Apache Beam也是類似的延遲執(zhí)行的風(fēng)格,先記錄相應(yīng)的執(zhí)行指令,等真正提交的時(shí)候才執(zhí)行。
編譯成Map-Reduce的執(zhí)行計(jì)劃
把一個(gè)Pig Latin的邏輯執(zhí)行計(jì)劃編譯層一個(gè)map-reduce任務(wù)非常的簡(jiǎn)單。map-reduce語(yǔ)言本質(zhì)上是提供了一個(gè)大規(guī)模的group-by的能力,其中map生成要group的key。而reduce則對(duì)每個(gè)group進(jìn)行處理。
一個(gè)Pig Latin的邏輯執(zhí)行計(jì)劃其實(shí)就是一個(gè)包含各種原語(yǔ)的圖,而其中有一種特殊的原語(yǔ)是(CO)GROUP, 把一個(gè)邏輯計(jì)劃編譯成物理計(jì)劃的過(guò)程其實(shí)就是以GROUP為邊界把邏輯計(jì)劃拆成多個(gè)MapReduce任務(wù)的過(guò)程。兩個(gè)GROUP之間的原語(yǔ)可以作為上一個(gè)GROUP的reduce的一部分,也可以作為下一個(gè)GROUP的map的一部分。

目前Pig的實(shí)現(xiàn)里面把Pig Latin文本編譯成邏輯計(jì)劃的過(guò)程跟各種具體的執(zhí)行引擎相隔離 -- 因?yàn)檫@個(gè)邏輯是通用的。只有編譯成具體的物理計(jì)劃的時(shí)候才會(huì)跟具體的執(zhí)行引擎有關(guān)。
Pig Pen
Pig Pen是一個(gè)Pig Latin的所見(jiàn)即所得的編程環(huán)境,有點(diǎn)Reactive Development的意思。
總結(jié)
- Pig Latin這種設(shè)計(jì)思路很新穎,很有意思。把兩種范式: 聲明式和命令式結(jié)合到一起(也真是挺敢想的)。
- Pig Latin編譯成物理計(jì)劃的過(guò)程沒(méi)想到這么簡(jiǎn)單。
- Pig Latin跟Beam看起來(lái)很像,Pig Latin一直提一個(gè)概念: Dataflow, 而B(niǎo)eam得前身就是Google Dataflow。
- Pig Latin想作為一種通用的數(shù)據(jù)處理語(yǔ)言,跟底層實(shí)際的引擎相分離,這一點(diǎn)跟Beam也很像。
- 稍微豐富一下Pig Latin的語(yǔ)言,是不是可以把它嫁接到Apache Beam上去,讓它不但支持離線,還能支持實(shí)時(shí)。這樣Pig Latin專門(mén)做他們語(yǔ)言層面的事情,Apache Beam專門(mén)做適配底層執(zhí)行引擎的事情去,這樣一下子Pig Latin就可以支持所有的數(shù)據(jù)處理引擎 -- 實(shí)現(xiàn)跨域式發(fā)展。
- 語(yǔ)言提供的原語(yǔ)越高階,那么編譯器可以進(jìn)行的優(yōu)化的可能也就越多。但是同時(shí)語(yǔ)言也就越受限,因?yàn)槟阋婚T(mén)語(yǔ)言越高階,那么就越具體,越針對(duì)某個(gè)具體的領(lǐng)域了(DSL)。
- 延遲加載可以給優(yōu)化帶來(lái)更多的機(jī)會(huì)。