Spark执行HiveSQL以及Hive自定义函数

Spark执行Hive

提示:Spark执行Hive的表只能是外表或是表不包含ACID事物的表



前言

Hive一般作为大数据的数据仓库,因其语句和SQL大部分通用。所以很多数据为存储在Hive表中。


提示:以下是本篇文章正文内容,下面案例可供参考

一、pom.xml导入依赖执行的包

代码如下

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.13</artifactId>
    <version>3.2.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-hive_2.13</artifactId>
    <version>3.2.0</version>
    <scope>provided</scope>
</dependency>

二、使用步骤

1.编写代码

实现代码如下(示例):

SparkConf sc = new SparkConf();
sc.setAppName("Hive-Text"); //当前执行名称
//Spark执行Parquet格式Hive时候Decimal格式问题
sc.set("spark.sql.parquet.writeLegacyFormat","true");
SparkSession ss = SparkSession
                 .builder()
                .config(sc)
                .enableHiveSupport() //执行Hive操作
                .getOrCreate();
//读取文件格式支持text/csv/json/parquet/orc
//其中text文件读取一行作为一个值,需要对象有多个字段,采用其他格式进行读取
Dataset<Row> frs = ss.read().text("input/data/config/test.sql");
List<String> sqls = new ArrayList<>();
//通过当前循环得到数据,row.length和文件格式有关
frs.toLocalIterator().forEachRemaining(new Consumer<Row>() {
     @Override
     public void accept(Row row) {
         int len = row.length();
         for(int i = 0; i < len; i++){
             //文件内容处理
             sqls.add(row.getString(i));
          }
     }
});
for (String sql : sqls){
     //执行SQL
     Dataset<Row> dataset = ss.sql(sql);
 }

2.Spark执行脚本

脚本编写需要注意格式,最好从Linux下载可执行sh脚本进行修改

脚本实例如下

spark-submit \
  --master yarn \
  --executor-memory 3072m \
  --num-executors 3\
  --class com.study.SparkHiveFile \
  /user/home/test/jar/spark.jar ${hdfs_sql_file} ${data_date}

异常处理

1:当执行Hive查询中包含自定义函数,函数中包含Hive查询,包数据库连接异常

处理方法是将自定义函数在Spark项目上复制一份。因当前项目中的类会覆盖在Hive 中创建自定义函数实现类。且数据库连接正常

2:当执行报异常:Should not be called directly时

需要重写下面方法:因Spark执行的是下面方法,即在Hive自定义函数时实现下面方法。

public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
        throw new IllegalStateException("Should not be called directly");
    }

3:当执行报:This table may be a Hive-managed ACID

需要注意Spark执行Hive的表不能是内表(即表由ACID事物属性的表)。可以采用外表进行替代。

Spark SQL 生成RDD过程(Catalyst)

无论是DataFrame API、Dataset API还是Spark SQL,他们都会被转换为未解析的逻辑执行计划(Unresolved Logical Execution Plan, ULEP)。当ULEP在数据目录(Catalog)中补齐字段类型、列名等时,就会称为解析好的逻辑执行计划(Resolved Logical Execution Plan,RLEP)。RLEP会经过多次转换生成优化的逻辑计划(Optimized Logical Execution, OLP)。 逻辑执行计划(Logical Execution Plan, LEP)不会包含如何计算的描述,而只包含必须被计算的内容。根据一些策略,OLP会转为物理执行计划(Physical Logical Plan,PLP)。最后通过采用成本模型(Cost Model)对要执行查询的Dataset执行统计,优化的PEP会选取最优的执行方案。最后执行操作会作用于RDD上。

从ULEP到RLEP过程

ULEP基本反映了抽象语法树的结构,但是ULEP还不能直接执行,首先需要从数据目录(Catalog)中检查所有引用关系是否都存在,这意味着所有的表名和字段名等都必须合法。如果表名或者关系存在,就会验证列名。其次多次引用的列名会被赋予一个列名,以便只读取一次。这是优化的第一步。最后,字段的数据类型会用来检查哪些关于列的表达式是否合法。因此,针对字符串求和的表达式就不会执行,并会在这一步报错。这个操作完成之后,就会从ULEP到RLEP。

优化RLEP

解析好优化的LEP会用来产生大量的候选PEP。PEP是完全解析的执行计划。这意味着一个PEP包含生成期望结构的详细之类。生成PEP的策略会对连接算法进行优化,此外对那些在一个RDD上执行的多个操作会根据规则简化为一个复杂的操作。在生成很多PEP后,最好的选择是基于启发式算法最小执行时间。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
spark + hive 自定义聚合函数回顾--group_concat实现 group_concat是一种常用的聚合函数,它可以将同一组内的多个值合并成一个字符串。在hive中,group_concat函数已经内置,但是在spark中需要自定义实现。 实现group_concat函数的步骤如下: 1. 继承org.apache.spark.sql.expressions.UserDefinedAggregateFunction类,实现其抽象方法。 2. 定义输入和输出的数据类型。 3. 实现update方法,用于更新聚合结果。 4. 实现merge方法,用于合并不同分区的聚合结果。 5. 实现evaluate方法,用于输出最终的聚合结果。 下面是一个简单的group_concat实现示例: import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction} import org.apache.spark.sql.types.{DataType, StringType, StructType} import org.apache.spark.sql.{Row, SparkSession} class GroupConcat extends UserDefinedAggregateFunction { // 定义输入数据类型 def inputSchema: StructType = new StructType().add("value", StringType) // 定义中间缓存数据类型 def bufferSchema: StructType = new StructType().add("buffer", StringType) // 定义输出数据类型 def dataType: DataType = StringType // 定义是否是确定性的 def deterministic: Boolean = true // 初始化中间缓存数据 def initialize(buffer: MutableAggregationBuffer): Unit = { buffer.update(0, "") } // 更新中间缓存数据 def update(buffer: MutableAggregationBuffer, input: Row): Unit = { val str = input.getString(0) if (!buffer.isNullAt(0)) { buffer.update(0, buffer.getString(0) + "," + str) } else { buffer.update(0, str) } } // 合并不同分区的中间缓存数据 def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = { if (!buffer2.isNullAt(0)) { update(buffer1, buffer2) } } // 输出最终的聚合结果 def evaluate(buffer: Row): Any = { buffer.getString(0) } } // 使用示例 val spark = SparkSession.builder().appName("group_concat").master("local[*]").getOrCreate() spark.udf.register("group_concat", new GroupConcat) val df = spark.sql("select id, group_concat(name) as names from table group by id") df.show() 在使用时,需要先将自定义的聚合函数注册到spark中,然后就可以在sql中使用了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值