基于 MapReduce 框架的大数据量查询语言的选择
简介: 随着信息规模的爆炸式增长,越来越多的公司特别是互联网公司面临着如何利用更效第成本地进行大数据量处理的难题。Google 于 2004 年发表了 MapReduce 编程模型,作为 MapReduce 的开源实现 Hadoop 的应用及其支撑 MapReduce 编程的高级语言也得到了越来越多的关注和重视,本文在应用层次上着重比较了目前流行的两种高级语言 -- Pig 和 Jaql,分析了他们适合的使用场景和各自的优势。
MapReduce 是 Google 公司的核心计算模型,它将复杂的运行于大规模集群上的并行计算过程高度的抽象到了两个函数 --Map 函数和 Reduce 函数,如果直接采用 MapReduce 框架来进行并行程序设计,需要了解 MapReduce 模型的框架并且将待处理的数据集分解成许多小的数据集,将对其的处理加入到这两个函数的实现中,而关注实现上层应用的程序员通常并没有必要关心这些实现细节。为了解决这个问题,Pig 和 Jaql 等针对半结构化大数据量的查询语言应运而生,运用这两种语言,程序员并不需要关心 MapReduce 框架原理而只需要应用这两种更容易理解和人性化的脚本语言。
Pig 是由 YaHoo! 发明的,已经贡献到 Apache 开源社区而且已经被很多公司在生产环境中应用。Jaql 是 IBM 实验室贡献的另外一种开源语言发布在 Google Code 上已经被多个项目所使用,它针对目前数据分析的一般流程,为开发人员进行数据处理的上下衔接提供了更多方便。
Pig 支持两种运行模式— local 模式和 Hadoop 模式,其 local 模式下也会利用 MapReduce 的框架进行了实现 ( 只不过不采用真正的 cluster 去运行 ),Jaql 支持的模式为 minicluster 模式和真正的 cluster 模式,其具体命令如下表 1:
模式 | Pig | Jaql |
---|---|---|
本地模式 | pig -x local | jaqlshell |
Cluster 模式 | Pig -x mapreduce | jaqlshell -c (real cluster) jaqlshell -d (--hdfs-dir) -n (--no --nodes) |
Jaql 和 Pig 均支持命令行输入,脚本,嵌入式的执行模式,其中我们可以从 Java 或者 Python 中调用 Jaql, 可以从 Java 程序中调用 Pig, 具体的操作可以参考 http://wiki.apache.org/pig/EmbeddedPig 和 http://code.google.com/p/jaql/wiki/EmbeddingJaql。
另外 ,JaqlServe 作为 Jaql 的扩展,使得用户可以通过 HTTP 或者封装 http 请求的类来对 Jaql server 进行访问,上载自己定义的程序模块或者自定义函数,然后提交由 Jaql 本身去执行,同时可以检查作业的状态,撤销作业等操作。而 Pig 目前并没有提供类似的功能。
Pig 的使用和普通的脚本语言非常类似,如下面的例子中是从 data 中读取数据并进行过滤:
A = LOAD 'data' USING MyStorage() AS (T: tuple(name:chararray, age: int)); B = FILTER A BY T == ('john', 25); |
而对于 Jaql 来说我们可以用下面的语句来实现相同的功能。
read(file('data')) -> filter $.age = 25 -> filter $.name = 'john'; |
从中可以看出 Jaql 语言借鉴了 Unix pipe 的特点,能够充分而简洁的体现数据在数据分析系统的处理过程 ( 比如从数据源经历数据抽取过程,数据转换过程再到数据写入过程 )。
Pig 支持的大多数数据类型由 Java 本身的数据类型发展而来,在 Java 中有着对应并且他们之间有着相似的语义,而 Jaql 支持的数据类型是基于 JSON 数据模型所支持的数据类型,其详细列表如下表 2 所示 :
数据类型 | Pig | Jaql |
---|---|---|
Numberic | int | |
decfloat | ||
long | long | |
float | ||
boolean | ||
double | double | |
Text | chararray | string |
Binary | bytearray | binary |
Complext | ||
tuple | ||
bag | ||
map | ||
JsonRecord | ||
JsonArray | ||
Date | date |
Pig 和 Jaql 操作符和内建函数以及自定义函数的比较 :
Pig 本身包括交互性的 shell 接口 Grunt,同时还包括一种描述性的类 SQL 语言 Pig Latin, 其语句即为对数据进行操作的关系式,其中包含了很多操作符比如 Filter,GROUP对对象进行过滤,分组,联合等操作,同时也支持了很多常用的内建函数 ( 如 count, avg 等 ) 来对数据进行处理 ,Pig 的内部引擎负责把 Pig Latin 的语句编译成为 MapReduce 作业去执行。Jaql 的实现同样借鉴了SQL,XQuery, LISP, andPigLatin等查询语言,两者在操作符和内建函数的大部分方面有着相似的功能,但是还是有着一些不同。下表 3 中列出了操作符和内建函数的对应关系:
操作符分类 | Pig | JAQL |
---|---|---|
关系型操作符 | GROUP | group |
DISTINCT | distinct | |
TOKENIZE | tokenize | |
FILTER | filter | |
FOREACH | ||
JOIN | equijoin join | |
ORDER | sort | |
SPLIT | strsplit,splitArr | |
UNION | union | |
LOAD | read | |
STORE | write | |
expand | ||
grep | ||
if else | ||
求值 | CONCAT | strcat |
strjoin | ||
merge | ||
IsEmpty | isnull | |
MAX | max | |
MIN | min | |
AVG | avg | |
SUM | sum | |
计算 | + | + |
- | - | |
* | * | |
/ | / | |
abs | ||
dive | ||
regex | Regex | |
? : | ||
调试 | DESCRIBE | Schemaof,typeof |
EXPLAIN | explain | |
ILLUSTRATE | ||
catch | ||
registerExceptionHandler | ||
timeout | ||
自定义函数 | DEFINE | fn |
REGISTER | javaudf | |
数据读取/存储 | Binary data | Binary data |
Text data | Text data | |
unstructured data in UTF-8 format. | Json data | |
XML | ||
CSV | ||
Json stream | ||
HBASE data |
从上面的比较可以看出,Pig 和 Jaql 实现的函数或者操作符存在一些不同之处,以下是特别需要注意的几点 :
1)Pig 在 mapred 模式下不能直接对本地的 data 进行处理而必须先进行 copyFromLocal 操作,然后通过 load 将 HDFS 上新创建的文件读出来进行处理 , 而对于 Jaql 来讲可以方便的对本地和 HDFS 上面的数据进行 read 和 write。同时 Pig 和 Jaql 理论上来讲都可以支持所有的文件类型 ( 如果你的数据格式不被它们默认的读写功能所认识,在 Pig 中需要自定义 function 来被 Load/Store 使用,而在 Jaql 是通过自己编写 Adapter 来进行数据格式和 Json 格式的转换 ),但是他们所支持的默认文件类型并不相同。Pig 默认支持读入的文件内容是以 tab 来进行分隔的,读入时可以根据文件存储的格式选择以 BinStorage 或者 PigStorage 方式读入 , 读入后会默认以逗号或者换行符来分隔各字段或者每一组数据。而 Jaql 可以读入的默认文件类型更为广泛如内容为 Json 格式或者 CSV 格式的文件,或者读取 hbase 数据库中的数据,这些数据将以 binary 或者 csv 格式保存,在再次读出的时候对应的还是原来的 Json 格式或者 csv 格式。
grunt> A = load 'testdata.txt' using PigStorage('\t'); grunt> DUMP A; .... (Alice,shandong) (Fred,tianjin) (Cindy,wuhan) (coco) grunt> quit; |
jaql> read(file('test3.json')); [ { "name": "Alice" }, { "age": "12" } ] jaql> read(file('test3.json'))->write(file('test4.json')); { "location": "test4.json", "type": "local" } |
(2) Pig 和 Jaql 均具有调用外部的脚本来处理自身数据的功能,Pig 是采用 Stream 模式来实现而 Jaql 通过调用 external call 来实现。两者的基本用法如下 :
jaql> A = ['Alice']; jaql> external = externalfn({cmd:'cut -c3',perPartition:false,mode:'push'}); jaql> A -> external(); [ "i" ] |
A = LOAD 'data'; B = STREAM A THROUGH `stream.pl -n 5`; grunt> A = load 'stream.txt' using PigStorage('\t'); grunt> C = STREAM A THROUGH `cut -c3`; grunt> dump C; |
Jaql 和 Pig 均将前面的数据作为命令的输入,并将命令执行后的结果作为输出打印到屏幕上或者保存在文件中。二者之间的区别是 Pig 的 STREAM 语句可以结合 define,ship, cache 操作符来定义更为复杂的输入输出文件及其处理,而 Jaql 对于此功能的支持比较简洁单一,对于一些比较复杂的操作无法完全满足 ( 如脚本文件具有对其他包或者模块的依赖 ),具体情况可参考 http://www.cloudera.com/blog/2009/06/analyzing-apache-logs-with-pig/.
(3) Pig Latin 本身已经支持对 HDFS 上文件的操作比如 cp,mkdir,mv 等操作,而 Jaql 的这些功能都是通过 hdfsShell 来实现的。
除此之外,在一些函数的意义上也有稍许差别,例如 Pig 的 diff 语句可以体现出两组数据间的具体差别,而 Jaql 的 compare 只是对两者是否相符进行判断,返回布尔值。
另外 , 为了能够对 JSON 数据模型进行更方便的操作 , 除了以上的操作符和函数外 ,Jaql 提供了很多其他的关键操作符 , 如 transform, expand, tee 等。
Pig 和 Jaql 均支持由 Java 编写的 Function, 均可以通过 register 操作符来实现。其具体操作如下所示 :
public class UPPER extends EvalFunc<String> { public String exec(Tuple input) throws IOException { if (input == null|| input.size() == 0) return null; try{ String str = (String)input.get(0); return str.toUpperCase(); }catch(Exception e){ throw WrappedIOException.wrap("Caught exception processing input row ", e); } } } Usage: REGISTER myudfs.jar; A = LOAD 'student_data' AS (name: chararray, age: int, gpa: float); B = FOREACH A GENERATE myudfs.UPPER(name); DUMP B; |
public JsonString eval(JsonString jstr) throws Exception { if(jstr == null) { return null; } else { return new JsonString(jstr.toString().toUpperCase()); } } Usage: Interactive model – Console jaql > registerFunction("Upper","com.ibm.fn.upper.Upper"); Jaql > Upper('hello world'); Output “HELLO WORLD” |
根据 Robert Stewart 在"Performance & Programming Comparison of JAQL, Hive, Pig and Java"一文中的实验结果表明:
(1)Pig 语言本身只支持有限迭代编程 (for 语句 ), 而不能支持不定迭代 (while),在这种情况 Pig 必须借助于用户实现自定义函数或者嵌入在 Java 里面实现,而 Jaql 支持不定迭代,并不需要在宿主语言 Java 中实现。实验表明,在使用 Pig 和 Jaql 编程完成相同的目的时,Jaql 的代码量要更小,书写更为简练,而且在书写嵌入式程序的时候也不需要考虑到数据类型检查等问题。
(2) 在 scalability 的性能测试中 , 对于 scale out 测试的结果来看 , Jaql 和 Pig 的测试性能不相上下 , 这也是 Jaql 设计要满足的的目的之一。另外 , 对于 MapRedcue 程序 , 因为 Pig 有内部优化的机制并且允许用户自己去控制 reduce 数目 , 这对程序执行的性能也有所帮助,目前 Jaql 的开发人员在这个方面已经做了进一步的工作。
IBM 目前对 Jaql 在与其他软件的集成上面已经做了大量的工作 , 包括和数据分析软件的集成 , 和关系型数据库的集成等等 , 相比而言 , Pig 更着重在被用作单一的数据查询语言 , 如果用户需要将 Pig 用于某些产品应用中 , 需要在其应用上开发新的中间层 .
由以上比较可知 :
(1) Jaql 和 Pig 在对数据的输入输出要求,数据的处理功能上,有着相似的处理能力
(2) Jaql 的优势在于其具有更好的语义及其语义的灵活性 ( 例如允许用户将一组功能定义为 Jaql 的函数并且直接用在查询里面,并且 Json 模型即方便用来处理半结构化数据,也能够扩展性的处理在数据清洗阶段的结构化数据 ),其编程更具有易用性,另外其与其他产品的集成也成为起一大特性。
学习
- 如果您希望深入了解 Pig, 请参考 Pig 官方网站文档。
- 如果如果您希望了解 Jaql 及其实现机制,请阅读 Jaql 源代码及其 wiki。
- 关于进一步的性能比较,可以参阅 Jaql,Hive, Pig Java 的性能和编程能力比较。。
- 从 Apache 官方网站可以阅读到更多和 Pig 相关的开源项目。
- 从 Cloudera 对于 Pig 的介绍和手册 . 2009.中可以获得详细的 Pig 安装和使用信息。
- 可以从 Vuk Ercegovac 关于 Jaql 对于 Redcuer 数目控制的实现讨论了解提高 Jaql 性能的思路。
- IBM Developer Cloud Blog将为提供云计算专家关于开发人员云的最新详情。
- ibm.com/cloud 门户服务于 IBM 云产品高级概述。
- 了解 BigInsights 产品的功能。
- 随时关注 developerWorks 技术活动和 网络广播。
- 访问 developerWorks Open source 专区获得丰富的 how-to 信息、工具和项目更新以及 最受欢迎的文章和教程,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。
forward from http://www.ibm.com/developerworks/cn/opensource/os-cn-jaqlpig/#resources