Hive进阶-- Hive SQLSpark SQL和 Hive on Spark SQL

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hive进阶-- Hive SQLSpark SQL和 Hive on Spark SQL相关的知识,希望对你有一定的参考价值。


1.Hive SQL

1.1 基本介绍

概念

Hive由Facebook开发,用于解决海量结构化日志的数据统计,于2008年贡献给 Apache 基金会。

Hive是基于Hadoop的数据仓库工具,可以将结构化数据映射为一张表,提供类似SQL语句查询功能

本质:将Hive SQL转化成MapReduce程序。

与关系型数据库的对比


项目

Hive

关系型数据库

数据存储

HDFS

磁盘

查询语言

HQL

SQL

处理数据规模

分区

支持

支持

扩展性

非常有限

数据写入

支持批量导入/单条写入

支持批量导入/单条写入

索引

0.7版本后添加了索引(不怎么使用)

支持复杂索引

执行延迟

数据加载模式

读时模式(快)

写时模式(慢)

应用场景

海量数据查询

实时查询


PS:

读时模式: Hive 在加载数据到表中的时候不会校验.
写时模式: mysql 数据库插入数据到表的时候会进行校验.

适用场景

Hive只适合用来做海量离线的数据统计分析,也就是数据仓库。

1.2 架构



【Hive进阶】--


(1)Client(用户接口)
JDBC(java访问Hive);ODBC(Open Database Connectivity);Client(hive shell);WEBUI(浏览器访问Hive)

(2)元数据(MetaStore)
元数据包括:表名、表所属的数据库(默认是default)、表的拥有者、列/分区字段,标的类型(表是否为外部表)、表的数据所在目录。这是数据默认存储在Hive自带的derby数据库中,推荐使用MySQL数据库存储MetaStore。

(3)Hadoop/HBase集群
使用 HDFS 进行存储数据,使用 MapReduce 进行计算。

(4)Driver(驱动器)
解析器(SQL Parser):将SQL字符串换成抽象语法树AST,对AST进行语法分析,判断表是否存在、字段是否存在、SQL语义是否有误。
优化器(Query Optimizer):将逻辑计划进行优化。
编译器(Physical Plan):将AST编译成逻辑执行计划。
执行器(Execution):把执行计划转换成可以运行的物理计划。对于Hive来说默认就是Mapreduce任务。

PS:从 hive-0.10.x开始,少数 Hql 不需要执行 MR,但是需要开启参数:

hive.fetch.task.conversion = more

添参数后,简单的查询,如select,不带count,sum,group by的 SQL,都不走map/reduce,直接读取hdfs文件进行filter过滤。

2.Spark SQL

2.1 基本介绍

概念

Spark SQL主要用于结构型数据处理,它的前身为Shark,在Spark 1.3.0版本后才成长为正式版,可以彻底摆脱之前Shark必须依赖HIVE的局面。与过去的Shark相比,一方面Spark SQL提供了强大的DataFrame API,另一方面则是利用Catalyst优化器,并充分利用了Scala语言的模式匹配与quasiquotes,为Spark提供了更好的查询性能。

在Databricks工程师撰写的论文《​​Spark SQL: Relational Data Processing in Spark​​》中,给出了Spark SQL与Shark以及Impala三者间的性能对比,如下图所示:



【Hive进阶】--


Michael Armbrust、Yin Huai等人写的博客《​​Deep Dive into Spark SQL’s Catalyst Optimizer​​》简单介绍了Catalyst的优化机制。

特点

与 Spark 集成

Spark SQL 查询与 Spark 程序集成。Spark SQL 允许我们使用 SQL 或可在 Java、Scala、Python 和 R 中使用的 DataFrame API 查询 Spark 程序中的结构化数据。要运行流式计算,开发人员只需针对 DataFrame / Dataset API 编写批处理计算, Spark 会自动增加计算量,以流式方式运行它。这种强大的设计意味着开发人员不必手动管理状态、故障或保持应用程序与批处理作业同步。相反,流式作业总是在相同数据上给出与批处理作业相同的答案。

统一数据访问

