Day13:Hive优化及数据倾斜

Posted 保护胖丁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day13:Hive优化及数据倾斜相关的知识,希望对你有一定的参考价值。

知识点01:回顾

  1. MapReduce处理Hive表数据的规则以及普通表结构的特点是什么?

    • 底层MapReduce的输入:表的最后一级目录

    • 普通表结构

      数据仓库目录/数据库目录/表的目录/数据文件
      
      • 最后一级目录就是表的目录
  2. 分区表的功能、结构和特点是什么?

    • 功能:将大量的数据按照一定规则条件进行划分,将数据分区目录存储,减少了底层MapReduce输入,提高了性能

    • 结构

      数据仓库目录/数据库目录/表的目录/分区目录/分区文件
      
    • 特点:最后一级目录是分区目录

    • 注意:分区表能实现优化的前提查询的过滤条件一定是分区字段【时间】

  3. 如何实现构建分区表?分区的设计是什么级别的?分区的字段是逻辑的还是物理的?

    • 两种方式
      • 静态分区:数据本身按照分区的规则划分好的
        • step1:创建分区表
        • step2:直接加载分区数据
      • 动态分区:数据本身没有做划分
        • step1:创建普通表,加载数据
        • step2:创建分区表,将普通表的数据动态分区写入分区表
    • 分区级别:目录
    • 分区字段:逻辑字段
  4. 分桶表的功能和设计思想是什么?分桶的设计是什么级别的?分桶字段是逻辑的还是物理的?

    • 功能:优化大表join大表过程,提高大表join大表的性能
    • 设计:将数据按照一定的规则划分到不同的文件中,将大文件拆分为多个小文件,实现每个小文件的Map Join
      • 本质:底层的MapReduce的分区,一个桶就是一个reduce,对应一个结果文件
      • 规则:Hash取余
    • 级别:文件
    • 分桶字段:物理字段
  5. Hive中的order by、sort by 、distribute by、cluster by的功能分别是什么?

    • order:全局排序,只能有1个reduce
    • sort:局部排序,多个Reduce,每个Reduce内部排序
    • distribute:指定底层MapReduce的K2的
    • cluster:在指定的是同一个字段的情况下,功能上等价于sort + distribute
  6. parse_url_tuple函数的功能及语法是什么?

    • 功能:用于解析URL
    • 语法:parse_url_tuple(url,… 要解析的字段)
      • HOST
      • PATH
      • QUERY
    • 应用:UDTF函数,一次性解析多个字段
  7. explode函数的功能及语法是什么?

    • 功能:拆分集合类型的元素,将每个元素变成一行
    • 语法:explode(Array | Map)
    • 应用:列转行
  8. lateral view的功能及语法是什么?

    • 功能:将UDTF结果构建成一个类似于视图的临时表,用于与原表进行拼接查询,解决UDTF查询限制问题

    • 语法

      lateral view udtf 别名 as 临时表的列的列名
      
    • 应用:搭配UDTF使用

知识点02:目标

  1. Hive中的函数
    • 了解反射函数、Python脚本辅助处理
    • 掌握
      • 行列转换实现:特殊函数
      • JSON处理函数
      • 开窗函数:窗口聚合、位置偏移、分析函数
        • 实例
  2. Hive优化以及数据倾斜【掌握】
    • 基本优化:参数优化、SQL优化、设计优化【文件格式】
    • 数据倾斜:现象、原因、解决

