Hive执行计划之hive依赖及权限查询和常见使用场景
概述
Hive查看执行计划的命令中还有两个不怎么常用但很重要的命令,接下来详细介绍一下。
有一个问题:如何在hiveSQL执行之前就探查到这段逻辑的血缘依赖关系?
hive血缘是很多生产级数仓必须要提供的功能,大多数解决方案都是使用hive hooks的方法通过SQL执行后解析得到hive表的依赖关系。
这个方案能细粒度到字段级依赖,属于很完善的一个解决方案,但有很多场景我们需要在SQL执行之前就得到依赖关系,那么如何解决的呢?
1.explain dependency的查询与使用
explain dependency 提供了这样的一个解决方案,它可以查询一段SQL需要的数据来源,以JSON的形式展现结果数据。里面主要包含两部分内容:
input_tables:描述一段SQL依赖的数据来源表,里面存储的是hive表名的列表,格式如下:
{"tablename":"库名@表名","tabletype":"表的类型(外部表/内部表)"}input_partitions:描述一段SQL依赖的数据来源表分区,里面存储的是分区名称的列表,格式如下:
{"partitionName":"库名@表名@分区列=分区列的值"}如果查询的表为非分区表,则显示为空。
可以通过以下例子来进行比对,其中例1是查询非分区普通表SQL的explain dependency,例2是查询分区表SQL的explain dependency。
例1 使用explain dependency查看SQL非分区普通表。
explain dependency
-- 统计年龄小于30岁各个年龄里,昵称里带“小”的人数
select age,count(0) as num from temp.user_info_all_no
where age < 30 and nick like '%小%'
group by age;
输出结果内容:
{"input_tables":[{"tablename":"temp@user_info_all_no","tabletype":"MANAGED_TABLE"}],"input_partitions":[]}
例2 使用explain dependency查看SQL查询分区表。
explain dependency
-- 统计年龄小于30岁各个年龄里,昵称里带“小”的人数,其中ymd字段为分区字段
select age,count(0) as num from temp.user_info_all where ymd >= '20230501'
and age < 30 and nick like '%小%'
group by age;
输出结果内容:
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"}]}
2.借助explain dependency解决一些常见问题
explain dependency的使用场景有以下几个:
场景一,快速排除。快速排除因为读不到相应分区的数据而导致任务数据输出异常。例如,在一个以天为分区的任务中,上游任务因为生产过程不可控因素出现异常或者空跑,导致下游任务引发异常。通过这种方式,可以快速查看SQL读取的分区是否出现异常。
场景二,理清表的输入,帮助理解程序的运行,特别是有助于理解有多重子查询,多表连接的依赖输入。
场景三,提前通过解析hiveSQL脚本进行血缘依赖解析,用于一些定制化数据平台工具开发中的血缘构建。
explain dependency的使用能帮助开发者解决哪些问题呢?
2.1.识别看似等价的SQL代码实际上是不等价的:
对于接触SQL不久的程序员来说,很多人容易将
select * from a left join b on a.no=b.no and a.f>1 and a.f<3;
这段逻辑等价于 select * from a left join b on a.no=b.no where a.f>1 and a.f<3;
这两段的逻辑的区别是在多表left join的时候where 后加条件是否等价与on后面加条件。
我们通过实例来看看其中的区别:
例3 使用explain dependency识别看似等价的SQL代码。
-- 代码1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and a.ymd >= '20230501' and a.ymd <= '20230502';
-- 代码2
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd
where a.ymd >= '20230501' and a.ymd <= '20230502';
输出结果内容:
// 代码1输出结果
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}
// 代码2输出结果
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}
通过以上输出结果可以看出,上面例子里的两段SQL其实并不等价。在left join(left outer join)的连接条件中加入非等值的过滤条件后,这里特指作用于a表,也就是连接的基表,并没有将左外连接的左右两个表按照过滤条件进行过滤,左外连接在执行时会读取所有分区数据,然后进行关联数据过滤操作。
left outer join 针对左表非等值条件on和where查询数据on条件查询数据大于where条件查询数据。
下面查看left outer join对右表的过滤条件实例:
例4 使用explain dependency识别left outer join 右表过滤非等值条件区别
-- 代码1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and b.ymd >= '20230501' and b.ymd <= '20230502';
-- 代码2
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd
where b.ymd >= '20230501' and b.ymd <= '20230502';
输出结果内容:
// 代码1输出结果,on后跟非等值条件
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}
// 代码2输出结果,where后跟非等值条件
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}
可以看到left outer join 针对右表非等值条件on和where查询数据左表都是全表扫描,右表on条件是条件过滤,where条件是全表扫描。
接下来对inner join,right outer join,full outer join进行测试。会发现
inner join 的类似针对左右表非等值条件on和where查询数据是等价的。
right outer join和left join相反。
full outer join都是全表扫描。
那么可以很好的判断出一下两段SQL的过滤条件数据读取范围是完全不一样的。就不贴执行结果了。
例5 left outer join下的对左表和右表不等值条件过滤。
-- 代码1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and a.ymd >= '20230501' and a.ymd <= '20230502';
-- 代码2
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd and b.ymd >= '20230501' and b.ymd <= '20230502';
以上不同join类型数据查询范围不一致主要原因和hive对join和where的谓词下推支持不同有关。通过explain dependency可以直接验证hive对join和where进行谓词下推规则的验证。