DataFrames 和 SQL 支持访问各种数据源的通用方法,如 Hive、Avro、Parquet、ORC、JSON 和 JDBC。这将连接这些来源的数据。这对于将所有现有用户容纳到 Spark SQL 中非常有帮助。

Hive兼容性

Spark SQL 对当前数据运行未经修改的 Hive 查询。它重写了 Hive 前端和元存储,允许与当前的 Hive 数据、查询和 UDF 完全兼容。

标准连接

连接是通过 JDBC 或 ODBC 进行的。JDBC和 ODBC 是商业智能工具连接的行业规范。

性能和可扩展性

Spark SQL 结合了基于成本的优化器、代码生成和列式存储,在使用 Spark 引擎计算数千个节点的同时使查询变得敏捷,提供完整的中间查询容错。Spark SQL 提供的接口为 Spark 提供了有关数据结构和正在执行的计算的更多信息。在内部,Spark SQL 使用这些额外信息来执行额外优化。Spark SQL 可以直接从多个来源(文件、HDFS、JSON/Parquet 文件、现有 RDD、Hive 等)读取。它确保现有 Hive 查询的快速执行。

下图描述了 Spark SQL 与 Hadoop 相比的性能。Spark SQL 的执行速度比 Hadoop 快 100 倍。



【Hive进阶】--


适用场景

实时计算 & 离线批量计算。

2.2 架构



【Hive进阶】--


3.Hive on Spark SQL

3.1基本介绍

Hive on Spark是由Cloudera发起,由Intel、MapR等公司共同参与的开源项目,其目的是把Spark作为Hive的一个计算引擎,将Hive的查询作为Spark的任务提交到Spark集群上进行计算。

通过该项目,可以提高Hive查询的性能,同时为已经部署了Hive或者Spark的用户提供了更加灵活的选择,从而进一步提高Hive和Spark的普及率。参考:​​javascript:void(0)​​

3.2 架构



【Hive进阶】--


参考:​​http://people.csail.mit.edu/matei/papers/2015/sigmod_spark_sql.pdf​

(1)Client
执行 bin/spark-sql 或者 bin/spark-shell 进入客户端
jdbc/odbc 的server
用户编程的 jar 包

(2)Metastore
元数据管理中心,即为 Hive 的metastore

(3)Driver
1)sql parser:基于antlr框架对 sql解析,生成抽象语法树
2)Analyzer:通过分析器,结合catalog,把logical plan和实际的数据绑定起来,将unresolve logical plan生成 logical plan
3)Catalyst optimizer
a.Logic plan:由 Analyzer 生成,称为 Unresolved Logical Plan
b.Cache Manager:缓存替换:替换有相同结果的 logical plan,在分析器之后,optimizer logic plan 之前发生
c.Optimizer logic plan:基于规则的优化;优化执行器 RuleExecutor 生成 spark plan
4)Physical plan:获取一个逻辑计划,并使用与 Spark执行引擎匹配的物理操作来生成一个或多个物理计划,然后使用CBO的模型在这个多个物理计划中选择最优的那个
5)Execution:使用 QueryExecution 执行物理计划,此时则调用 SparkPlan的execute() 方法,生成 RDD

(4)RDD
弹性分布式数据集,注意其5大特性:一个分区列表、func 作用于每个分区、宽窄依赖、就近原则、tuple

(5)HDFS/对象存储(如OSS、S3、COS、OBS 等)
HDFS: Hadoop Distributed File System -- Apache
OSS: Object Storage Service -- 阿里云
COS: CloudObjectStorage -- 腾讯云
OBS: Object Storage Service -- 华为云
S3: Simple Storage Service -- Amazon

4.Hive SQL和 Spark sql 的区别

相同点

都支持ThriftServer服务,为JDBC提供解决方案
都支持静态分区、动态分区
都支持多种文件存储格式:text、parquet、orc等
都支持 UDF 函数

不同点

