Spark 使用JDBC进行select查询

本文介绍如何使用Spark通过JDBC接口优化从PostgreSQL数据库读取特定行数据的方法,避免全表扫描导致的性能问题和资源浪费,通过指定where条件减少数据加载量。

spark 可以 通过jdbc直接将数据库中的一整张表直接读取出来作为一个DataFram,这样会出现几个问题:
- 1. 表格太大,导致出现OOM;
- 2. 读出来很多不需要的行,在join的时候,造成性能的浪费

这里记录通过JDBC通过select where语句只查询表格中的指定行

java 代码如下:

package utils;

import Main.NewWordsEx;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.sql.*;

import java.io.Serializable;
import java.util.*;
import java.util.regex.Pattern;

public class IOUtils implements Serializable {
     /**
     * 通过jdbc 读取postgresql
     * @param sqlContext
     * @param tokens where 条件中需要筛选的字段
     * @return
     */
    public static DataFrame readDataFromPSQL(SQLContext sqlContext, List<String> tokens) {
        if (tokens == null || tokens.size() == 0){
            return null;
        }
        StringBuilder builder = new StringBuilder();
        for (String s : tokens) {
            builder.append("\'"+s+"\'");
            builder.append(",");
        }
        return readDataFromPSQL(sqlContext, builder.deleteCharAt(builder.length() - 1).toString());
    }

    public static DataFrame readDataFromPSQL(SQLContext sqlContext, String token) {
        if (token == null || token.equals("") || token.length() == 0){
            return null;
        }
        DataFrame df = sqlContext.read().format("jdbc")
                .option("url", "jdbc:postgresql://127.0.0.1:5432/test_macan")
                .option("user", "postgres")
                .option("password", "postgres")
                .option("driver", "org.postgresql.Driver")
                .option("dbtable", "(select token, fequence from idf_feq where token in (" + token + ")) as tmp")
                .load();

        return df;
    }