知识点03:Hive函数:多行转多列

  • 目标掌握行列转换的应用场景及实现多行转多列

  • 路径

    • step1:行列转换的场景
    • step2:多行转多列实现
  • 实施

    • 行列转换的场景
      在这里插入图片描述

      • 分析:基于每个小时分析不同指标【UV,PV,IP】的结果

        select hourstr,count(distinct userid),count(url),count(distinct ip) from table group by hourstr
        
        00		300		1000		90
        01		200		1000		90
        02		100		1000		90
        ……
        23		1000		1000		90
        
      • 需求:得到每个指标不同小时的值:每个小时UV的值

        select * from table where key = 'uv'
        
        00	01	02  ……	23
        300	200	100		1000
        
    • 多行转多列实现

      • 输入

        vim /export/data/r2c1.txt
        
        a	c	1
        a	d	2
        a	e	3
        b	c	4
        b	d	5
        b	e	6
        
        --建表
        create table row2col1(
        col1 string,
        col2 string,
        col3 int
        )row format delimited fields terminated by '\\t';
        --加载数据
        load data local inpath '/export/data/r2c1.txt' into table row2col1;
        
      • 结果

        +-------+----+----+----+--+
        | col1  | c  | d  | e  |
        +-------+----+----+----+--+
        | a     | 1  | 2  | 3  |
        | b     | 4  | 5  | 6  |
        +-------+----+----+----+--+
        
      • 分析

        • 肯定做了分组:按照第一列分组

        • 每一组如何能返回这一组的结果

          • 数据

            col1	col2	col3
            a		c		1
            a		d		2
            a		e		3
            
          • 结果

            +-------+----+----+----+--+
            | col1  | c  | d  | e  |
            +-------+----+----+----+--+
            | a     | 1  | 2  | 3  |
            
          • SQL

            select
            	col1,
            	max(case col2 when 'c' then col3  else 0 end ) as c,
            	max(case col2 when 'd' then col3  else 0 end ) as d,
            	max(case col2 when 'e' then col3  else 0 end ) as e
            from table
            group by col1;
            
      • 函数

        • group by语句中:select后面的字段要么是分组字段要么是聚合函数的结果

          • 每一组只返回一条
        • case when:判断函数

          • 功能:实现多种条件的判断

          • 方式一

            case col
            when  value1 then rs1
            when  value2 then rs2
            ……
            else rsN
            end
            
          • 方式二

            case
            when col = value1 then rs1
            when col = value2 then rs2
            ……
            else rsN
            end
            
      • SQL

        select 
          col1 as col1,
          max(case col2 when 'c' then col3 else 0 end) as c,
          max(case col2 when 'd' then col3 else 0 end) as d,
          max(case col2 when 'e' then col3 else 0 end) as e
        from 
          row2col1
        group by 
          col1;
        
  • 小结

    • case when函数的功能及语法?
    • 功能:判断函数,实现条件的判断
      • 语法
        • case col when value
        • case when col = value
      • 应用:多条件判断场景

知识点04:Hive函数:多行转单列

  • 目标实现多行转单列的SQL开发

  • 实施

    • 输入

      vim /export/data/r2c2.txt
      
      a	b	1
      a	b	2
      a	b	3
      c	d	4
      c	d	5
      c	d	6
      
      --建表
      create table row2col2(
      col1 string,
      col2 string,
      col3 int
      )row format delimited fields terminated by '\\t';
      --加载数据
      load data local inpath '/export/data/r2c2.txt' into table row2col2;
      
    • 结果

      +-------+-------+--------+--+
      | col1  | col2  |  col3  |
      +-------+-------+--------+--+
      | a     | b     | 1,2,3  |
      | c     | d     | 4,5,6  |
      +-------+-------+--------+--+
      
    • 分析

      • 分组:col1,col2
      • 聚合:将每组中的三行变成一行
      • 拼接字符串:将集合中每个元素进行拼接为字符串
    • 函数

      • collect_list/collect_set
        • 功能:聚合函数,将多行的内容合并为一行的内容
        • 语法:
          • collect_list(col):不做去重
          • collect_set(col):做去重
        • 应用:用于将多行转换为单列单行
      • concat / concat_ws
        • 功能:字符串拼接的
        • 语法
          • concat(str1,str2,str3……):不能指定分隔符,有一个为null,整个结果就为null
          • concat_ws(分隔符,str1,str2,str3……):可以指定分隔符,只要一个不为null,结果就不为null
    • SQL

      select
         col1,
         col2,
         concat_ws(",",collect_set(cast(col3 as string))) as col3
      from row2col2
      group by col1,col2;
      
  • 小结

    • concat与concat_ws函数的功能与语法?
      • 功能:字符串拼接
      • 语法
        • concat(str1,str2,str3……)
        • concat_ws(分隔符,str1,str2……)
    • collect_set与collect_list的功能与语法?
      • 功能:将多行合并到一行
      • 语法
        • collect_set(col):做去重
        • collect_list(col):不做去重

