SparkStreaming中的updateStateByKey累加操作

SparkStreaming中的updateStateByKey累加操作_第1张图片
先看官方文档再上代码
文档的大概意思是:
updateStateByKey操作,可以让我们为每个key维护一份state,并持续不断的更新该state。
1、首先,要定义一个state,可以是任意的数据类型;
2、其次,要定义state更新函数——指定一个函数如何使用之前的state和新值来更新state。

对于每个batch,Spark都会为每个之前已经存在的key去应用一次state更新函数,无论这个key在batch中是否有新的数据。如果state更新函数返回none,那么key对应的state就会被删除。

当然,对于每个新出现的key,也会执行state更新函数。

注意,updateStateByKey操作,要求必须开启Checkpoint机制。


import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}

/*
**
* 使用spark streaming完成有状态统计
 */
object StatefulWordCount {
  def main(args: Array[String]): Unit = {
    
    val sparkConf = new SparkConf().setMaster("local[2]").setAppName("StatefulWordCount")
    val ssc = new StreamingContext(sparkConf,Seconds(5))

    // 如果使用了stateful的算子,必须要设置checkpoint
    ssc.checkpoint(".")

    val lines = ssc.socketTextStream("172.16.146.111",6789)

    val result = lines.flatMap(_.split(" ")).map((_,1))
    val state = result.updateStateByKey[Int](updateFunction _)
    state.print()

    //result.print()
    ssc.start()
    ssc.awaitTermination()
  }
  /**
   * 把当前的数据去更新已有的或者是老的数据
   * @param currentValues
   * @param preValues
   * @return
   */
  def updateFunction(currentValues:Seq[Int],preValues:Option[Int]):Option[Int] = {
    val current = currentValues.sum
    val pre = preValues.getOrElse(0);
    Some(current + pre)

  }
}

上面是使用updateStateByKey方法把batch的数据做增量更新;我们还可以把数据的状态更新到mysql数据库中,同样的,按照官网文档来看:

第一种(不可取):

//一个在driver端 一个在worker端口 容易产生序列化错误
dstream.foreachRDD { rdd =>
  val connection = createNewConnection()  // executed at the driver
  rdd.foreach { record =>
    connection.send(record) // executed at the worker
  }
}

第二种(不可取):

/**拿到每一个RDD里面的每一条数据,一条数据产生一个connection,进行大量
*与数据库的交互,虽然解决了序列化问题但是负载太大
*/
dstream.foreachRDD { rdd =>
  rdd.foreach { record =>
    val connection = createNewConnection()
    connection.send(record)
    connection.close()
  }
}

第三种(硬编码):

//假如有100w条数据,有10个partition,只用创建10个connection,好很多
dstream.foreachRDD { rdd =>
  rdd.foreachPartition { partitionOfRecords =>
    val connection = createNewConnection()
    partitionOfRecords.foreach(record => connection.send(record))
    connection.close()
  }
}

第四种(推荐):

//用到了连接池,要多少个拿多少个,使用HBase自带的api能更快捷进行累加统计
dstream.foreachRDD { rdd =>
  rdd.foreachPartition { partitionOfRecords =>
    // ConnectionPool is a static, lazily initialized pool of connections
    val connection = ConnectionPool.getConnection()
    partitionOfRecords.foreach(record => connection.send(record))
    ConnectionPool.returnConnection(connection)  // return to the pool for future reuse
  }
}

你可能感兴趣的:(SparkStreaming中的updateStateByKey累加操作)