Pig基础

jopen 10年前

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函数。

详细函数列表,请参与官方文档。