SparkSql 以jdbc为数据源

https://blog.csdn.net/someby/article/details/83892120

    1.SparkSQL操作关系数据库意义
    2.SparkSQL操作关系数据库

一、通过SparkSQL操作关系数据库意义

    1.SparkSQL可以通过jdbc从传统关系型数据库中读写数据,读取数据后直接生成DataFrame,然后在加上借助于Spark内核的丰富的API来进行各种操作;
    2.关于JDBC的驱动jar可以使用在Spark的jars目录中,也可以在使用spark-submit提交的时候引入,编码和打包的时候不需要这个JDBC的jar
    3.在实际的企业级开发环境中,如果数据库中数据规模特别大,例如10亿条数据此时如果用DB去处理的话,一般需要对数据进行多批次处理,例如分成100批(受限于单台Server的处理能力,)且实际的处理可能会非常复杂,通过传统的Java EE等技术很难或者不方便实现处理算法,此时采用sparkSQL 获得数据库中的数据并进行分布式处理就可以非常好解决该问题,但是由于SparkSQL加载DB的数据需要时间,所以一般会SparkSQL和具体操作的DB之间加上一个缓冲层,例如中间使用redis,可以把SparkSQL处理速度提高到原来的45倍;
    4.MYSQL 是单机版本的,SparkSQL访问MYSQL就可以并行计算,SparkSQL中的DataFrame的计算能力比关系数据库快很多,可以尽可能的满足应用

