sparkSQL入数据库源码解析

1.用sparkSQL入mysql和sparkcore入mysql有啥区别呢?我们看看sparksql源码:

写入:

df.write.jdbc(url1, "TEST.TRUNCATETEST", properties)

转化一下配置信息:

def jdbc(url: String, table: String, connectionProperties: Properties): Unit = {
  assertNotPartitioned("jdbc")
  assertNotBucketed("jdbc")
  // connectionProperties should override settings in extraOptions.
  this.extraOptions = this.extraOptions ++ connectionProperties.asScala
  // explicit url and dbtable should override all
  this.extraOptions += ("url" -> url, "dbtable" -> table)
  format("jdbc").save()
}

获取datasource

def save(): Unit = {
  assertNotBucketed("save")
  val dataSource = DataSource(
    df.sparkSession,
    className = source,
    partitionColumns = partitioningColumns.getOrElse(Nil),
    bucketSpec = getBucketSpec,
    options = extraOptions.toMap)

  dataSource.write(mode, df)
}

选择写出的方式

def write(mode: SaveMode, data: DataFrame): Unit = {
    if (data.schema.map(_.dataType).exists(_.isInstanceOf[CalendarIntervalType])) {
      throw new AnalysisException("Cannot save interval data type into external storage.")
    }

    providingClass.newInstance() match {
      case dataSource: CreatableRelationProvider =>
        dataSource.createRelation(sparkSession.sqlContext, mode, caseInsensitiveOptions, data)
      case format: FileFormat =>
        writeInFileFormat(format, mode, data)
      case _ =>
        sys.error(s"${providingClass.getCanonicalName} does not allow create table as select.")
    }
  }
}

//判断一下模式是什么,插入前:采用 创建表 还是 追加 还是 删除数据 等方式

override def createRelation(
    sqlContext: SQLContext,
    mode: SaveMode,
    parameters: Map[String, String],
    df: DataFrame): BaseRelation = {
  val jdbcOptions = new JDBCOptions(parameters)
  val url = jdbcOptions.url
  val table = jdbcOptions.table
  val createTableOptions = jdbcOptions.createTableOptions
  val isTruncate = jdbcOptions.isTruncate

  val conn = JdbcUtils.createConnectionFactory(jdbcOptions)()
  try {
    val tableExists = JdbcUtils.tableExists(conn, url, table)
    if (tableExists) {
      mode match {
        case SaveMode.Overwrite =>
          if (isTruncate && isCascadingTruncateTable(url) == Some(false)) {
            // In this case, we should truncate table and then load.
            truncateTable(conn, table)
            saveTable(df, url, table, jdbcOptions)
          } else {
            // Otherwise, do not truncate the table, instead drop and recreate it
            dropTable(conn, table)
            createTable(df.schema, url, table, createTableOptions, conn)
            saveTable(df, url, table, jdbcOptions)
          }

        case SaveMode.Append =>
          saveTable(df, url, table, jdbcOptions)

        case SaveMode.ErrorIfExists =>
          throw new AnalysisException(
            s"Table or view '$table' already exists. SaveMode: ErrorIfExists.")

        case SaveMode.Ignore =>
          // With `SaveMode.Ignore` mode, if table already exists, the save operation is expected
          // to not save the contents of the DataFrame and to not change the existing data.
          // Therefore, it is okay to do nothing here and then just return the relation below.
      }
    } else {
      createTable(df.schema, url, table, createTableOptions, conn)
      saveTable(df, url, table, jdbcOptions)
    }
  } finally {
    conn.close()
  }
  createRelation(sqlContext, parameters)
}

//核心就是这个saveTable方法:

  

def saveTable(
    df: DataFrame,
    url: String,
    table: String,
    options: JDBCOptions) {
  val dialect = JdbcDialects.get(url)
  val nullTypes: Array[Int] = df.schema.fields.map { field =>
    getJdbcType(field.dataType, dialect).jdbcNullType
  }

  val rddSchema = df.schema
  val getConnection: () => Connection = createConnectionFactory(options)
  val batchSize = options.batchSize
  val isolationLevel = options.isolationLevel
//主要是这句话,我去,还不是对每个分区进行操作么?呵呵
  df.foreachPartition(iterator => savePartition(
    getConnection, table, iterator, rddSchema, nullTypes, batchSize, dialect, isolationLevel)
  )
}