知识点05:Hive函数:多列转多行

  • 目标实现多列转多行的SQL开发

  • 实施

    • 输入

      vim /export/data/c2r1.txt
      
      a	1	2	3
      b	4	5	6
      
      create table col2row1(
      col1 string,
      col2 int,
      col3 int,
      col4 int
      )row format delimited fields terminated by '\\t';
      --加载数据
      load data local inpath '/export/data/c2r1.txt' into table col2row1;
      
    • 结果

      +-----------+-----------+-----------+--+
      | _u1.col1  | _u1.col2  | _u1.col3  |
      +-----------+-----------+-----------+--+
      | a         | c         | 1         |
      | b         | c         | 4         |
      | a         | d         | 2         |
      | b         | d         | 5         |
      | a         | e         | 3         |
      | b         | e         | 6         |
      +-----------+-----------+-----------+--+
      
    • 分析

      • 实现行的合并
      • union all
    • 实现

      select col1,'c' as col2,col2 as col3 from col2row1
      union all
      select col1,'d' as col2,col3 as col3 from col2row1
      union all
      select col1,'e' as col2,col4 as col3 from col2row1;
      
  • 小结

    • union all的功能?
      • 功能:实现行的合并
      • 语法:select…… union all select ……
      • 应用:union all与union区别

知识点06:Hive函数:多列转单行

  • 目标实现多列转单行的SQL开发

  • 实施

    • 输入

      vim /export/data/c2r2.txt
      
      a	b	1,2,3
      c	d	4,5,6
      
      create table col2row2(
      col1 string,
      col2 string,
      col3 string
      )row format delimited fields terminated by '\\t';
      --加载数据
      load data local inpath '/export/data/c2r2.txt' into table col2row2;
      
    • 输出

      +-------+-------+-------+--+
      | col1  | col2  | col3  |
      +-------+-------+-------+--+
      | a     | b     | 1     |
      | a     | b     | 2     |
      | a     | b     | 3     |
      | c     | d     | 4     |
      | c     | d     | 5     |
      | c     | d     | 6     |
      +-------+-------+-------+--+
      
    • 分析

      • 由少变多,将一行的内容拆分为多行
    • 函数

      • explode:将一个集合类型的内容中的每一个元素变成一行
    • SQL

      select 
      col1, 
      col2, 
      lv.col3 as col3
      from 
        col2row2 
      lateral view 
        explode(split(col3, ',')) lv as col3;
      
  • 小结

    • explode函数的功能?
      • 功能:用于将集合或者数组类型列中的每一个元素变成一行
      • 语法:explode(Array | map)
      • 应用:将一列转为多行

知识点07:Hive函数:反射函数

  • 目标了解reflect函数的功能及用法

  • 实施

    • 功能

      • 用于在Hive中直接调用Java中类的方法
    • 本质

      • 通过给定的类,反射构建了这个类的对象,通过对象调用方法给用户返回
    • 语法

      reflect(类,方法,参数)
      
    • 测试

      select reflect("java.util.UUID", "randomUUID");
      select reflect("java.lang.Math","max",20,30);
      select reflect("org.apache.commons.lang.math.NumberUtils","isNumber","123");
      
    • 应用:一般用于Java中已经有对应的工具类,可以直接被调用,省去写UDF

  • 小结

    • 了解即可

知识点08:Hive函数:Python脚本

  • 目标了解Hive中如何实现Python脚本辅助处理

  • 实施

    • 数据

