Spark 2.x管理与开发-【Spark基础编程案例】案例三:访问数据库

一、需求

将RDD数据保存到MySQL中

取出所有的Tomcat访问日志中的jsp名,计数并将其存入MySQL中。

二、MySQL表格设计

 

 

三、Scala代码实现

package coreExamples

import org.apache.spark.SparkContext
import org.apache.spark.SparkConf
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.DriverManager

/**
 * Spark Core 案例-3
 * 访问数据库-将jsp名字计数并存入MySQL中
 */
object FangWenShuJuKu {
  //本质就是WordCount
  def main(args: Array[String]): Unit = {
    //首先,创建SparkContext对象
    val conf = new SparkConf().setMaster("local").setAppName("FangWenShuJuKu")
    val sc = new SparkContext(conf)
    /**
     * 读入日志解析
     * 192.168.88.1 - - [30/Jul/2017:12:54:42 +0800] "GET /MyDemoWeb/web.jsp HTTP/1.1" 200 239
     * (web.jsp,1)
     */
    val rdd1 = sc.textFile("D:/大数据高级资料及测试/Spark Core案例数据/localhost_access_log.2017-07-30.txt")
      .map(
        line => {
          //一、解析字符串

          //1.得到两个引号之间的东西
          val index1 = line.indexOf("\"")
          val index2 = line.lastIndexOf("\"")
          //GET /MyDemoWeb/web.jsp HTTP/1.1
          val line1 = line.substring(index1 + 1, index2)

          //2.得到两个空格之间的东西
          val index3 = line1.indexOf(" ")
          val index4 = line1.lastIndexOf(" ")
          ///MyDemoWeb/web.jsp
          val line2 = line1.substring(index3 + 1, index4)

          //3.得到jsp名字
          //web.jsp
          val jspName = line2.substring(line2.lastIndexOf("/") + 1)

          //4.返回jsp名字并计数
          (jspName, 1)
        })
    //    rdd1.foreach(
    //      t => {
    //        var conn: Connection = null
    //        var pst: PreparedStatement = null
    //        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xinrong_2?characterEncoding=UTF-8&useSSL=false", "root", "123456")
    //        pst = conn.prepareStatement("insert into jspName value(?,?)")
    //        pst.setString(1, t._1)
    //        pst.setInt(2, t._2)
    //        pst.executeUpdate()
    //      })
    /**
     * 对上面的程序进行优化:
     * 针对分区数据操作
     */
    rdd1.foreachPartition(saveToMysql)
    sc.stop()
  }
  //二、定义针对分区的数据的操作
  def saveToMysql(it: Iterator[(String, Int)]) = {
    var conn: Connection = null
    var pst: PreparedStatement = null
    try {
      conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/xinrong_2?characterEncoding=UTF-8&useSSL=false", "root", "123456")
      pst = conn.prepareStatement("insert into jspName value(?,?)")
      it.foreach(
        t => {
          pst.setString(1, t._1)
          pst.setInt(2, t._2)
          pst.executeUpdate()
        })
    } catch {
      case t: Throwable => t.printStackTrace()
    } finally {
      if (pst != null) pst.close()
      if (conn != null) conn.close()
    }
  }
}

四、注意-容易出现的问题

Task not serializable-没有序列化

产生错误原因:

原来:conn和pst的定义和赋值都是在foreach()之前

修改之后:conn和pst的定义和赋值都是在foreach()里面

 

 

 

虽然这样运行没毛病,但是每一条数据都要获取一次jdbc的连接,然后访问MySQL数据库,性能太低

优化后:针对分区的数据进行操作

 

 五、结果

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3