数据分析引擎Pig

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cp_Mark/article/details/80294184

涉及到的知识点:

  1. Pig的体系结构
  2. 安装和配置(本地模式、集群模式)
  3. Pig的常用命令
  4. Pig的数据模型 – 表结构(重要)
  5. 使用PigLatin语句分析数据
  6. Pig的自定义函数

Pig的体系结构

Pig是一个用来处理大规模数据集的平台,由Yahoo开发,贡献给Apache。它可以简化MapReduce任务的开发,支持PigLatin语句(类似SQL)。其本质是一个翻译器,将PigLatin语句转换为MapReduce任务来执行,并返回。

Pig0.17开始支持转换为Spark任务

下面是Pig的体系结构图:

Pig的体系结构

安装和配置

安装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_HOME

PATH=$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的常用命令

pig的常用命令其实和我们hdfs的差不多,具体如下:

​ ls、cd、cat、mkdir、pwd
copyFromLocal(上传)、copyToLocal(下载)
sh: 调用操作系统的命令(即linux下的命令)
register、define —–> 部署pig的自定义函数的jar包

我们这里随便的用几个,其它的自己再去研究:

Pig常用命令演示

register和define在后面讲到自定义函数时会说明。

Pig的数据模型

Pig的数据模型其实就是表,不过和普通的表结构不同:

Pig的数据模型

PigLatin语句的介绍和使用

在开始介绍说明之前,我们需要启动Yarn的HistoryServer(因为Pig会使用到):

mr-jobhistory-daemon.sh start historyserver

可以通过下列网页地址访问historyserver:

URL: http://192.168.171.113:19888/jobhistory

现在开始正式介绍PigLatin。

常见的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的计算

PigLatin语句的使用

(1)创建员工表 emp

 emp = load '/scott/emp.csv';        

查看表结构:

describe emp;

结果如下:

pig创建emp表1

原因是我们并没有指定表结构,(2)中会使用表结构来创建emp表。

(2)创建表,指定schema(结构)

emp = load ‘/scott/emp.csv’ as(empno,ename,job,mgr,hiredate,sal,comm,deptno);

注意:

  • 默认的列的类型:bytearray
  • 默认的分隔符:制表符

    基于以上两点,我们创建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);

查询的结果如下:

pig创建emp表2和3
表结构已经清楚了,我们通过dump emp;来查看一下数据:

pig创建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;

通过foreach和generate查询emp的固定列

(4)查询员工信息,按照月薪排序

SQL:

 select * from emp order by sal;

PigLatin(order .. by ..定义排序):

emp4 = order emp by sal;

输出的结果如下:

通过order by对emp表排序

(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;   注意:两个等号

结果如下:

filter查询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;

结果如下:

join多表查询结果

(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;

结果如下:

[union结果

(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;  

word_count结果

注意:后面的操作依赖前面的操作,类似Spark的RDD(依赖关系)

Pig自定义函数

需要用到的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

pig自定义运算函数

过滤函数

以查询薪水大于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);

pig自定义过滤函数

加载函数

首先我们需要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);

load加载data.txt默认的结果

我们现在要改变这种行为,代码如下:

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();

hive自定义加载函数结果

到此为止Pig相关的知识就完成了。下一篇数据分析引擎Sqoop!!!

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页