在这里插入图片描述

  • 建表加载数据

    --创建原始数据表:用户id、电影id、用户评分、用户的观影时间
    CREATE TABLE u_data (
      userid INT,
      movieid INT,
      rating INT,
      unixtime STRING)
    ROW FORMAT DELIMITED FIELDS TERMINATED BY '\\t'
    STORED AS TEXTFILE;
    
    --加载数据:
    load data local inpath '/export/data/u.data' into table u_data;
    
    --查询数据
    select count(*) from u_data;
    
  • 创建目标表

    --创建新表:用户id、电影id、用户评分、用户的时间是周几
    CREATE TABLE u_data_new (
      userid INT,
      movieid INT,
      rating INT,
      weekday INT)
    ROW FORMAT DELIMITED FIELDS TERMINATED BY '\\t';
    
  • 创建Python脚本

    --创建Python脚本实现将原始表的时间转为对应的星期几
    vim /export/data/weekday_mapper.py
    
    import sys
    import datetime
    
    for line in sys.stdin:
      line = line.strip()
      userid, movieid, rating, unixtime = line.split('\\t')
      weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()
      print '\\t'.join([userid, movieid, rating, str(weekday)])
    
  • 加载脚本,写入数据

    --加载python脚本并将数据写入新表
    add FILE /export/data/weekday_mapper.py;
    
    INSERT OVERWRITE TABLE u_data_new
    SELECT
      TRANSFORM (userid, movieid, rating, unixtime)
      USING 'python weekday_mapper.py'
      AS (userid, movieid, rating, weekday)
    FROM u_data;
    
  • 查询分析

    --统计每周内每天用户观影的次数
    SELECT 
      weekday, 
      COUNT(*)
    FROM 
      u_data_new
    GROUP BY 
      weekday;
    
  • 小结

    • 了解即可