二、SparkSQL操作关系数据库

    //创建spark数据库
    create database spark;

    
    //创建userinfor表
    create table userinfor(
       id INT NOT NULL AUTO_INCREMENT,
       name VARCHAR(100) NOT NULL,
       age INT not null,
       PRIMARY KEY (id)
    );

   
    //向userinfor表中插入三条数据
    insert into userinfor (name,age) values("Michael",20);
    insert into userinfor (name,age) values("Andy",30);
    insert into userinfor (name,age) values("Justin",19);

         

    //创建scoreinfor表
    create table scoreinfor(
       id INT NOT NULL AUTO_INCREMENT,
       name VARCHAR(100) NOT NULL,
       score INT not null,
       PRIMARY KEY (id)
    );

    
    //向scoreinfo表中插入三条数据
    insert into scoreinfor (name,score) values("Michael",98);
    insert into scoreinfor (name,score) values("Andy",95);
    insert into scoreinfor (name,score) values("Justin",91);  

   

    //创建jion后的存储表userscoreinfor表
    create table userscoreinfor(
       id INT NOT NULL AUTO_INCREMENT,
       name VARCHAR(100) NOT NULL,
       age INT not null,
       score INT not null,
       PRIMARY KEY (id)
    );

  Java代码示例:


 
 
  1. package SparkSQL;
  2. import org.apache.spark.SparkConf;
  3. import org.apache.spark.api.java.JavaPairRDD;
  4. import org.apache.spark.api.java.JavaRDD;
  5. import org.apache.spark.api.java.JavaSparkContext;
  6. import org.apache.spark.api.java.function.Function;
  7. import org.apache.spark.api.java.function.PairFunction;
  8. import org.apache.spark.api.java.function.VoidFunction;
  9. import org.apache.spark.sql.*;
  10. import org.apache.spark.sql.types.DataTypes;
  11. import org.apache.spark.sql.types.StructField;
  12. import org.apache.spark.sql.types.StructType;
  13. import scala.Tuple2;
  14. import java.sql.Connection;
  15. import java.sql.DriverManager;
  16. import java.sql.SQLException;
  17. import java.sql.Statement;
  18. import java.util.ArrayList;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. /**
  22. * FileName: SparkSQLJDBCToMySQL
  23. * Author: hadoop
  24. * Email: 3165845957@qq.com
  25. * Date: 18-11-8 下午11:37
  26. * Description:
  27. */
  28. public class SparkSQLJDBCToMySQL {
  29. public static void main(String[] args) {
  30. //创建SparkConf用于读取系统信息并设置运用程序的名称
  31. SparkConf conf = new SparkConf().setAppName( "SparkSQLJDBCToMySQL").setMaster( "local");
  32. //创建JavaSparkContext对象实例作为整个Driver的核心基石
  33. JavaSparkContext sc = new JavaSparkContext(conf);
  34. //设置输出log的等级,可以设置INFO,WARN,ERROR
  35. sc.setLogLevel( "ERROR");
  36. //创建SQLContext上下文对象,用于SqL的分析
  37. SQLContext sqlContext = new SQLContext(sc);
  38. /**
  39. * 1.通过format("jdbc")的方式来说明SparkSQL操作的数据来源是JDBC,
  40. * JDBC后端一般都是数据库,例如去操作MYSQL.Oracle数据库
  41. * 2.通过DataframeReader的option方法把要访问的数据库信息传递进去,
  42. * url:代表数据库的jdbc链接的地址和具体要连接的数据库
  43. * datable:具体要连接使用的数据库
  44. * 3.Driver部分是SparkSQL访问数据库的具体驱动的完整包名和类名
  45. * 4.关于JDBC的驱动jar可以使用在Spark的lib目录中,也可以在使用
  46. * spark-submit提交的时候引入,编码和打包的时候不需要这个JDBC的jar
  47. */
  48. DataFrameReader reader = sqlContext.read().format( "jdbc"); //指定数据来源
  49. reader.option( "url", "jdbc:mysql://localhost:3306/spark"); //指定连接的数据库
  50. reader.option( "dbtable", "userinfor"); //操作的表
  51. reader.option( "driver", "com.mysql.jdbc.Driver"); //JDBC的驱动
  52. reader.option( "user", "root"); //用户名
  53. reader.option( "password", "123456"); //用户密码
  54. /**
  55. * 在实际的企业级开发环境中,如果数据库中数据规模特别大,例如10亿条数据
  56. * 此时如果用DB去处理的话,一般需要对数据进行多批次处理,例如分成100批
  57. * (受限于单台Server的处理能力,)且实际的处理可能会非常复杂,
  58. * 通过传统的Java EE等技术很难或者不方便实现处理算法,此时采用sparkSQL
  59. * 获得数据库中的数据并进行分布式处理就可以非常好解决该问题,
  60. * 但是由于SparkSQL加载DB的数据需要时间,所以一般会SparkSQL和具体操作的DB之
  61. * 间加上一个缓冲层,例如中间使用redis,可以把SparkSQL处理速度提高到原来的45倍;
  62. */
  63. Dataset userinforDataSourceDS = reader.load(); //基于userinfor表创建Dataframe
  64. userinforDataSourceDS.show();
  65. reader.option( "dbtable", "scoreinfor");
  66. Dataset scoreinforDataSourceDs = reader.load(); //基于scoreinfor表创建Dataframe
  67. //将两个表进行jion操作
  68. JavaPairRDD<String,Tuple2<Integer,Integer>> resultRDD = userinforDataSourceDS.javaRDD().mapToPair( new PairFunction<Row,String,Integer>() {
  69. private static final long serialVersionUID = 1L;
  70. @Override
  71. public Tuple2<String, Integer> call(Row row) throws Exception {
  72. return new Tuple2<String,Integer>(row.getAs( "name"),row.getAs( "age"));
  73. }
  74. }).join(scoreinforDataSourceDs.javaRDD().mapToPair( new PairFunction<Row,String,Integer>() {
  75. private static final long serialVersionUID = 1L;
  76. @Override
  77. public Tuple2<String, Integer> call(Row row) throws Exception {
  78. return new Tuple2<String,Integer>(row.getAs( "name"),row.getAs( "score"));
  79. }
  80. }));
  81. //调用RowFactory工厂方法生成记录
  82. JavaRDD<Row> reusltRowRDD = resultRDD.map( new Function<Tuple2<String, Tuple2<Integer, Integer>>, Row>() {
  83. @Override
  84. public Row call(Tuple2<String, Tuple2<Integer, Integer>> tuple) throws Exception {
  85. return RowFactory.create(tuple._1,tuple._2._1,tuple._2._2);
  86. }
  87. });
  88. /**
  89. * 动态构造DataFrame的元数据,一般而言,有多少列以及每列的具体类型可能来自于json文件,也可能来自于数据库
  90. */
  91. List<StructField> structFields = new ArrayList<StructField>();
  92. structFields.add(DataTypes.createStructField( "name", DataTypes.StringType, true));
  93. structFields.add(DataTypes.createStructField( "age", DataTypes.IntegerType, true));
  94. structFields.add(DataTypes.createStructField( "score", DataTypes.IntegerType, true));
  95. //构建StructType,用于最后DataFrame元数据的描述
  96. StructType structType = DataTypes.createStructType(structFields);
  97. //生成Dataset
  98. Dataset personDS = sqlContext.createDataFrame(reusltRowRDD,structType);
  99. personDS.show();
  100. /**
  101. * 1.当Dataframe要把通过SparkSQL,core、ml等复杂操作的数据写入数据库的时候首先是权限的问题,必须确保数据库授权了当前操作SparkSQL的用户;
  102. * 2.Dataframe要写数据到DB的时候,一般都不可以直接写进去,而是要转成RDD,通过RDD写数据到DB中,
  103. */
  104. personDS.javaRDD().foreachPartition( new VoidFunction<Iterator<Row>>(){
  105. @Override
  106. public void call(Iterator<Row> rowIterator) throws Exception {
  107. Connection connection = null; //数据库连接
  108. Statement statement = null; //
  109. try{
  110. connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/spark", "root", "123456");
  111. statement = connection.createStatement();
  112. while (rowIterator.hasNext()){
  113. String sqlText = "insert into userscoreinfor (name,age,score) values (";
  114. Row row = rowIterator.next();
  115. String name = row.getAs( "name");
  116. int age = row.getAs( "age");
  117. int score = row.getAs( "score");
  118. sqlText+= "'"+name+ "',"+ "'"+age+ "',"+ "'"+score+ "')";
  119. statement.execute(sqlText);
  120. }
  121. } catch (SQLException e){
  122. e.printStackTrace();
  123. } finally {
  124. if (connection != null){
  125. connection.close();
  126. }
  127. }
  128. }
  129. });
  130. }
  131. }

