TIP
本文主要是介绍 Pig-基础原理总结 。
# Pig基本原理
Pig是一款数据装载、处理、存储的工具。我们可以使用pig将数据装载到内存中成为一个关系,然后再通过PigLatin语言对数据进行操作,最后再将数据转换的结果存储到一个文件中。它的底层事实上是MR的任务,所以会具备MR的各个特性。
# Pig的组成
Pig由两部分组成
- Pig Latin是Pig处理数据所用到的语言。
- Pig运行的环境,在单机模式下运行环境下为JVM,在集群模式下为Hadoop Cluster。(因为它就是一个MR所以可以运行在YARN上面)。
# Pig的操作方式
- 在shell下运行(Grunt)。
- 写脚本,(xxx.pig)。
- 使用Java语言进行操作,(就像是JDBC一样)。
# Pig与普通数据库的区别
- PigLatin语句是一行一行的进行执行,不像SQL一样它有一些嵌套。但是SQL的底层与PigLatin是一样的,SQL语句在表面上是嵌套的但是在底层也是一句一句去执行的。
- PigLatin模式的定义很灵活,模式也就是数据的约束。在SQL中往往是先创建表然后再插入数据。但是Pig作为一款数据处理工具它的情况就是先有数据然后在数据上添加对应字段的名称与属性。
- SQL中只能够有平坦的数据结构,而在Pig中会有嵌套的数据结构。
- Pig基于MR所以不支持随机IO而SQL支持随机IO。
# PigLatin基本介绍
# 基本组成与特征
一个PigLatin程序是由一组PigLatin语句组成。大小写敏感,每一句最后都以分号结尾。其中所有的一行行的语句都是为了构建一个查询计划。在构建查询计划时语句是不会执行的。在构建完成之后Pig就会做一个整体的优化。比如我们在上一句查询了1000条数据然后下面会给出一些条件去过滤掉一些数据。这样的话我们就可以直接去追求那些最终的数据从而省略掉中间的步骤。最后在使用比如说DUMP语句的时候再去将逻辑计划转换成物理计划再执行。需要注意的是如果中间出现了一个错误的语法那么逻辑计划的搭建就会终止。
# 多查询执行
在批处理下还有一个隐藏的优化点就是多查询执行就是一个执行的结果可以多个过程所引用。在交互式的环境下STORE、DUMP命令总是会触发语句的执行,各个操作之间彼此隔离他们内部会包含run语句,执行的结果不能重用。但是批处理下的STORE操作就能够实现多查询执行它内部包含exec语句。
有些语句并不会处理数据比如说导包或者是注册语句,由于它不会处理语句的特性所以他们不会像执行计划一样最后才执行,相反他们会立即执行。
# 关系——语句执行的结果
当我们把文件中的数据通过PigLatin装载到内存中的时候,当我们将数据执行了一些操作的时候,我们会把这个数据称作为关系,它在内存中它而且可能具有复杂嵌套的结构。在结构方面它与Pig中的包(本质上也是一个复杂嵌套的数据结构)一样。但是Pig赋予他们的操作是不同的。
# PigLatin的表达式
详见文档
# PigLatin的数据类型
在PigLatin中没有一种数据对饮Java中的byte、short、char。字符串使用chararray来表示,其它整数类型与Java的基本类似。除此之外还有一些复杂的类型,tuple元组、bag包、map映射。如果在装载数据时没有指定类型那么默认的数据类型就是bytearray。
其它详见文档。
# PigLatin的模式
模式简单来说就是字段名称+字段类型。上面我们已经提到,SQL中会现有模式然后再有数据。但是Pig是一个数据处理工具所以会在已有的数据上添加对应的模式。但是我们也可以不去定义模式,通过$[数字]来引用获取数据,此时获取到的所有数据的类型都是bytearray类型。
# 模式重用
Pig的模式十分的灵活,我们可以在任意一个操作之前重新定义我们所需要的模式,但是这也带来了一些负担。因为有些模式不是每次都需要定义因为它与上一次的模式是一样的。但是Pig本身是不能够实现模式的重用的,这时就能够使用HCatalog来利用Hive的matestore来存储模式进而达到模式重用。
# 验证与空值
实务上我们采集到的数据可能是残缺的不完整的或者是格式错误的,这时候我们可能使用过滤操作把它过滤掉。当然没有明确的手段去处理这些事情可以说是非常的灵活。
# 模式合并
在SQL中我们在一些操作之后会得到新的模式比如说把两张表连接。Pig中不用为每个新产生的关系申明模式。大多数情况下会根据输入的模式来确定输出的模式。但是模式的合并可能会比较复杂当数据横向合并的时候会由于两组数据字段的个数不同而不兼容。
# PigLatin函数
# 计算函数
这里着重强调聚集函数中的代数函数,代数函数可以有进一步的优化。比如说求最大值这种运算,几个小组的最大值的最大值就是所有数的最大值。但是几个小组的中位数的中位数就不是全体数字的中位数。前一个计算就属于代数函数。
# 过滤函数
过滤函数的底层是一个判断,返回值是一个boolean。满足要求的就返回true否则就返回false这样的话返回为true的值会留下来。
# 加载函数
加载函数会指明如何从外部存储加载到一个关系。
# 存储函数
如何把一个关系中的数据存储到外部存储。通常加载和存储会由相同的类来实现。
# 宏
就是一些函数或者是操作的封装。一个宏可以打包一段PigLatin的代码。使用$前缀进行引用,在运行的时候会展开宏。
# UDF自定义函数
# 过滤UDF
所有的UDF都是EvalFunc的实现类(直接或者间接)。在调用的时候我们会使用类名加参数,这也是为什么Pig中会严格的区分大小写。由于UDF在Java语言描述的情况下会在执行的时候以jar包的方式运行,所以会在运行时将jar文件传输到集群。
我们也可以利用UDF来告诉Pig所期望的各个字段的值。重写EvalFunc的getArgToFuncMapping()方法。
# 计算UDF
只是继承不同的类而已,在动态调用中如果用到了Java中的代码的话我们就会使用反射技术同时使用Method的InvokeXX来调用。
# 加载UDF
Pig数据的加载会先于mapper的运行,所以保证数据可以被分割成各个独立的mapper是十分重要的。在遇到损坏的数据的时候通常会采取返回null的策略。我们可以在类中定义类的模式但需要注意的是AS中所定义的模式的优先级会高于在函数中的优先级。
# 数据处理
# 加载和存储
常见的调用默认的存储函数,比如说PigStorage。
# 数据的过滤
- 最常见的就是FOREACH … GENERATE操作
新关系 = FOREACH 旧关系 GENERATE 旧关系中的字段的一些处理,当然也包含我们的添加字段。
- STREAM操作主要是提供了脚本或者外部程序操作的一种可能。
# 数据的分组与连接
- JOIN,就是通过两个数据集的某一个关联字段来进行连接。如果连接的关系太大不能放全部放在内存中,则可以使用普通的连接操作。如果有一个关系小到可以放在内存中那么就使用分段复制连接。它同样支持类似于SQL中的内连接,外连接。
- COGROUP,就是一个能够产生复杂嵌套数据结构的JOIN。
- CROSS,就是PigLatin中的笛卡尔积。这个场景经常应用于计算相似度但是随着我们对业务的进一步了解,我们会减少一些表之间的笛卡尔积来避免无效的运算。
- GROUP与SQL中的GROUP类似,这样的话会按照一个指标去聚合数据。
另外排序与数据的组合或者是拆分与SQL基本类似。合并会由于字段的不一致所导致模式的混乱,如果字段值的个数不一致的话那么就或导致没有模式。同样分割也是按照一定的指标去实现数据分离(IF … OTHERWISE…)。
# Pig Trick
- 运行在MR模式下,我们需要做的最重要的一件事就是处理的并行度与所处理的数据集大小匹配。一种好的方式就是参数设置成稍微小于急群中reduce任务时的slot的个数。
- 有一些值会随着运行时间的不同而改变,这时我们就使用参数代换来动态的改变参数。
# 参考文章
- https://www.it610.com/article/1292786775546208256.htm
← Pig-基础信息介绍 Pig-基础知识总结 →