谓词下推可详细查看什么是谓词下推,看这一篇就够了
2.2 通过explain dependency验证将过滤条件在不同位置的查询区别
如果要使用外连接并需要对左右两个表进行条件过滤,做好的方式是将过滤条件放到就近处,即如果已经知道表数据过滤筛选条件,那么在使用该表前,就先用过滤条件进行过滤,然后进行其他操作。
一些SQL内置优化器会做一些过滤下推优化,但部分条件还是不会进行下推。所以我们在写SQL时尽量养成先过滤而后进行其他操作(聚合,关联)的习惯。
可以看如下实例:
例6 left outer join对左表过滤数据的优化对比。
-- 代码1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd
where a.ymd >= '20230501' and a.ymd <= '20230502';
-- 代码2
explain dependency
select a.uid from (
select uid,ymd from temp.user_info_all
-- 在子查询内部进行过滤
where ymd >= '20230501' and ymd <= '20230502'
) a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd;
-- 代码3
explain dependency
select a.uid from (
select uid,ymd from temp.user_info_all
-- 在子查询内部进行过滤
where ymd >= '20230501' and ymd <= '20230502'
) a
left outer join (
select uid,ymd from temp.user_act_info
where ymd >= '20230501' and ymd <= '20230502'
) b
on a.uid = b.uid and a.ymd = b.ymd;
执行结果:
//代码1,左右表都进行了过滤
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}
//代码2,右表进行了全表扫描
{"input_tables":[{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}
//代码3,左右表都进行了过滤
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}
可以看到left outer join对左表过滤数据的优化中代码1片段等价于代码3片段,即两表都在就近处都过滤。
例7 left outer join对右表过滤数据的优化对比。
-- 代码1
explain dependency
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd
where b.ymd >= '20230501' and b.ymd <= '20230502';
-- 代码2
explain dependency
select a.uid from (
select uid,ymd from temp.user_info_all
-- 在子查询内部进行过滤
where ymd >= '20230501' and ymd <= '20230502'
) a
left outer join (
select uid,ymd from temp.user_act_info
where ymd >= '20230501' and ymd <= '20230502'
) b
on a.uid = b.uid and a.ymd = b.ymd;
-- 代码3
explain dependency
select a.uid from temp.user_info_all a
left outer join (
select uid,ymd from temp.user_act_info
where ymd >= '20230501' and ymd <= '20230502'
) b
on a.uid = b.uid and a.ymd = b.ymd;
执行结果内容:
// 代码1 ,左右表都进行了全表扫描
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230503"},{"partitionName":"temp@user_act_info@ymd=20230606"}]}
//代码2,左右表都进行了过滤
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"}]}
//代码3,右表都进行了过滤
{"input_tables":[{"tablename":"temp@user_info_all","tabletype":"MANAGED_TABLE"},{"tablename":"temp@user_act_info","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"temp@user_act_info@ymd=20230501"},{"partitionName":"temp@user_act_info@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230430"},{"partitionName":"temp@user_info_all@ymd=20230501"},{"partitionName":"temp@user_info_all@ymd=20230502"},{"partitionName":"temp@user_info_all@ymd=20230503"},{"partitionName":"temp@user_info_all@ymd=20230504"},{"partitionName":"temp@user_info_all@ymd=20230505"},{"partitionName":"temp@user_info_all@ymd=20230529"}]}
可以看到left outer join对右表过滤数据的优化中代码2是最优,代码3次之,代码1最差。
3.查看SQL操作涉及到的相关权限信息
通过explain authorization可以知道当前SQL访问的数据来源(INPUTS) 和数据输出(OUTPUTS),以及当前Hive的访问用户 (CURRENT_USER)和操作(OPERATION)。
可以看以下实例:
例8 使用explain authorization查看权限相关信息。
explain authorization
select a.uid from temp.user_info_all a
left outer join temp.user_act_info b
on a.uid = b.uid and a.ymd = b.ymd
where a.ymd >= '20230501' and a.ymd <= '20230502';
执行结果:
INPUTS:
temp@user_info_all
temp@user_act_info
temp@user_info_all@ymd=20230501
temp@user_info_all@ymd=20230502
temp@user_act_info@ymd=20230501
temp@user_act_info@ymd=20230502
OUTPUTS:
hdfs://nameservice1/tmp/hive/hdfs/a88cc133-c310-4129-bfa0-28011ac23904/hive_2023-06-07_19-42-55_464_2777807904847671424-1/-mr-10000
CURRENT_USER:
hdfs
OPERATION:
QUERY
AUTHORIZATION_FAILURES:
Permission denied: Principal [name=hdfs, type=USER] does not have following privileges for operation QUERY [[SELECT] on Object [type=TABLE_OR_VIEW, name=temp.user_act_info], [SELECT] on Object [type=TABLE_OR_VIEW, name=temp.user_info_all]]
从上面的信息可知:
上面案例的数据来源是temp数据库中的 user_info_all表和user_act_info表;
数据的输出路径是hdfs://nameservice1/tmp/hive/hdfs/a88cc133-c310-4129-bfa0-28011ac23904/hive_2023-06-07_19-42-55_464_2777807904847671424-1/-mr-10000;
当前的操作用户是hdfs,操作是查询(QUERY);
观察上面的信息我们还会看到AUTHORIZATION_FAILURES信息,提示对当前的输入没有查询权限,但如果运行上面的SQL的话也能够正常运行。为什么会出现这种情况?Hive在默认不配置权限管理的情况下不进行权限验证,所有的用户在Hive里面都是超级管理员,即使不对特定的用户进行赋权,也能够正常查询。
通过上面对explain相关参数的介绍,可以发现explain中有很多值得我们去研究的内容,读懂 explain 的执行计划有利于我们优化Hive SQL,同时也能提升我们对SQL的掌控力。
下一期:Hive执行计划之什么是hiveSQL向量化模式及优化详解
按例,欢迎点击此处关注我的个人公众号,交流更多知识。
后台回复关键字 hive,随机赠送一本鲁边备注版珍藏大数据书籍。
Hive执行计划之hive依赖及权限查询和常见使用场景的更多相关文章
- hive执行计划简单分析
原始SQL: select a2.ISSUE_CODE as ISSUE_CODE, a2.FZQDM as FZQDM, a2.FZQLB as FZQLB, a2.FJJDM as FJJDM, ...
- 强制SQL Server执行计划使用并行提升在复杂查询语句下的性能
最近在给一个客户做调优的时候发现一个很有意思的现象,对于一个复杂查询(涉及12个表)建立必要的索引后,语句使用的IO急剧下降,但执行时间不降反升,由原来的8秒升到20秒. 通过观察执行 ...
- 【转】Hive执行计划
执行语句 hive> explain select s.id, s.name from student s left outer join student_tmp st on s.name = ...
- Hive 执行计划
执行语句 hive> explain select s.id, s.name from student s left outer join student_tmp st on s.name = ...
- Execution Plan 执行计划介绍
后面的练习中需要下载 Demo 数据库, 有很多不同的版本, 可以根据个人需要下载. 下载地址 -http://msftdbprodsamples.codeplex.com/ 1. 什么是执行计划 ...
- sparkSQL1.1入门之四:深入了解sparkSQL执行计划
前面两章花了不少篇幅介绍了SparkSQL的执行过程,非常多读者还是认为当中的概念非常抽象.比方Unresolved LogicPlan.LogicPlan.PhysicalPlan是长得什么样子,没 ...
- 谈一谈SQL Server中的执行计划缓存(上)
简介 我们平时所写的SQL语句本质只是获取数据的逻辑,而不是获取数据的物理路径.当我们写的SQL语句传到SQL Server的时候,查询分析器会将语句依次进行解析(Parse).绑定(Bind).查询 ...
- Oracle 执行计划(Explain Plan)
如果要分析某条SQL的性能问题,通常我们要先看SQL的执行计划,看看SQL的每一步执行是否存在问题. 如果一条SQL平时执行的好好的,却有一天突然性能很差,如果排除了系统资源和阻塞的原因,那么基本可以 ...
- SQL Server如何固定执行计划
SQL Server 其实从SQL Server 2005开始,也提供了类似ORACLE中固定执行计划的功能,只是好像很少人使用这个功能.当然在SQL Server中不叫"固定执行计划&qu ...
- MYSQL与TiDB的执行计划
前言 这里采用了tpc-h一个数据库的数据量来进行查询计划的对比.并借助tpc-h中的22条查询语句进行执行计划分析. mysql采用的是标准安装,TiDB采用的是单机测试版,这里的性能结果不能说明其 ...
随机推荐
- ChatGPT 开源了第一款插件,都来学习一下源码吧!
3 月 23 日,OpenAI 又投出了一枚重磅炸弹:为 ChatGPT 推出插件系统! 此举意味着 ChatGPT 将迎来"APP Store"时刻,也就是围绕它的能力,形成一个 ...
- 基于列存储的开源分布式NoSQL数据库Apache Cassandra入门分享
@ 目录 概述 定义 特性 与Hbase对比 Cassandra使用场景 术语 架构 概览 Dynamo 数据集分区使用令牌环的一致性哈希 存储引擎 部署 单实例部署 集群部署 CQL 概述 数据模型 ...
- 非线性规划—R实现
非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法.运筹学八大分支之一,20世纪50年代初,库哈(H.W.Kuhn) 和托克 (A.W.Tucker) 提出了 ...
- kubernetes核心实战(一)--- namespace
kubernetes核心实战 1.资源创建方式 命令行创建 yaml文件创建 2.namespace 命名空间(namespace)是Kubernetes提供的组织机制,用于给集群中的任何对象组进行分 ...
- 2.JWT实现单点登录的概念
1.总结: 昨天主要是了解了JWT的作用.构成以及RSA的作用和构成,再就是分布式认证的流程和集中式的差别 JWT的作用:JWT用于生成和校验token JWT的构成:头部.载荷以及签名 头部:设置规 ...
- 数据泵:impdp导入用户ORA-01653
,问题描述:在导入一个用户数据的时候,大小为14G左右,导进来的时候卡半天,后来发现是表空间满了,已经恢复了大概6G左右,剩下8G左右没有恢复.此时磁盘剩余19G,加了15G的表空间,磁盘就剩下4G左 ...
- 去掉Bom头的方法和为什么要清除Bom头
什么是bom头? 在utf-8编码文件中BOM在文件头部,占用三个字节,用来标示该文件属于utf-8编码,现在已经有很多软件识别bom头,但是还有些不能识别bom头,比如PHP就不能识别bom头,这也 ...
- 如何在 DevOps 中进行 API 全生命周期管理?
随着 DevOps 理念在中国企业当中的普及和发展,中国企业 DevOps 落地成熟度不断提升,根据中国信通院的数据已有近 6 成企业向全生命周期管理迈进. 而在研发全生命周期管理之中,API 管理的 ...
- DolphinDB +Python Airflow 高效实现数据清洗
DolphinDB 作为一款高性能时序数据库,其在实际生产环境中常有数据的清洗.装换以及加载等需求,而对于该如何结构化管理好 ETL 作业,Airflow 提供了一种很好的思路.本篇教程为生产环境中 ...
- Linux普通用户使用docker以及docker-compose
# 添加limstorm普通用户到docker用户组 sudo gpasswd -a limstorm docker # 切换docker用户组,该命令类似login指令,当它是以相同的帐号,另一个群 ...