知识点09:Hive函数:JSON处理

  • 目标掌握Hive中处理JSON数据的两种方式

    • 常见的数据格式:结构化数据格式
      • csv:每一列都是用逗号分隔符
      • tsv:每一列都是用制表符分隔符
      • json:专有的JSON格式文件
      • properteies
      • xml
  • 路径

    • step1:JSON函数
    • step2:JSONSerDe
  • 实施

    • JSON函数

      • 创建数据

        vim /export/data/hivedata.json
        
        {"id": 1701439105,"ids": [2154137571,3889177061],"total_number": 493}
        {"id": 1701439106,"ids": [2154137571,3889177061],"total_number": 494}
        
      • 创建表

        create table tb_json_test1 (
        json string
        );
        --加载数据
        load data local inpath '/export/data/hivedata.json' into table tb_json_test1;
        
      • 函数

        • get_json_object:用于解析JSON字符串,指定取出JSON字符串中的某一个元素

          select 
            get_json_object(t.json,'$.id'), 
            get_json_object(t.json,'$.total_number') 
          from 
            tb_json_test1 t ;
          
        • json_tuple:UDTF函数,一次性取出多个JSON字符串的元素

          select 
            t1.json,
            t2.* 
          from 
            tb_json_test1 t1 
          lateral view 
            json_tuple(t1.json, 'id', 'total_number') t2 as c1,c2;
          
    • JSONSerDe

      • 功能:可以直接在加载数据文件的时候解析JSON格式

      • 配置:修改hive-env.sh

      export HIVE_AUX_JARS_PATH=/export/server/hive-2.1.0-bin/hcatalog/share/hcatalog/hive-hcatalog-core-2.1.0.jar

      
      - 重启hiveserver2
      
      - 创建表
      
      ```sql
      create table tb_json_test2 (
      id string,
      ids array<string>,
      total_number int)
      ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
      STORED AS TEXTFILE;
      --加载数据
      load data local inpath '/export/data/hivedata.json' into table tb_json_test2;
      
      • 查询数据

在这里插入图片描述

  • 小结

    • Json处理的函数有哪些?

      • 方式一:JSON函数

        • get_json_object(jsonString,“$.元素名称”):一次只能取一个

        • json_tuple(jsonString,“ . 元 素 名 称 ” , “ .元素名称”,“ ..元素名称”,“$.元素名称”……)

          • UDTF函数
          • 一次取多个
        • 应用:数据中的某个字段是JSON格式的

          id		name		age		info【JSON】
          1		张三		18			{addr:上海……}
          
      • 方式二:通过JSONSerde来加载JSON格式的文件

        • 数据就是一个json文件,每一条数据就是一个完整的JSON字符串

          json1
          json2
          json3
          ……
          

知识点10:Hive函数:窗口聚合函数

  • 目标掌握窗口聚合函数的使用

  • 路径

    • step1:常见的分析需求
    • step2:窗口的基本语法
    • step3:窗口聚合函数
  • 实施

    • 常见的分析需求

      • 需求1:统计得到每个部门薪资最高的那个员工薪水

        select
          deptno,
          max(salary)
        from tb_emp
        group by deptno;
        
      • 需求2:统计得到每个部门薪资最高的前两名的薪水

        select
          deptno,
          max(salary)
        from tb_emp
        group by deptno
        order by salary;
        
      • 问题:分组一组只能返回一条,怎么办?

        • 需求中出现了关键词每个、各个、不同,要么做分组,要么做分区

        • 分组:group by:一组返回一条

        • 分区:窗口函数partition by:一组返回多条

    • 窗口的基本语法

      funName(参数) over (partition by col [order by col]  [window_szie])
      
      • partition by:分区,将相同分区的数据放在一起

      • order by:分区内部按照某个字段进行排序

      • window_szie:窗口大小,指定的是函数处理数据的范围

        -- N preceding :前N行
        -- N following :后N行
        -- current row:当前行
        -- unbounded preceding 表示从前面的起点,第一行
        -- unbounded following:表示到后面的终点,最后一行
        
    • 窗口聚合函数

      max/min/avg/count/sum
      
      • 创建数据

        vim /export/data/window.txt
        
        cookie1,2018-04-10,1
        cookie1,2018-04-11,5
        cookie1,2018-04-12,7
        cookie1,2018-04-13,3
        cookie2,2018-04-13,3
        cookie2,2018-04-14,2
        cookie2,2018-04-15,4
        cookie1,2018-04-14,2
        cookie1,2018-04-15,4
        cookie1,2018-04-16,4
        cookie2,2018-04-10,1
        cookie2,2018-04-11,5
        cookie2,2018-04-12,7
        
      • 创建表

        --建库
        create database db_function;
        use db_function;
        --建表
        create table itcast_f1(
        cookieid string,
        daystr string,
        pv int
        ) row format delimited fields terminated by ',';
        --加载
        load data local inpath '/export/data/window.txt' into table itcast_f1;
        --本地模式
        set hive.exec.mode.local.auto=true;
        
      • 需求1:实现分区内起始行到当前行的pv累加,指定排序

        select 
          cookieid,
          daystr,
          pv,
          sum(pv) over (partition by cookieid order by daystr) as pv1 
        from 
          itcast_f1;
        

在这里插入图片描述

  • 如果指定了partition by和order by,没有指定窗口大小

  • 默认窗口:分区的第一行到当前行

  • 需求2:实现分区内起始行到当前行的pv累加,不指定排序

    select 
        cookieid,
        daystr,
        pv,
        sum(pv) over(partition by cookieid ) as pv2
      from 
        itcast_f1;

在这里插入图片描述

  • 需求3:实现分区内起始行到当前行的pv累加,指定窗口
      select 
        cookieid,
        daystr,
        pv,
        sum(pv) over (partition by cookieid order by daystr rows between unbounded preceding and current row) as pv3
      from 
      itcast_f1;

在这里插入图片描述

- 需求4:实现分区内指定前N行到当前行的pv累加

  ```sql
  select 
    cookieid,
  daystr,
    pv,
  sum(pv) over (partition by cookieid order by daystr rows between 3 preceding and current row) as pv4
  from 
    itcast_f1;
  ```

在这里插入图片描述

  • 需求5:实现分区内指定前N行到后N行的pv累加
      select 
        cookieid,
        daystr,
        pv,
        sum(pv) over(partition by cookieid order by daystr rows between 3 preceding and 1 following) as pv5
      from 
        itcast_f1;

在这里插入图片描述

  • 需求6:实现分区内指定当前行到最后一行的pv累加

    select 
      cookieid,
      daystr,
      pv,
      sum(pv) over(partition by cookieid order by daystr rows between current row and unbounded following) as pv6
    from 
      itcast_f1;
    

在这里插入图片描述

  • 小结

    • 窗口函数的语法及关键字的含义是什么?

      • 语法

        functionName(参数) over (partition by col order by col 【window_size】)
        
      • 含义

        • partition by:分区,将相同分区的数据放在一起
        • order by:排序,基于分区内部排序
        • window_size:基于分区内部计算的窗口大小

知识点11:Hive函数:窗口位置函数

  • 目标掌握窗口位置函数的使用

  • 路径

    • step1:first_value
    • step2:last_value
    • step3:lag
    • step4:lead
  • 实施

    • first_value

      • 功能:取每个分区内某列的第一个值

      • 语法:FIRST_VALUE(col) over (partition by col1 order by col2)

      • 示例

        use db_emp;
        --取每个部门薪资最高的员工姓名
        select
          empno,
          ename,
          salary,
          deptno,
          FIRST_VALUE(ename) over (partition by deptno order by salary desc) as first
        from
          db_emp.tb_emp;
        

    在这里插入图片描述

    • last_value

      • 功能:取每个分区内某列的最后一个值

      • 语法:LAST_VALUE() over (partition by col1 order by col2)

      • 注意:一定要 注意默认窗口的计算范围

      • 示例

      --取每个部门薪资最低的员工编号
      select
        empno,
        ename,
        salary,
        deptno,
        LAST_VALUE(empno) over (partition by deptno order by salary desc) as last
      from
        db_emp.tb_emp; 
       无法实现
      --取每个部门薪资最低的员工编号
      select
        empno,
        ename,
        salary,
        deptno,
        LAST_VALUE(empno) over (partition by deptno order by salary desc rows between unbounded preceding and unbounded following) as last
      from
        db_emp.tb_emp;
  • lag

    • 功能:取每个分区内某列的前N个值

    • 语法:LAG(col,N,defaultValue) over (partition by col1 order by col2)

      • col:取分区内某一列的值
      • N:向前偏移N个单位
      • defaultValue:如果取不到的默认值
  • 示例

      select
        empno,
        ename,
        salary,
        deptno,
        LAG(salary,1,0) over (partition by deptno order by salary) as deptno2
      from
        db_emp.tb_emp;

在这里插入图片描述

  • lead

    • 功能:取每个分区内某列的后N个值

    • 语法:LEAD(col,N,defaultValue) over (partition by col1 order by col2)

      • col:取分区内某一列的值
    • N:向后偏移N个单位

      • defaultValue:如果取不到的默认值
    • 示例

      select
        empno,
        ename,
        salary,
        deptno,
      LEAD(salary,1,0) over (partition by deptno order by salary) as deptno2
      from
        db_emp.tb_emp;
      

在这里插入图片描述

  • 小结

    • first_value的功能及语法?
      • 功能:取分区内部某一列第一条
      • 语法:first_value(col)
    • last_value的功能及语法?
      • 功能:取分区内部某一列最后一条
      • 语法:last_value(col)
    • lag的功能及语法?
      • 功能:取分区内某一列的向前偏移N个单位的值
      • 语法:lag(col,N,default)
    • lead的功能及语法?
      • 功能:取分区内某一列的向后偏移N个单位的值
      • 语法:lead(col,N,default)

知识点12:Hive函数:窗口分析函数

  • 目标掌握窗口分析函数的使用

  • 路径

    • step1:row_number
    • step2:rank
    • step3:dense_rank
    • step4:ntil
  • 实施

    • row_number

      • 功能:用于实现分区内记录编号

      • 语法:row_number() over (partition by col1 order by col2)

      • 特点:如果值相同,继续编号

      • 示例

        --统计每个部门薪资最高的前两名
        select
          empno,
          ename,
          salary,
          deptno,
          row_number() over (partition by deptno order by salary desc) as numb
        from
          db_emp.tb_emp;
        
    • rank

      • 功能:用于实现分区内排名编号[会留空位]

      • 语法:rank() over (partition by col1 order by col2)

      • 特点:如果值相同,编号相同,会留下空位

      • 示例

        --统计每个部门薪资排名
        select
          empno,
          ename,
          salary,
          deptno,
          rank() over (partition by deptno order by salary desc) as numb
        from
          db_emp.tb_emp;
        

hive数据倾斜及解决方案

Hive架构倾斜优化sql及常见问题

hive数据倾斜及处理

大数据SQL优化之数据倾斜解决案例全集

Kylin3.1.2实战--调优CUBE构建数据倾斜优化

大数据面试题----HIVE的调优及数据倾斜