SparkSQL 1

SparkSQL

什么是SparkSQL
首先看官网的描述
官网地址:http://spark.apache.org/sql/
在这里插入图片描述
我们先简单了解下官网是怎么描述SparkSQL这个东西的:Spark SQL is Apache Spark’s module for working with structured data. 根据官网的描述就是Spark SQL是Spark的一个模块用来处理结构化数据的。

  1. SparkSQL将SQL和Spark program结合到了一起使用
  2. 统一的数据库访问方式
  3. SparkSQL对HiveQL的支持
  4. 对JDBC和ODBC的支持

如何构建SparkSQL和Hive上元数据的联系
既然Spark只是一个计算数据的组件,那么它也就需要读取存放在其他地方的数据了。可以是HDFS,externa Data Source,Hive table等。如果是Hive上面的数据,那么我们最起码要知道这些数据的元数据吧,而Hive上面的元数据是存储在MySQL上面的,我们就要想办法来连接到这个元数据所在的地方吧。那么我们可以使用hive-site.xml这个hive的配置文件,否则我们的spark-sql就会在当前目录下创建一个metastore_db这样的一个Derby数据库来临时存储我们这次操作所产生的元数据了,同时因为Derby是单会话连接,我们没有办法在同一个目录下创建多一个metastore_db,多不方便呀。

我直接在某个目录下启动了spark-sql --master local[2] 在控制台上的日志中你会看到这么一个warning的:

18/12/27 15:08:20 WARN metastore.ObjectStore: Version information not found in metastore. hive.metastore.schema.verification is not enabled so recording the schema version 1.2.0
18/12/27 15:08:20 WARN metastore.ObjectStore: Failed to get database default, returning NoSuchObjectException

我们同时在这个目录下发现多了两个东西:
在这里插入图片描述
一个是metastore_db的文件夹和一个derby.log的日志文件。
如果我再次在相同目录下使用spark-sql --master local[2],可以看下控制台上会报错,原因是当前文件夹下已经有了一个metastore_db了,也就是说,Spark不是必须要有Hive,只要有metastore就可以了。
所以,我们这个时候需要将$HIVE_HOME/conf/hive-site.xml这个文件放到$SPARH_HOME/conf里面去,这样spark就能读取到metastore了。

接着我们再次使用spark-sql来启动SparkSQL,结果还是报错:
Caused by: org.datanucleus.exceptions.NucleusException: Attempt to invoke the “BONECP” plugin to create a ConnectionPool gave an error : The specified datastore driver (“com.mysql.jdbc.Driver”) was not found in the CLASSPATH. Please check your CLASSPATH specification, and the name of the driver.
这次是没有找到mysql的驱动jar包,把mysql的jar包放到spark上面的jars就好了。不过我在官网上:http://spark.apache.org/docs/latest/sql-data-sources-troubleshooting.html
又看到了这么一段话:
The JDBC driver class must be visible to the primordial class loader on the client session and on all executors. This is because Java’s DriverManager class does a security check that results in it ignoring all drivers not visible to the primordial class loader when one goes to open a connection. One convenient way to do this is to modify compute_classpath.sh on all worker nodes to include your driver JARs.
这里描述了JDBC的驱动jar包要对client session和所有executor可读,所以应该是可以通过一些配置属性来让driver和executor来都能读取这个驱动jar包的。
我们在$SPARK_HOME/conf/spark-default.xml里面配置两个参数:
在这里插入图片描述
spark.driver.extraClassPath和spark.executor.extraClassPath,这两个同时指向我们存放JDBC驱动jar包的路径。这样我们再次启动spark-sql的时候就不会报错了,我们还可以读取到Hive下的tables了。
在这里插入图片描述
在这个界面里面我们输入的就是SQL了,和操作Hive是很类似的。当然,在开发的过程中我们不是在这里执行的,我们是会在idea中把代码开发好了再到spark-submit上提交的。

idea上使用SparkSQL

配置pom.xml文件
我们需要在我们的项目工程下面的pom.xml里面添加对SparkSQL的依赖
groupId:org.apache.spark
artifactId:spark-sql_2.11
version:${spark.version} //指定你自己的spark版本
下载好我们需要的所有包就可以开始使用了。

SparkSQL的入口
和我们使用spark-core进行编程一样,我们都需要一个程序入口点,但是在SparkSQL里面的入口点是SparkSession这个对象作为入口点(这个对象内部已经实现了一个SparkContext,所以说只要是spark程序都是以SparkContext作为入口点的)

package sparkSQL

import org.apache.spark.sql.SparkSession