Spark SQL 是 Spark 的一个库文件
Spark SQL 中 schema 是自动推断的
Spark SQL 支持标准 SQL 语句,也支持 HQL 语句等(即支持SQL方式开发,也支持HQL开发,还支持函数式编程(DSL)实现SQL语句)
Spark SQL 支持 Spark Datasets 和 Spark DataFrames 的操作,而 Hive SQL 仅支持 Hive 表的操作。
Spark SQL 支持使用 Spark API 和 SQL 同时进行数据处理,而 Hive SQL 仅支持 SQL 操作。
Hive中必须有元数据,一般由 MySql 管理,必须开启 metastore 服务
Hive 中在建表时必须明确使用 DDL 声明 schema

Hive 查询进阶笔记

总结工作中遇到的 Hive-sql 难点问题。

目录:

  1. Hive 查询性能优化

  2. 求两组数据的并集、交集、差集

  3. Hive 中查询用户留存率

  4. Hive 中的窗口函数

Hive查询性能优化

  1. 什么是数据倾斜
    当我们在 Hive上进行查询时,因为数据的分散度不够, 导致大量数据集中在一台或者几台服务器上, 导致数据的计算速度远远低于平均计算速度, 计算过程特别耗时。

  2. 数据倾斜的表现
    任务进度长时间维持在 99%,查看任务监控页面,发现只有少量子任务未完成。

  3. 如何避免数据倾斜

  • sql 优化

  • 业务逻辑优化

  1. 优化方法:

  • 当数据量特别大时,用 group by 代替 count(distinct)

写法:

# 求客户端每日的去重 uv
with a1 as (
    select 
        hit_date,
        user_account 
    from
        android_data
    where
        hit_date between '2018-10-01' and'2018-10-03'
    group by
        hit_date, user_account
)
select  
    hit_date,
    count(user_account) as uv 
from
    a1 
group by
    hit_date
order by 
    hit_date

  • join 优化

将条目少的表/子查询放在 Join 操作符的左边。原因是在 Join 操作的 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,将条目少的表放在左边,可以有效减少发生错误的几率。

写法:

# a, b表中, b 表最小
select
    a.price_close, b.price_close
from
    b join a on b.ymd = a.ymd AND b.symbol = a.symbol
where
    a.symbol = 'apple'

  • 避免 union all 子查询中使用 group by 【替换 count(distinct) 除外】、count(distinct)、max、min等。

写法:

use computer_view;
with a1 as (
        select
            user_account,
            hit_date
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-13'
            and
            nbtn_name like "%支付宝%"
        union all 
        select
            user_account,
            hit_date
        from
            client_ios_log_view
        where
            hit_date between '2018-12-01' and '2018-12-13'
        and
        nbtn_name like "%支付宝%")
select
    hit_date,
    count(user_account) as pv
from
    a1
group by
    hit_date

  • 避免不同数据类型进行关联

使用 CAST 函数对数据类型进行转换,语法为 cast(value AS TYPE)

写法:

select 
    a.price_close,
    b.price_close
from
    a join b  on a.user_id = cast(b.user_id as string)
where
    hit_date between '2018-11-01' and '2018-11-02'
    and 
    a.symbol = 'apple'

  • 无效 ID 在关联时的数据倾斜问题

把空值的 key 变成一个字符串加上随机数,就能把倾斜的数据分到不同的 reduce 上 ,解决数据倾斜问题。
需要用到 case When … Else…End 语法

写法 1:

Select
    *
From 
    a Join  b
On
     a.user_id is not null
And 
    a.user_id = b.user_id
Union all
Select
    * 
froM
    a
where
    a.user_id is null

写法 2:

Select
    *
From
    a left out Join b
On 
Case when 
    a.user_id is null 
then 
    concat(‘dp_hive’,rand() ) 
else 
    a.user_id = b.user_id end;

  • 在查询中, 避免使用 select *, 使用条件限制取需要的列。

  • 在使用 Join 进行外关联时, 将副表的过滤条件写在 where 后面,会先全表关联, 再进行过滤, 这样会耗费资源。

写法 1:

SELECT
    a.price_close, b.price_close
FROM
    b JOIN a ON b.ymd = a.ymd AND b.symbol = a.symbol
WHERE
    s.symbol = 'APPLE'

写法 2:

# 正确的写法是将 where 条件卸载 on 后面
SELECT
    a.price_close, b.price_close