Scala代码示例:


 
 
  1. package SparkSQL
  2. import java.sql.{Connection, Driver, DriverManager, SQLException, Statement}
  3. import org.apache.spark.sql.{Row, RowFactory, SQLContext}
  4. import org.apache.spark.{SparkConf, SparkContext}
  5. /**
  6. * FileName: SparkSQLJDBCMySQLScala
  7. * Author: hadoop
  8. * Email: 3165845957@qq.com
  9. * Date: 18-11-9 上午9:27
  10. * Description:
  11. *
  12. */
  13. object SparkSQLJDBCMySQLScala {
  14. def main(args: Array[String]): Unit = {
  15. //创建SparkConf用于读取系统信息并设置运用程序的名称
  16. val conf = new SparkConf().setMaster( "local").setAppName( "SparkSQLJDBCMySQLScala")
  17. //创建JavaSparkContext对象实例作为整个Driver的核心基石
  18. val sc = new SparkContext(conf)
  19. //设置输出log的等级,可以设置INFO,WARN,ERROR
  20. sc.setLogLevel( "INFO")
  21. //创建SQLContext上下文对象,用于SqL的分析
  22. val sqlContext = new SQLContext(sc)
  23. /**
  24. * 1.通过format("jdbc")的方式来说明SparkSQL操作的数据来源是JDBC,
  25. * JDBC后端一般都是数据库,例如去操作MYSQL.Oracle数据库
  26. * 2.通过DataframeReader的option方法把要访问的数据库信息传递进去,
  27. * url:代表数据库的jdbc链接的地址和具体要连接的数据库
  28. * datable:具体要连接使用的数据库
  29. * 3.Driver部分是SparkSQL访问数据库的具体驱动的完整包名和类名
  30. * 4.关于JDBC的驱动jar可以使用在Spark的lib目录中,也可以在使用
  31. * spark-submit提交的时候引入,编码和打包的时候不需要这个JDBC的jar
  32. */
  33. val reader = sqlContext.read.format( "jdbc")
  34. reader.option( "url", "jdbc:mysql://localhost:3306/spark") //指定连接的数据库
  35. reader.option( "dbtable", "userinfor") //操作的表
  36. reader.option( "driver", "com.mysql.jdbc.Driver") //JDBC的驱动
  37. reader.option( "user", "root") //用户名
  38. reader.option( "password", "123456") //用户密码
  39. /**
  40. * 在实际的企业级开发环境中,如果数据库中数据规模特别大,例如10亿条数据
  41. * 此时如果用DB去处理的话,一般需要对数据进行多批次处理,例如分成100批
  42. * (受限于单台Server的处理能力,)且实际的处理可能会非常复杂,
  43. * 通过传统的Java EE等技术很难或者不方便实现处理算法,此时采用sparkSQL
  44. * 获得数据库中的数据并进行分布式处理就可以非常好解决该问题,
  45. * 但是由于SparkSQL加载DB的数据需要时间,所以一般会SparkSQL和具体操作的DB之
  46. * 间加上一个缓冲层,例如中间使用redis,可以把SparkSQL处理速度提高到原来的45倍;
  47. */
  48. val userinforDataSourceDS = reader.load() //基于userinfor表创建Dataframe
  49. userinforDataSourceDS.show()
  50. reader.option( "dbtable", "scoreinfor")
  51. val scoreinforDataSourceDS = reader.load() //基于scoreinfor表创建Dataframe
  52. scoreinforDataSourceDS.show()
  53. //将两个表进行jion操作
  54. val result = userinforDataSourceDS.rdd.map(row=>(row.getAs( "name").toString,row.getInt( 2))).join(scoreinforDataSourceDS.rdd.map(row=>(row.getAs( "name").toString,row.getInt( 2))))
  55. //将两个表进行jion操作
  56. val resultRDD = result.map(row=>{
  57. val name = row._1.toString
  58. val age:java.lang.Integer = row._2._1
  59. val score:java.lang.Integer = row._2._2
  60. RowFactory.create(name,age,score)
  61. })
  62. /**
  63. * 1.当Dataframe要把通过SparkSQL,core、ml等复杂操作的数据写入数据库的时候首先是权限的问题,必须确保数据库授权了当前操作SparkSQL的用户;
  64. * 2.Dataframe要写数据到DB的时候,一般都不可以直接写进去,而是要转成RDD,通过RDD写数据到DB中,
  65. */
  66. val userscoreinforDS = sqlContext.createDataFrame(resultRDD.map(row => PersonAgeScore(row.getString( 0),row.getInt( 1),row.getInt( 2))))
  67. userscoreinforDS.show()
  68. userscoreinforDS.foreachPartition(row=>{
  69. var connection:Connection = null
  70. var states:Statement = null;
  71. try {
  72. connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/spark", "root", "123456")
  73. states = connection.createStatement()
  74. while (row.hasNext){
  75. var sqlText = "insert into userscoreinfor (name,age,score) values ("
  76. val line = row.next
  77. val name = line.getAs( "name").toString
  78. val age:java.lang.Integer = line.getAs( "age")
  79. val score:java.lang.Integer = line.getAs( "score")
  80. sqlText += "'" + name + "'," + age + "," + score + ")"
  81. println(sqlText)
  82. states.execute(sqlText)
  83. }
  84. } catch {
  85. case e: SQLException=>{
  86. e.printStackTrace()
  87. }
  88. } finally {
  89. if (connection != null)
  90. connection.close()
  91. }
  92. })
  93. }
  94. }

 

运行结果:

 

注意:在在idea上运行代码时候,遇到问题

 1.Exception in thread "main" java.lang.ClassNotFoundException: com.mysql.jdbc

  解决:将mysql-connector-java-5.1.40-bin.jar引入工程中

 2.msyql Caused by: java.net.ConnectException: 拒绝连接 (Connection refused)

解决:数据库配置是localhost,连接应该为jdbc:mysql://localhost:3306/spark

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值