object SparkSQLApp {
  def main(args: Array[String]): Unit = {
    val ss = SparkSession.builder().master("local[2]").appName("the test of SparkSession").getOrCreate()
    // TODO...
    ss.stop()
  }
}
<think>好的,我现在需要解决用户的问题,他们希望用SparkSQL和ANSJ分词器处理岗位要求,统计技能出现次数并存入MySQL。用户已经提供了代码框架,我需要补充完整。首先,我需要理解用户的需求和提供的背景信息。首先,用户的任务是读取Parquet文件中的岗位要求(responsibility字段),使用ANSJ分词器提取出技能词(词性为en的英文词),统计这些技能的出现次数,然后将结果存入MySQL的eduskill表。用户提供的代码框架中,需要完成读取Parquet、分词、统计、转换Dataset和存储到MySQL这几个步骤。我需要确保每一步都正确实现。首先,读取Parquet文件,使用SparkSQL的read方法。然后,处理responsibility字段,这里可能需要使用JavaRDD来操作,因为涉及到分词处理。ANSJ分词器的使用部分,用户已经给出了示例代码,需要应用到每个responsibility文本上,提取出词性为en的词汇。这里要注意,可能需要将词汇转为小写,避免大小写不同导致的统计错误。接下来是统计词频,使用mapToPair将每个词映射为键值对,然后用reduceByKey进行累加。之后,将统计结果转换为Dataset,需要定义schema,包括skill和count两个字段。最后,将Dataset写入MySQL,需要配置JDBC连接参数,如URL、表名、用户名和密码等。 用户提供的预计输出是一个技能列表及其出现次数,如hadoop30,spark28等。这说明最终的统计结果需要按照技能名称排序,可能需要按出现次数降序排列,但用户代码中没有提到排序,可能不需要,但需要确认。不过任务描述中没有明确要求排序,所以可能只需统计后直接存储。 接下来,我需要检查用户提供的代码框架中的空缺部分,并补充完整。例如,读取Parquet文件需要正确的路径,用户代码中的路径是"/root/job.parquet",可能需要确认是否正确。另外,存储到MySQL时,连接参数需要根据实际环境配置,用户给出的示例中可能使用本地MySQL实例,用户名为root,密码为123123,这些可能需要用户根据实际情况修改。在分词过程中,需要注意ANSJ分词器的准确性,确保能正确识别出英文技能词汇。例如,示例中的代码使用ToAnalysis进行分词,然后过滤出词性为en的词汇。这里需要确保分词后的词性标注正确,比如Hadoop、Spark等会被正确识别为en词性。另外,需要考虑Spark的分布式处理是否会影响ANSJ分词器的性能。由于ANSJ是Java库,应该可以在每个executor上正常运行,但需要确保依赖正确打包。用户提供的pom.xml中需要包含ansj_seg的依赖,否则运行时会出现类找不到的错误。最后,存储到MySQL时,需要使用Spark的JDBC写入功能,并指定正确的表结构。eduskill表应该包含skill和count两个字段,类型为VARCHAR和INT。写入模式为overwrite,确保每次运行覆盖旧数据,或者根据需要选择append模式。可能遇到的问题包括:Parquet文件路径不正确导致读取失败,MySQL连接配置错误导致写入失败,ANSJ分词器未正确初始化导致分词失败,或者词性过滤条件错误导致未能正确提取技能词。需要逐一检查这些部分,确保代码的正确性。总结步骤:读取数据→提取字段→分词→过滤→统计→转换→存储。每一步都需要正确实现,并处理可能的异常情况。现在,根据这些思考,可以编写完整的Java代码,并添加必要的注释,确保用户能够理解每个部分的作用。</think>以下是完整的解决方案代码和解释: ```java package com; import org.ansj.domain.Result; import org.ansj.domain.Term; import org.ansj.splitWord.analysis.ToAnalysis; import org.apache.spark.api.java.JavaPairRDD; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.RowFactory; import org.apache.spark.sql.SparkSession; import org.apache.spark.sql.types.DataTypes; import org.apache.spark.sql.types.StructField; import org.apache.spark.sql.types.StructType; import scala.Tuple2; import java.util.ArrayList; import java.util.List; public class EduSkill { public static void main(String[] args) { SparkSession spark = SparkSession.builder() .appName("SkillAnalysis") .master("local") .getOrCreate(); spark.sparkContext().setLogLevel("ERROR"); /********** Begin **********/ // 1. 读取Parquet文件 Dataset<Row> df = spark.read().parquet("/root/job.parquet"); // 2. 提取responsibility字段并分词 JavaRDD<String> responsibilities = df.select("responsibility") .toJavaRDD() .map(row -> row.getString(0)); // 3. 分词处理并过滤en词性 JavaRDD<String> skills = responsibilities.flatMap(text -> { Result result = ToAnalysis.parse(text); List<Term> terms = result.getTerms(); List<String> enWords = new ArrayList<>(); for (Term term : terms) { if ("en".equals(term.getNatureStr())) { enWords.add(term.getName().toLowerCase()); } } return enWords.iterator(); }); // 4. 统计词频 JavaPairRDD<String, Integer> counts = skills .mapToPair(word -> new Tuple2<>(word, 1)) .reduceByKey(Integer::sum); // 5. 转换为Dataset List<StructField> fields = new ArrayList<>(); fields.add(DataTypes.createStructField("skill", DataTypes.StringType, false)); fields.add(DataTypes.createStructField("count", DataTypes.IntegerType, false)); StructType schema = DataTypes.createStructType(fields); JavaRDD<Row> rowRDD = counts.map(tuple -> RowFactory.create(tuple._1(), tuple._2()) ); Dataset<Row> resultDF = spark.createDataFrame(rowRDD, schema); // 6. 写入MySQL resultDF.write() .format("jdbc") .option("url", "jdbc:mysql://localhost:3306/yourdb") .option("dbtable", "eduskill") .option("user", "root") .option("password", "yourpassword") .mode("overwrite") .save(); /********** End **********/ spark.stop(); } } ``` 代码解释: 1. **数据读取**:使用`spark.read().parquet()`读取指定路径的Parquet文件 2. **字段提取**:通过select操作获取responsibility字段的JavaRDD 3. **分词处理**: - 使用`ToAnalysis.parse()`进行精准分词 - 过滤保留词性为"en"的英文词汇 - 统一转换为小写保证统计准确性 4. **词频统计**: - 将每个词转换为`(word, 1)`的键值对 - 使用`reduceByKey`进行累加统计 5. **数据转换**: - 定义包含skill和count字段的Schema - 将统计结果转换为DataFrame 6. **数据存储**: - 配置MySQL连接参数(需替换实际数据库信息) - 使用overwrite模式覆盖写入目标表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值