FROM
    b JOIN a ON ( b.ymd = a.ymd AND b.symbol = a.symbol and s.symbol = 'APPLE')

求两组数据的交集, 并集, 差集

  1. 并集 - union 与 union all

union, 结果包含所有行, 并删除重复行
unoin all, 结果包含所有行, 但不删除重复行

union 语句用法:

use computer_view;
with a1 as (
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-02'
            and
            nbtn_name like "%支付宝%"
        union 
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-02'
        and
        nbtn_name like "%手淘%")
select
    count(user_account) as pv
from
    a1

点击支付宝或者手淘活动的人数总共有 435499 人


union all 语句用法:

use computer_view;
with a1 as (
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-02'
            and
            nbtn_name like "%支付宝%"
        union all 
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-02'
        and
        nbtn_name like "%手淘%")
select
    count(user_account) as pv
from
    a1

点击支付宝或者手淘活动的次数为 665935


  1. 交集 - intersect 函数

use computer_view;
with a1 as (
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-02'
            and
            nbtn_name like "%支付宝%"
        intersect
        select
            user_account
        from
            client_ios_log_view
        where
            hit_date between '2018-12-01' and '2018-12-02'
        and
        nbtn_name like "%手淘%")
select
    count(user_account) as pv
from
    a1

点击支付宝又点击手淘活动的人数为 66174


  1. 差集 - except 函数 与 join 写法

写法 1:

use computer_view;
with a1 as (
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-25'
            and
            nbtn_name like "%支付宝%"
        except
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-25'
        and
        nbtn_name like "%手淘%")
select
    count(user_account) as pv
from
    a1

写法 2:

use computer_view;
with a1 as (
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-01' and '2018-12-25'
            and
            nbtn_name like "%支付宝%"),
a2 as (
        select
            user_account
        from
            client_android_log_view
        where
            hit_date between '2018-12-20' and '2018-12-25'
        and
        nbtn_name like "%支付宝%")
select
    count(distinct a1.user_account) as pv
from
    a1 left outer join a2 
    on a1.user_account = a2.user_account
    and a2.user_account is  null 

只参加支付宝活动, 没有参加手淘活动的人数为 369325
在求差集时, 需要注意前后顺序, 否则会出现逻辑错误
可以发现, 差集 + 交集 =并集, 369325 + 66174 = 435499


HIVE中查询留存率

求11月10-15号每天的 1、3、7日留存率

方法 1.

  1. 统计每天的 uv

  2. 统计上一天与本天 uv 的交集用户数

  3. 算出留存率

方法 2:

  1. 统计每天的 uv

  2. 使用 date_add 函数, 一次性求出 10-15 号每一天的次 1、3、7日留存

  3. 算出留存率

---

# 统计10-15号每天 uv
SELECT  
    hit_date,
    count(distinct user_account) as uv
FROM
    computer_view.client_android_log_view
WHERE   
    hit_date between  '2018-11-10' and '2018-11-15'
group BY 
    hit_date
order BY 
    hit_date

# 统计 10-15 号每天的次日留存数, 统计次 3、7 日留存只需将 1 换为 3、7
with a1 as (
    select 
        user_account,
        hit_date
    from 
        computer_view.client_android_log_view
    where 
        hit_date between  '2018-11-10' and '2018-11-15'
),
a2 as (
        select 
        user_account,
        hit_date
    from 
        computer_view.client_android_log_view
    where 
        hit_date between '2018-11-10' and '2018-11-25'
)
select 
    a1.hit_date,
    count(distinct a1.user_account) as uv
from
    a1 join a2 on a1.user_account = a2.user_account
