Spark中ip映射数据应用库,二分查找省份,将结果写入mysql

需求:
* 根据数据应用库,查找ip对应的省份,将数据写出到mysql。(二分查找方法)
* 使用广播变量进行执行优化。

import java.sql.{Connection, DriverManager, PreparedStatement}

import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
  * Created by hqs on 2018/1/28.
  * 根据数据应用库,查找ip对应的省份,将数据写出到mysql。(二分查找方法)
  * 使用广播变量进行执行优化。
  * 实现代码重构。
  */
object IpLocation3 {

 //字符串ip转十进制
  def ip2Long(ip: String): Long = {
    val fragments = ip.split("[.]")
    var ipNum = 0L
    for (i <- 0 until fragments.length) {
      ipNum = fragments(i).toLong | ipNum << 8L
    }
    ipNum
  }
  //二分查找实现
  def search(ip:Long,ipArray:Array[(Long,Long,String)]):Int ={

    var start = 0
    var end = ipArray.length-1
    while(start<=end){
      val middle = (start+end) >> 1
      val middleKey = ipArray(middle)
      val middleKeyStart = middleKey._1
      val middleKeyEnd = middleKey._2
      if(ip >= middleKeyStart && ip <= middleKeyEnd){
        return middle
      }else if(ip < middleKeyStart){
        end = middle -1
      }else{
        start = middle +1
      }
    }
    //若无法匹配,返回-1
    -1
  }
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    .setMaster("local")
    .setAppName(IpLocation3.getClass.getName)
    val sc = new SparkContext(conf)

    val ipData: RDD[String] = sc.textFile("ip.txt")
    val ipRule:RDD[(Long,Long,String)] = ipData.map({
      line=>
        val fields = line.split("[|]")
        val start = fields(2).toLong
        val end = fields(3).toLong
        val province = fields(6)
        (start,end,province)
    })
    val ipRules = ipRule.collect()
    //使用广播变量,将数据广播到executor。多个task使用这个数据。减少连接请求。
    val ipbc: Broadcast[Array[(Long, Long, String)]] = sc.broadcast(ipRules)


    val accessData: RDD[String] = sc.textFile("access.log")
    val proAndOne:RDD[(String,Int)] = accessData.map({
      line =>
        val ipLong:String = line.split("[|]")(1)

        //将字符串的ip转为十进制的ip。
        val ip = ip2Long(ipLong)
        //将广播变量里面的数据取出,使用。
        val ipArray: Array[(Long, Long, String)] = ipbc.value
        //二分查找数据在引用库中的索引。
        val index = search(ip,ipArray)
        var province = "unknow"
        if(index != -1){
          province = ipArray(index)._3
        }
        (province,1)
    })

    //产生结果。
    val res: RDD[(String, Int)] = proAndOne.reduceByKey(_+_).sortBy(-_._2)

    res.foreachPartition({
      it =>
        //将结果写入到数据库。获得连接,写数据,关流。
        var conn: Connection = null
        var pst: PreparedStatement = null
        var pst1: PreparedStatement = null
          try{
          val url = "jdbc:mysql://localhost:3306/car?characterEncoding=utf-8"
          conn = DriverManager.getConnection(url,"root","123456")
          pst = conn.prepareStatement("create table if not exists access_log(province varchar(20),count int)")
          pst.execute()
          pst1 = conn.prepareStatement("insert into access_log values(?,?)")
          it.foreach({
            v =>
              pst1.setString(1,v._1)
              pst1.setInt(2,v._2)
              pst1.execute()
          })
        }catch{
          case e : Exception => e.printStackTrace()
        }finally{
          if(pst1 != conn) pst1.close()
          if(pst != conn) pst.close()
          if(null != conn) conn.close()
        }
    })
    //释放资源。
    sc.stop()
  }
}
总结1:广播变量缓存在各个而写从投入的内存中,而不是每个task。
广播变量被创建之后,可以在集群的任何函数中调用。通过 -- .value获取
广播变量是只读的,不能修改。rdd不能被广播,需要收集后再通过sc广播。
总结2:jdbc连接mysql数据库思想。导入jar包。建立连接对象,预处理SQL语句,
执行,关闭连接通道。
总结3:Spark数据写出到mysql中,使用foreachPartition方法,减少数据库连接。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值