上一节我们简要介绍了Pig的安装和简单使用。接下来我们继续。
Pig和数据库的区别
上一节讲到了Pig的分组(group)和筛选(filter),让人感觉这种用法和数据库的SQL差不多。实际上Pig和传统的关系型数据库以及SQL语言是有很明显区别的。我们逐个讲解。
1) Pig Latin是面向数据流的编程方式,而SQL是一种描述型编程语言。我们以前学习SQL的时候经常听到过这样一句话:用SQL,你只需要告诉它你需要什么,具体怎么做交给SQL就行了。而Pig Latin是需要你一步一步根据数据流的处理方式来编程的,也就是说你要设计数据流的每一个步骤,有点类似SQL的查询规划器。
2) 传统的关系数据库(RDBMS)需要你预先定义表结构(模式),所有的数据处理都是基于这些有着严格格式的表数据。而Pig则不需要这样,你可以在运行时动态定义模式。本质上来说,Pig可以处理任何格式的元组。一般情况下,Pig的数据来源是文件系统,比如HDFS,而RDBMS的数据是存储在数据库中的。(备注:关于元组的概念,基本和Python中的touple是差不多的)
3) Pig支持比较复杂的,比如嵌套结构的数据处理。这种特殊的处理能力加上UDF(用户自定义函数)使得Pig具有更好的可定制型。
4) 一些RDBMS特有的特性是Pig所没有的,比如事务处理和索引。Pig和MapReduce一样,是基于批量的流式写操作。
Hive是介于Pig和RDBMS之间的一种数据处理方案,其处理语言HiveQL类似于SQL,这就使得熟悉SQL的人可以快速熟悉和使用Hive。和Pig一样,Hive的存储方式也是基于文件系统(HDFS)。我们会在单独的章节来讲解Hive。
Pig Latin语法结构
本节我们只简要介绍Pig的常用语法结构及相关概念。
1) ls命令
显示目录结构。比如以下命令将列出HDFS的目录结构。
ls /
备注:
1) 要列出HDFS的目录结构,请确保当前是以MapReduce模式运行
2) 一般情况下,命令需要以分号显式结束。但一些交互式命令不需要以分号结束,比如ls。如果你不能确定是否需要分号,加上一个分号总归不会错,比如“ls /;”。
3) 需要以分号结束的语句,可以将一个语句分成多行书写,这样可读性更好。(有点类似Python)
4) 单行注释可用双连字符(--);多行注释用类似于C语言的/* */。
5) 和其它语言一样,Pig的保留关键字不能作为标示符。
6) Pig的大小写敏感没有一致的规定。操作和命令是不区分大小写的,比如load,group。而函数式区分大小写的,比如MAX。
2) Explain命令
考虑如下语句:
records = load ‘/home/user/input/temperature1.txt’;
valid_records= filter records by temperature!=999;
grouped_records= group valid_records by year;
dumpgroup_records;
Pig Latin解释器看到第一条load语句时,会执行数据的读取操作吗?不会,因为后续还可能会对数据进行筛序或分组。解释器会生成load语句的逻辑计划,但是并不会执行读取操作,自然也就不会执行检查工作,所以即使load的这个文件不存在,此时也不会报错。最后,当碰到dump语句时,逻辑计划即被编译成物理计划,并最终执行数据的读取和输出操作。
Pig的物理计划是有一系列MapReduce作业组成的。使用explain命令即可查看这些逻辑计划和物理计划。
有些语句不会生成逻辑计划,比如用于诊断操作的describe,expain语句等,这些语句会被解释器立即执行。
3) Set命令
Setdebug on用于开启调试日志
Setjob.name ‘job name’ 用于设置MapReduce作业的名称。Pig提交的MapReduce作业,如果没有特别指明的话,作业名称由系统生成。如果你希望给你的Pig作业指定一个有意义的名称,以便可以方便的通过Hadoop的Web UI查看的话,set job.name将会是一个非常好的命令。
4) run和exec
这两个命令都可以执行Pig脚本。区别是exec以批处理方式运行,这意味着所有该脚本文件中定义的标示符,当脚本执行完后在脚本调用窗口将不可访问。而以run运行时,相当于将脚本文件中的内容手工在调用窗口输入并执行,这就意味着脚本文件中所有的标示符,比如定义的关系在脚本执行完后仍然可访问,同时在shell的命令历史记录中可以找到脚本中的语句。
5) 表达式
Pig有丰富的表达式类型,和其它编程语言基本相似,比如加减乘除。
6) 类型
Pig有四种数值类型,分别是int,long,float和double。其用法和java一致。
bytearray类似java的字节数组,用于表示二进制大对象。
chararry类似java的string字符串,用于存储utf-16格式的数据。当然,也可以处理utf-8的数据。
Pig没有boolean类型,你可以使用int等其他类型轻松实现。
Pig还有一些比较复杂的数据类型,比如元组(touple),包(bag)和映射(map)。这些复杂类型的数据,一般从文件加载或由一些关系操作而得来。
包(bag)和关系(relation)的概念大致相似,但是其处理方式是有区别的。
包是元组的无序集合,比如{(1,’value’),(‘v1’,v2)}。而关系则是有名字的包,这个名称成为关系的别名。一般情况下,一个Pig语句会产生一个关系。
通过文字直接创建一个关系是非法的,比如:
A = {(1,2)} –非法
将一个关系的字段投影为一个新的关系也是非法的,比如:
B = a.$0 –非法
作为一个变通方案,你可以用以下语句实现该功能:
B = foreach A genereate $0
将来的Pig版本也许会统一包和关系的处理模式,以消除这种不一致。
7) 模式
Pig中的关系可以有对应的模式。在模式中可以定义关系里面的字段的名称和类型。正如我们先前看到的语句:
records= load ‘/home/user/input/temperature1.txt’ as (year: chararray,temperature: int);
其中 as子句定义了关系records的字段名称和类型。执行describe records命令后,你将看到以下输出:
records: {year: chararray,temperature:int}
Pig擅长于处理纯文本信息,在模式定义方面提供了更大的灵活性,包括数据类型的选择。这和传统的关系型数据库是有本质区别的。(传统的RDBMS必须要事先定义严格的表结构)
在定义模式的时候,不需要为每个字段都定义类型。如果缺省,则pig指定默认类型为字节数组bytearray。
你甚至可以选择不定义模式。但是一旦你选择了定义模式,则必须为每个字段指明名称和类型,当然类型是可选的。这意味着以下语句是合法的:
records= load ‘/home/user/input/temperature1.txt’;
如果你需要引用没有定义模式的关系中的字段,则只能通过索引引用,而不能通过字段名称来引用,因为你根本就没有定义字段名称。
在查询中定义模式是非常灵活的。但是当很多查询基于相同的模式时,这种在查询中定义模式的方法就不利于重用和维护了。此时可以自己写加载函数来实现,稍后我们再讲。同时,apache开源项目Hcatalog提供了相关功能,其基于Hive的metastore实现。Pig可以充分利用这个功能,具体请参阅相关文档。(我不太熟悉这个)
一般情况下,我们不需要为每一个关系定义其模式。比如通过filter筛序而来的新的关系和待筛选的数据源具有相同的模式。但是有些操作产生的关系却有可能具有不同的模式,比如union操作。具体后续再详细介绍。
8) 验证和空值
传统的关系数据库中,如果你尝试写入一个类型不符的数据到数据库,比如将字符‘a’写到一个类型定义为int的字段,则会报错。Pig的处理于此不同,比如load语句在读取数据时,碰到了一个字符‘a’,而此位置的数据在模式定义中定义为int,则pig会用空值(null)取代之,同时会输出提示信息,但是不会终止语句执行。本质原因是,对于大数据集而言,有一定比例的损坏数据是一种常见情况。此时我们可以通过如下语句进行筛选:
corrupt_records= FILTER records BY temperature is not null;
或者用split语句:
Splitrecords into good_one if temperature is not null, bad_one is temperature isnull;
9) 函数
Pig的函数有4种类型:
l 计算函数(Evalfunction) 比如MAX。(请注意,函数名是区分大小写的)
l 筛选函数(Filterfunction) 用于过滤掉不需要的数据,比如ISEMPTY函数。
l 加载函数(loadfunction) 用于从外部数据源(比如文件)加载数据到关系。
l 存储函数(stroefunction) 用于将关系中的数据输出到外部存储。比如PigStorage函数。
详细函数列表,请参与官方文档。
下一节我们看看用户自定义函数(User-Defined Functions)。
备注:本文严重参考英文版Hadoop教程翻译而来,当然并没有严格按照原文内容翻译,仅供参考。