savePartition方法:

/**
 * Returns a PreparedStatement that inserts a row into table via conn.
 */
def insertStatement(conn: Connection, table: String, rddSchema: StructType, dialect: JdbcDialect)
    : PreparedStatement = {
  val columns = rddSchema.fields.map(x => dialect.quoteIdentifier(x.name)).mkString(",")
  val placeholders = rddSchema.fields.map(_ => "?").mkString(",")
  val sql = s"INSERT INTO $table ($columns) VALUES ($placeholders)"
  conn.prepareStatement(sql)
}

好了,分析完毕,其实sparkSQL入mysql和sparkcore自己读分区入MYSQL是一样的。

怎么使用?

df.write.jdbc(url1, "TEST.TRUNCATETEST", properties)
df2.write.mode(SaveMode.Overwrite).option("truncate", true)
  .jdbc(url1, "TEST.TRUNCATETEST", properties)

properties,配置信息,key:user,password,url,dbtable,batchsize,isolationLevel:NONE,READ_COMMITTED,READ_UNCOMMITTED,REPEATABLE_READ,SERIALIZABLE,默认:READ_UNCOMMITTED


if (!committed) {
  // The stage must fail.  We got here through an exception path, so
  // let the exception through unless rollback() or close() want to
  // tell the user about another problem.
  if (supportsTransactions) {
    conn.rollback()
  }
  conn.close()

如何实现mysql到其他数据库的直接传输:

主要利用ResultSet中的

getMetaData来获取字段个数Array.tabulate[Object](rs.getMetaData.getColumnCount)(i => rs.getObject(i + 1))
然后通过PreparedStatement的setObject来进行插入

实战过程中的问题:

    1.设置了batchsieze为什么插入还是很慢?

          mysql必须要在链接中设置批处理开启,jdbc:mysql://localhost:3306/mydb?rewriteBatchedStatements=true

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要通过Java代码实现SparkSQL操作数据库,需要遵循以下步骤: 1. 导相关的依赖库,包括Spark SQLJDBC驱动程序。 2. 创建SparkSession对象,设置相关的配置信息,如应用程序名称、Master URL等。 3. 使用SparkSession对象创建DataFrame或Dataset对象,通过读取数据库中的表或查询结果来加载数据。 4. 对DataFrame或Dataset对象进行数据处理和转换,如过滤、聚合、排序等操作。 5. 将处理后的数据保存到数据库中,或者通过JDBC连接执行SQL语句对数据库进行操作。 下面是一个简单的示例代码,演示了如何使用Java代码实现SparkSQL操作数据库: ```java import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SparkSession; public class SparkSQLDemo { public static void main(String[] args) { // 创建SparkSession对象 SparkSession spark = SparkSession.builder() .appName("SparkSQLDemo") .master("local[*]") .config("spark.sql.warehouse.dir", "/user/hive/warehouse") .getOrCreate(); // 读取数据库中的表 Dataset<Row> df = spark.read() .format("jdbc") .option("url", "jdbc:mysql://localhost:3306/test") .option("dbtable", "student") .option("user", "root") .option("password", "123456") .load(); // 对数据进行处理和转换 Dataset<Row> result = df.filter("age > 18") .groupBy("gender") .count() .orderBy("gender"); // 将结果保存到数据库中 result.write() .format("jdbc") .option("url", "jdbc:mysql://localhost:3306/test") .option("dbtable", "result") .option("user", "root") .option("password", "123456") .save(); // 关闭SparkSession对象 spark.stop(); } } ``` 在上面的示例代码中,我们使用SparkSession对象读取了数据库中的student表,然后对数据进行了过滤、聚合和排序等操作,最后将结果保存到了result表中。需要注意的是,我们在读取和保存数据时都使用了JDBC连接,并设置了相关的参数,如数据库URL、用户名和密码等。 ### 回答2: 使用Java代码操作SparkSQL数据库需要按照以下步骤进行: 1. 引Spark SQLJDBC的相关依赖 在使用SparkSQLJDBC之前,需要在Java项目中引相关依赖。可以通过Maven或Gradle等构建工具引这些依赖。比如,以下是使用Maven引的相关依赖: ```xml <dependencies> <!--Spark SQL依赖--> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version>2.4.5</version> </dependency> <!--JDBC依赖--> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.5</version> </dependency> </dependencies> ``` 2. 创建SparkSession对象 在Java代码中使用SparkSQL时,需要先创建SparkSession对象,该对象是SparkSQL的一个口点。可以通过如下代码创建SparkSession对象: ```java SparkSession spark = SparkSession .builder() .appName("Java Spark SQL Example") .config("spark.master", "local") .getOrCreate(); ``` 3. 连接数据库 连接数据库需要使用JDBC驱动程序来完成。可以通过如下代码连接PostgreSQL数据库: ```java //定义JDBC链接URL和用户名密码 String dbUrl = "jdbc:postgresql://localhost:5432/testdb"; String username = "postgres"; String password = "postgres"; //创建连接 Properties connectionProperties = new Properties(); connectionProperties.setProperty("user", username); connectionProperties.setProperty("password", password); connectionProperties.setProperty("driver", "org.postgresql.Driver"); //读取数据库表数据 Dataset<Row> jdbcDF = spark.read() .jdbc(dbUrl, "person", connectionProperties); jdbcDF.show(); ``` 4. 执行SparkSQL操作 连接数据库后,就可以执行SparkSQL操作了。可以使用DSL语言或SQL语句来操作数据。比如,以下是使用DSL语言操作数据的代码: ```java //过滤25岁以上的人员 Dataset<Row> filteredDF = jdbcDF.filter(col("age").gt(25)); //按照姓名进行分组,并统计每组的人数 Dataset<Row> groupedDF = filteredDF.groupBy("name").count(); groupedDF.show(); ``` 以上就是使用Java代码实现SparkSQL操作数据库的步骤。通过这些步骤,我们可以轻松地读取和操作数据库中的数据,并使用SparkSQL进行数据分析和处理。 ### 回答3: Spark SQL 是 Apache Spark 提供的一个模块,允许我们使用 Structured Data 的方式来查询和操作数据。它支持将 SQL 查询转变为 Spark 的 RDD,并可以在 Spark 中直接运行 SQL 查询,使得处理任何数据都变得更加简单和高效。通过 Spark SQL,我们可以使用 SQL 的语法针对不同的数据源,如 Hive、HBase 或 Parquet 文件做数据分析。 而要通过 Java 代码实现 Spark SQL 操作数据库,我们需要使用以下步骤: 1. 首先,需要在代码中引 Spark SQL 的依赖,通常使用 Maven 或 Gradle 等构建工具进行依赖管理。 2. 接着,需要创建 SparkSession 对象,用于和 Spark 进行交互。 3. 然后,需要将外部数据源(如 JDBC 数据库)的数据读取到 Spark SQL 中,可以使用以下代码实现: ``` Dataset<Row> jdbcDF = sparkSession.read() .format("jdbc") .option("url", "jdbc:mysql://localhost/testdb") .option("dbtable", "mytable") .option("user", "root") .option("password", "mypassword") .load(); ``` 其中,`format` 参数用于指定要加载的数据源类型,`url` 参数用于指定数据库的连接地址,`dbtable` 参数用于指定要读取的数据表,`user` 和 `password` 参数用于指定数据库的登录信息。 4. 然后,我们就可以对读取到的数据进行 SQL 查询,例如: ``` jdbcDF.createOrReplaceTempView("mytable"); Dataset<Row> resultDF = sparkSession.sql("SELECT * FROM mytable WHERE age > 18"); ``` 其中,`createOrReplaceTempView` 方法用于将读取到的数据集注册为一张表格,`sql` 方法用于执行 SQL 查询。该查询返回的结果会被封装为一个 Dataset<Row> 对象。 5. 最后,我们可以将查询结果保存到外部数据源中,例如: ``` resultDF.write() .format("jdbc") .option("url", "jdbc:mysql://localhost/testdb") .option("dbtable", "result_table") .option("user", "root") .option("password", "mypassword") .save(); ``` 这段代码将查询结果保存到 MySQL 数据库的 `result_table` 表中。 通过以上步骤,我们就可以使用 Java 代码实现 Spark SQL 操作数据库的功能了。当然,在实际使用中,我们还需要处理一些异常情况,如连接超时、SQL 语法错误等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值