    public static void main(String[] args) {
        SparkConf conf = new SparkConf().setAppName("LoadData")//
                .setMaster("local[*]");
        Logger logger = Logger.getLogger("org.apache.spark");
        logger.setLevel(Level.WARN);
        JavaSparkContext jsc = new JavaSparkContext(conf);
        final SQLContext sqlContext = new SQLContext(jsc);

        List<String> tokenList = sqlContext.read().parquet(IOUtils.hdfsData2016)
                .select("text")
                .toJavaRDD()
                .flatMap(new FlatMapFunction<Row, String>() {
                    @Override
                    public Iterable<String> call(Row row) throws Exception {
                        List<String> rst = new ArrayList<>(tokenizer(row.getString(0), 2).keySet());
                        return rst;
                    }
                }).collect();
        DataFrame idfDF = IOUtils.readDataFromPSQL(sqlContext, tokenList/*Arrays.asList("百零", "驻紫", "了相关业")*/);
        idfDF.show();

        sqlContext.clearCache();
        jsc.close();
    }

方法就是使用option参数,在dbtable的value哪里写查询选项。
.option("dbtable", "(select token, fequence from idf_feq where token in (" + token + ")) as tmp")

测试的数据库很简单,就两列,在实际中我存储的是IDF,例如:

tokenidf_frequence
北京10
张三20
附带一下spark 通过jdbc 写数据的方法:
    /**
     * 写数据到SQL
     * @param dataFrame
     */
    public static void saveDataToPSQL(DataFrame dataFrame, String table) {
        if (dataFrame == null) {
            return;
        }
        Properties properties = new Properties();
        properties.put("user", "postgres");
        properties.put("password", "postgres");
        properties.put("driver", "org.postgresql.Driver");
        properties.put("fetchSize", "5000"); dataFrame.write().mode(SaveMode.Append).jdbc("jdbc:postgresql://127.0.0.1:5432/test_macan",
                table,
                properties);
    }

总结

如果问题,请指教。

<think>嗯,用户的问题是关于在使用Spark通过JDBC查询时,具体使用的是Spark引擎还是Hive引擎。我需要结合他们提供的引用内容来解答这个问题。 首先,用户引用了几个例子,包括使用SparkJDBC方式连接Hive,以及通过不同的代码示例展示如何连接和操作。这些例子中,有的使用SparkSession读取数据,有的直接使用JDBC连接Hive。我的任务是分析在这些情况下,执行查询的引擎到底是Spark还是Hive。 根据我的知识,Spark JDBC连接Hive时,Spark会通过JDBC驱动将查询发送到Hive的HiveServer2服务。HiveServer2负责解析和执行查询,因此实际执行的是Hive引擎。不过,当Spark读取数据后,如果进行后续处理,比如转换或动作,这部分可能会由Spark引擎处理。需要区分查询执行和数据处理的不同阶段。 另外,引用中提到Spark本身不支持通过JDBC写入Hive,但通过第三方工具如Byzer实现,这里可能涉及到不同的处理流程。但用户的问题主要集中在查询阶段,所以需要明确查询的解析和执行由哪个引擎负责。 还要注意用户之前的问题是关于JDBC连接指定数据库的,这次的问题可能是在同一上下文中,即在使用JDBC连接Hive时的引擎使用情况。需要确保回答准确,并结合提供的引用内容,比如引用1和引用2中的代码示例,说明查询是如何被处理的。 总结来说,当Spark通过JDBC连接Hive并执行查询时,查询语句会被发送到HiveServer2,由Hive引擎执行。而后续的数据处理如果使用Spark的API,则会由Spark引擎处理。需要明确区分这两个阶段,并引用提供的示例代码来支持结论。</think>#### 一、核心结论 **Spark通过JDBC查询Hive时,实际执行引擎取决于查询触发端:** - **JDBC查询本身由Hive引擎执行**(通过HiveServer2接收并解析SQL) - **后续数据处理由Spark引擎执行**(当数据加载到Spark DataFrame/Dataset后) --- #### 二、执行过程详解 ##### 1. **查询触发阶段** 通过JDBC连接时,Spark会通过Hive JDBC驱动(`org.apache.hive.jdbc.HiveDriver`)将SQL语句传递给HiveServer2服务[^1][^2]。此时: - **SQL解析与执行完全依赖Hive引擎** - 涉及Hive元数据、HQL语法、UDF等特性 - 示例代码中的`jdbcDF.show()`会触发HiveServer2执行查询[^1] ##### 2. **数据加载阶段** 当查询结果返回后: - Spark将结果数据封装为**DataFrame**或**Dataset** - **后续转换操作(如filter/map/join)由Spark引擎执行** - 示例代码中的`.load()`仅定义数据来源,不触发计算[^2] --- #### 三、关键验证点 | 特征 | Hive引擎执行 | Spark引擎执行 | |---------------------|----------------------------|----------------------------| | **日志来源** | HiveServer2日志 | Spark作业日志 | | **语法支持** | 仅支持HQL | 支持Spark SQL/DataFrame API | | **执行计划** | 通过`EXPLAIN`查看Hive执行计划 | 通过`.explain()`查看Spark计划 | | **UDF调用** | 依赖Hive注册的UDF | 需在Spark中重新注册 | --- #### 四、典型场景分析 ##### 场景1:纯JDBC查询(无Spark计算) ```java // 引用[4]中的代码示例 ResultSet res = stmt.executeQuery("SELECT * FROM table"); // 完全由Hive引擎执行[^4] ``` ##### 场景2:Spark加载后计算 ```scala // 引用[2]中的代码扩展 val filteredDF = rdd.filter($"age" > 18) // 由Spark引擎执行[^2] ``` --- #### 五、性能影响对比 | 操作类型 | 引擎 | 延迟 | 适用场景 | |--------------------|------------|-----------|------------------------| | **复杂聚合查询** | Hive引擎 | 较高 | 需要利用Hive优化器的场景 | | **大规模数据转换** | Spark引擎 | 较低 | 迭代计算、机器学习流水线 | | **即时查询** | Hive引擎 | 中等 | BI工具直接对接Hive | --- #### 六、配置优化建议 1. **Hive引擎侧优化** ```properties # 增加HiveServer2并行度 hive.server2.parallel.ops.num=8 ``` 2. **Spark侧优化** ```scala spark.read.jdbc() .option("partitionColumn", "id") // 启用并行加载 .option("numPartitions", "4") ``` --- #### 七、特殊场景说明 **混合引擎执行**(如通过Spark执行`CREATE TABLE`语句): ```scala spark.sql("CREATE TABLE hive_table USING hive ...") // 元数据操作仍由Hive Metastore处理[^3] ``` --- #### 八、引擎选择建议 1. **优先使用Hive引擎** - 需要兼容现有Hive工作流 - 使用Hive特定功能(如动态分区、Bucketing) 2. **优先使用Spark引擎** - 需要迭代计算或复杂DAG - 需与Spark MLlib等库集成 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值