涉及到的知识点:
Pig是一个用来处理大规模数据集的平台,由Yahoo开发,贡献给Apache。它可以简化MapReduce任务的开发,支持PigLatin语句(类似SQL)。其本质是一个翻译器,将PigLatin语句转换为MapReduce任务来执行,并返回。
Pig0.17开始支持转换为Spark任务
下面是Pig的体系结构图:
安装流程如下:
(1)解压安装包
tar -zxvf pig-0.17.0.tar.gz -C ~/training/
(2)设置环境变量 vi ~/.bash_profile
PIG_HOME=/root/training/pig-0.17.0
export PIG_HOMEPATH=$PIG_HOME/bin:$PATH
export PATH
(3)使环境变量生效
source ~/.bash_profile
到这里就安装配置完成,下面我们来看一下它的两种模式。
特点:操作的是Linux的文件
启动命令:pig -x local
当日志中出现以下这一句时,说明启动的是本地模式:
日志:Connecting to hadoop file system at: file:///
特点:链接到HDFS上
集群模式需要设置一个环境变量( ~/.bash_profile文件): PIG_CLASSPATH —-> Hadoop的配置文件所在的目录(注意这里的名字必须叫:PIG_CLASSPATH )
PIG_CLASSPATH=$HADOOP_HOME/etc/hadoop
export PIG_CLASSPATH
配置完成重新生效配置文件source ~/.bash_profile,然后输入pig命令即可,如果出现如下日志说明集群模式配置成功:
日志: Connecting to hadoop file system at: hdfs://bigdata113:9000
pig的常用命令其实和我们hdfs的差不多,具体如下:
ls、cd、cat、mkdir、pwd
copyFromLocal(上传)、copyToLocal(下载)
sh: 调用操作系统的命令(即linux下的命令)
register、define —–> 部署pig的自定义函数的jar包
我们这里随便的用几个,其它的自己再去研究:
register和define在后面讲到自定义函数时会说明。
Pig的数据模型其实就是表,不过和普通的表结构不同:
在开始介绍说明之前,我们需要启动Yarn的HistoryServer(因为Pig会使用到):
mr-jobhistory-daemon.sh start historyserver
可以通过下列网页地址访问historyserver:
URL: http://192.168.171.113:19888/jobhistory
现在开始正式介绍PigLatin。
(1)load 加载数据到表(bag)
(2)foreach 相当于循环,对bag中每一个行tuple进行处理
(3)filter 过滤、相当于where
(4)group by 分组
(5)order by 排序
(6)join 链接(相当于多表查询)
(7)generate 提取列(查询指定列)
(8)union、intersect 集合运算
(9)输出:dump 输出到屏幕 –> 只有9会触发计算,其它的都不会
store 输出到文件
注意:
PigLatin语句跟Spark中算子/方法非常像
PigLatin有些会触发计算,有些不会
类似Spark RDD 算子(方法):
Transformation方法(算子) —–> 不会触发计算
Action方法(算子) ——> 会触发Spark的计算
(1)创建员工表 emp
emp = load '/scott/emp.csv';
查看表结构:
describe emp;
结果如下:
原因是我们并没有指定表结构,(2)中会使用表结构来创建emp表。
(2)创建表,指定schema(结构)
emp = load ‘/scott/emp.csv’ as(empno,ename,job,mgr,hiredate,sal,comm,deptno);
注意:
默认的分隔符:制表符
基于以上两点,我们创建emp表的最终版如下:
emp = load ‘/scott/emp.csv’ using PigStorage(‘,’) as(empno:int,ename:chararray,job:chararray,mgr:int,hiredate:chararray,sal:int,comm:int,deptno:int);
查询的结果如下:
表结构已经清楚了,我们通过dump emp;
来查看一下数据:
顺便创建一下部门表,后面多表查询的示例会用到:
dept = load ‘/scott/dept.csv’ using PigStorage(‘,’) as(deptno:int,dname:chararray,loc:chararray);
(3)查询员工信息:员工号 姓名 薪水(PigLatin每一次查询都需要定义一张新表来保存查询结果)
SQL如下:
select empno,ename,sal from emp;
PigLatin如下(foreach表示对每一行做处理,generate表示提取列):
emp3 = foreach emp generate empno,ename,sal; ----> 不会触发计算
输出结果:
dump emp3;
(4)查询员工信息,按照月薪排序
SQL:
select * from emp order by sal;
PigLatin(order .. by ..定义排序):
emp4 = order emp by sal;
输出的结果如下:
(5)分组:求每个部门的最高工资: 部门号 部门的最高工资
SQL:
select deptno,max(sal) from emp group by deptno;
PigLatin(group .. by .. 定义分组):
1.先分组
emp51 = group emp by deptno;
此时数据如下:
(10,{(7934,MILLER,CLERK,7782,1982/1/23,1300,,10), (7839,KING,PRESIDENT,,1981/11/17,5000,,10), (7782,CLARK,MANAGER,7839,1981/6/9,2450,,10)})
(20,{(7876,ADAMS,CLERK,7788,1987/5/23,1100,,20), (7788,SCOTT,ANALYST,7566,1987/4/19,3000,,20), (7369,SMITH,CLERK,7902,1980/12/17,800,,20), (7566,JONES,MANAGER,7839,1981/4/2,2975,,20), (7902,FORD,ANALYST,7566,1981/12/3,3000,,20)})
(30,{(7844,TURNER,SALESMAN,7698,1981/9/8,1500,0,30), (7499,ALLEN,SALESMAN,7698,1981/2/20,1600,300,30), (7698,BLAKE,MANAGER,7839,1981/5/1,2850,,30), (7654,MARTIN,SALESMAN,7698,1981/9/28,1250,1400,30), (7521,WARD,SALESMAN,7698,1981/2/22,1250,500,30), (7900,JAMES,CLERK,7698,1981/12/3,950,,30)})
从上面的数据也可以看出来,Pig的表结构是一种JSON格式的结构。
2.求每个组(每个部门)工资的最大值(下面命令中的MAX必须大写)
emp52 = foreach emp51 generate group,MAX(emp.sal)
查询最终的结果如下:
(6)查询10号部门的员工
SQL:
select * from emp where deptno=10;
PigLatin(filter过滤数据):
emp6 = filter emp by deptno==10; 注意:两个等号
结果如下:
(7)多表查询:部门名称、员工姓名
SQL:
select d.dname,e.ename from emp e,dept d where e.deptno=d.deptno;
PigLatin(join链接表,用于多表查询):
emp71 = join dept by deptno,emp by deptno;(将两张表链接为一张表)
emp72 = foreach emp71 generate dept::dname,emp::ename;
结果如下:
(8)集合运算: 查询10和20号部门的员工信息
SQL:
select * from emp where deptno=10
union
select * from emp where deptno=20;
注意:
Oracle中,是否任意的集合都可以参与集合运算?答案:不是。必须满足以下条件:
参与集合运算的各个集合,必须列数相同、且类型一致
select deptno,job,sum(sal) from emp group by deptno,job
union
select deptno,to_char(null),sum(sal) from emp group by deptno
union
select to_number(null),to_char(null),sum(sal) from emp;
PigLatin(union是并集的运算):
emp10 = filter emp by deptno==10;
emp20 = filter emp by deptno==20;
emp1020 = union emp10,emp20;
结果如下:
(9)执行WordCount
1.加载数据
mydata = load '/input/data.txt' as (line:chararray);
2.将字符串分割成单词
words = foreach mydata generate flatten(TOKENIZE(line)) as word;
3.对单词进行分组
grpd = group words by word;
4.统计每组中单词数量
cntd = foreach grpd generate group,COUNT(words);
5.打印结果
dump cntd;
注意:后面的操作依赖前面的操作,类似Spark的RDD(依赖关系)
需要用到的jar
/root/training/pig-0.17.0/pig-0.14.0-core-h2.jar
/root/training/pig-0.17.0/lib
/root/training/pig-0.17.0/lib/h2
/root/training/hadoop-2.7.3/share/hadoop/common
/root/training/hadoop-2.7.3/share/hadoop/common/lib
Pig的自定义函数总共有3种,分别是:
下面我们分别会举一个例子。
以根据员工的薪水,判断薪水的级别的需求为例。代码如下:
package pig;
import org.apache.pig.EvalFunc;
import org.apache.pig.data.Tuple;
import java.io.IOException;
/**
* function:根据员工的薪水,判断薪水的级别
* 生产jar之后,在pig中的调用模板如下:
* 调用 emp2 = foreach emp generate empno,ename,sal,运算函数(sal) --> 因为有时候运算函数的参数时是多个,所以它用一个Tuple对象返回,我们可以直接到里面去取
* emp2 = foreach emp generate empno,ename,sal,pig.CheckSalaryGradle(sal);
*
* author by cpMark
* create on 2018/5/12.
*/
/**
* EvalFunc中的泛型T为返回的数据类型
*/
public class CheckSalaryGradle extends EvalFunc<String> {
@Override
public String exec(Tuple tuple) throws IOException {
//Tuple为传递过来的参数的包装类
int sal = (int) tuple.get(0);
if(sal < 1000) return "Grade A";
else if(sal >= 1000 && sal <3000) return "Gradle B";
else return "Gradle C";
}
}
导出jar包,上传到linux上,然后将其注册到pig中,命令如下:
register /root/temp/pig_method.jar
以查询薪水大于2000的员工需求为例。代码如下:
package pig;
import org.apache.pig.FilterFunc;
import org.apache.pig.data.Tuple;
import java.io.IOException;
/**
* function:查询薪水大于2000的员工
* 生产jar之后,在pig中的调用模板如下:
* emp3 = filter emp by 过滤函数(sal)
* emp3 = filter emp by pig.IsSalaryTooHigh(sal);
* author by cpMark
* create on 2018/5/12.
*/
public class IsSalaryTooHigh extends FilterFunc {
@Override
public Boolean exec(Tuple tuple) throws IOException {
//取出薪水
int sal = (int) tuple.get(0);
return sal>2000?true:false;
}
}
打成jar,上传到linux,注册到pig:
register /root/temp/pig_method.jar
运行命令及结果如下:
emp3 = filter emp by pig.IsSalaryTooHigh(sal);
首先我们需要mapreduce的lib,将其下载拷贝过来,可参考MapperReduce的使用及高级功能 中的拷贝目录。然后我们要了解一下加载函数的默认行为:
默认情况下会使用load语句去加载文件,加载成Bag表。默认行为是按行读取,一行数据会被解析成一个Tuple(即Pig中的行)。
当然我们也可以改变这个行为,那就是自定义加载函数啦。
我们以如下数据为测试数据:
I love Beijing
I love China
Beijing is the capital of China
默认的解析行为的结果是 – ()表示行,{}表示表
:
data1 = load ‘/input/data.txt’ as(word:chararray);
我们现在要改变这种行为,代码如下:
package pig;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.InputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.pig.LoadFunc;
import org.apache.pig.backend.hadoop.executionengine.mapReduceLayer.PigSplit;
import org.apache.pig.data.BagFactory;
import org.apache.pig.data.DataBag;
import org.apache.pig.data.Tuple;
import org.apache.pig.data.TupleFactory;
import java.io.IOException;
/**
* function:
* author by cpMark
* create on 2018/5/12.
*/
public class MyLoadFunc extends LoadFunc {
/**
* 定义HDFS的输入流
*/
private RecordReader mReader;
@Override
public void setLocation(String path, Job job) throws IOException {
// 指定HDFS的路径
FileInputFormat.setInputPaths(job, new Path(path));
}
@Override
public InputFormat getInputFormat() throws IOException {
// 输入数据的数类型是什么:字符串
return new TextInputFormat();
}
@Override
public void prepareToRead(RecordReader recordReader, PigSplit pigSplit) throws IOException {
//recordReader代表HDFS的输入流
mReader = recordReader;
}
@Override
public Tuple getNext() throws IOException {
// 对reader中读入的每一行数据进行处理
//数据 I love Beijing
//定义返回结果
Tuple result = null;
try {
//判断是否有数据
if(!mReader.nextKeyValue()){
//没有输入数据,直接返回null
return result;
}
//读入数据
String data = mReader.getCurrentValue().toString();
//分词
String[] words = data.split(" ");
//生成返回的tuple
result = TupleFactory.getInstance().newTuple();
//把每个单词单独生成一个tuple,然后把这些tuple放入bag中,再把这个bag放入result中
//创建一个表
DataBag bag = BagFactory.getInstance().newDefaultBag();
for (String word : words) {
Tuple tuple = TupleFactory.getInstance().newTuple();
//放入单词
tuple.append(word);
//把tuple放入bag
bag.add(tuple);
}
//把bag放入result(Tuple)中
result.append(bag);
}catch (Exception e){
e.printStackTrace();
}
return result;
}
}
打成jar,上传到linux,注册到pig:
register /root/temp/pig_method.jar
调用自己的加载函数加载/input/data.txt,并输出结果:
mydemo = load '/input/data.txt' using pig.MyLoadFunc();
到此为止Pig相关的知识就完成了。下一篇数据分析引擎Sqoop!!!