WHERE   
    a2.hit_date =  date_add(a1.hit_date, 1
group by 
    a1.hit_date
order BY
    a1.hit_date

HIVE中的窗口函数

  1. over 函数

语法: over(partition by ….)
作用: 与聚合函数 sum(), count(), avg() 等结合使用, 实现分组聚合的功能

# 根据日期和 mac_id 进行分组求每组的数量和, 并按日期排序
select
    hit_date, 
    mac_id,
    mac_color,
    day_num,
    sum(day_num) over(partition by hit_date, mac_id order by hit_date) as sum_num
from
    test.datas

结果:

hit_date mac_id mac_color day_num sum_num
20171011 1292 金色 11 89
20171011 1292 黑色 19 89
20171011 1292 粉金 58 89
20171011 1292 金色 1 89
20171011 2013 金色 9 22
20171011 2013 金色 3 22
20171012 1292 金色 5 18
20171012 1292 粉金 1 18
20171012 2013 粉金 1 7
20171012 2013 金色 6 7
20171013 1292 黑色 1 1
20171013 2013 粉金 2 2

与 group by 语句的区别:

grou by 字段只能显示与分组聚合相关的字段, 而 over(partition by) 可以显示所有字段

# group by 语句
select
    hit_date,
    mac_id,
    sum(day_num) 
from
    test.data
group by
    hit_date,
    mac_id
order by
    hit_date

结果:

day_id mac_id sum_num
20171011 124609 1
20171011 20130 22
20171011 12922 89
20171012 12922 18
20171012 20130 7
20171013 12922 1
20171013 20130 2

  1. LAG 和 LEAD 函数

语法: 
LAG(col,n,DEFAULT)  用于统计窗口内往上第n行值; 
LEAD(col,n,DEFAULT)  用于统计窗口内往下第n行值

# 计算 11 月 1-10 号, 不同日期同一用户登陆客户端 pv 量对比

with a1 as (select
    user_account,
    count(user_account) as pv,
    hit_date
from
    computer_view.client_android_log_view
where
    hit_date between '2018-11-01' and'2018-11-10'
group by
    user_account, hit_date)
select
    user_account,
    a1.hit_date,
    a1.pv,
    lag(a1.pv, 1over (partition by user_account order by user_account, a1.hit_date) as pv1,
    lead(a1.pv, 1over(partition by user_account  order by user_account, a1.hit_date) as pv2
from
    a1
limit 100

  1. first_value() 和 last_value() 函数 

说明:
first_value() :比较每个用户浏览次数与第一天浏览次数进行比较,查询返回当前浏览次数以及第一天浏览次数
last_value() : 比较每个用户浏览次数与最新一天浏览次数进行比较,查询返回当前浏览次数以及最新一天浏览次数

with a1 as (select
    distinct user_account,
    count(user_account) as pv,
    hit_date
from
    computer_view.client_android_log_view
where
    hit_date between '2018-11-01' and'2018-11-10'
group by
    user_account, hit_date)
select
    distinct user_account,
    a1.hit_date,
    a1.pv,
    first_value(a1.pv) over (partition by user_account order by user_account, a1.hit_date) as pv1,
    last_value(a1.pv) over(partition by user_account  order by user_account, a1.hit_date) as pv2
from
    a1
limit 100

  1. rank、dense_rank、 row_number 排序函数

说明:
rank 函数, 返回数据项在分组中的排名, 排名相等的会留下空位, 如1、2、2、4
dense_rank 函数, 返回数据项在分组中的排名, 排名相等的不会留下空位, 如1、2、2、3
row_number 函数, 返回数据项在分组中的排名, 排名不管数据是否相等, 如1、2、3、4

select 
    a,
    row_number() over(order by b) row_number,
    rank() over(order by b) rank,
    dense_rank() over(order by b) dense_rank 
from 
    lijie.test_rank

结果:

a row_number rank dense_rank
A 1 1 1
C 2 2 2
D 3 3 3
B 4 3 3
E 5 5 4
F 6 6 5
G 7 7 6

以上就是对自己工作中常见的 Hive-sql 查询语句总结, 希望能够给你带来一些启发。

参考资料:
博客:过往记忆 - hive

可点击【阅读原文】跳转博客网址


以上是关于Hive进阶-- Hive SQLSpark SQL和 Hive on Spark SQL的主要内容,如果未能解决你的问题,请参考以下文章

Hive_进阶

Hive总结Hive查询进阶

Hive学习之五 《Hive进阶—UDF操作案例》 详解

Hive 查询进阶笔记

Hive进阶_Hive数据查询

Hive进阶操作看过来