使用SparkSQL实现根据ip地址计算归属地二

本文介绍了一种在SparkSQL中使用广播变量优化大规模数据处理的方法,避免了在大数据量下进行Join操作的高成本,通过将IP地址规则缓存在每台机器上,显著减少了I/O操作,提高了处理效率。

在使用SparkSQL实现根据ip地址计算归属地一 中虽然实现了最终目的,但是当数据量大的时候Join的代价是很大的,因为其他机器上都没有这个ip地址规则,所以要想进行比较只能从其他机器上拉过来再进行比较,那么如何进行优化呢,我们通过之前的使用SparkCore中的RDD的操作方式很容易就会想到将ip地址规则给缓存起来,即在每台机器上都有IP地址规则,这样就不用从其他机器上拉取了,也就减少了大量的I/O操作,实现优化

废话不多说,直接上代码:

package cn.ysjh0014.SparkSql

import cn.ysjh0014.TestIp
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}

object IpLocationSQL1 {

  def main(args: Array[String]): Unit = {

    val session: SparkSession = SparkSession.builder().appName("IpLocationSQL1").master("local[4]").getOrCreate()

    //取到HDFS中的ip规则
    import session.implicits._
    val rulesLines: Dataset[String] = session.read.textFile(args(0))
    //整理ip规则数据
    val rules: Dataset[(Long, Long, String)] = rulesLines.map(line => {
      val fields = line.split("[|]")
      val startNum = fields(2).toLong
      val endNum = fields(3).toLong
      val province = fields(6)
      (startNum, endNum, province)
    })

    //收集ip规则到Driver端
    val Rules: Array[(Long, Long, String)] = rules.collect()

    //广播(必须使用SparkContext),返回到Driver端
    val broadcast: Broadcast[Array[(Long, Long, String)]] = session.sparkContext.broadcast(Rules)


    //创建RDD,读取访问日志
    val accessLines: Dataset[String] = session.read.textFile(args(1))

    //整理数据
    val result: Dataset[Long] = accessLines.map(log => {
      //将log日志的每一行进行切分
      val fields = log.split("[|]")
      val ip = fields(1)
      //将ip转换成十进制
      val ipNum = TestIp.ip2Long(ip)
      ipNum
    })

    val DFResult: DataFrame = result.toDF("ip_Num")

    DFResult.createTempView("table")

    //定义一个自定义函数(UDF),并注册,该函数的功能是输入一个ip地址对应的十进制,返回一个省份名称
    session.udf.register("IpProvice", (ipNum: Long) => {
      //查找ip地址规则,实现已经广播,已经在Executor中了,使用广播变量的引用就可以获取ip规则对应的数据了
      val IpRules: Array[(Long, Long, String)] = broadcast.value
      //根据ip地址对应的十进制查找省份名称
      val index = TestIp.binarySearch(IpRules, ipNum)

      var provice = "未知"
      if (index != -1) {
        provice = IpRules(index)._3
      }
      provice

    })

    //执行Sql
    val r: DataFrame = session.sql("SELECT IpProvice(ip_Num) provice, COUNT(*) counts FROM table GROUP BY provice ORDER BY counts DESC")

    r.show()

    session.stop()

  }
}

运行结果就不展示了,都是跟之前的案例结果一样,这里重点在于优化的过程,而不是结果,很明显上边的代码至少不用进行Join操作了,当数据量大的时候会有优势

注意:

这里的代码是在之前的案例基础上的,但是运行之后会发现报错,如下图:

这是因为spark版本的问题,将pom文件中的spark版本由原来的2.1.0改为2.2.0就不会报错了

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值