Spark流式状态管理(updateStateByKey、mapWithState等)
通常使用Spark的流式框架如Spark Streaming,做无状态的流式计算是非常方便的,仅需处理每个批次时间间隔内的数据即可,不需要关注之前的数据,这是建立在业务需求对批次之间的数据没有联系的基础之上的。
但如果我们要跨批次做一些数据统计,比如batch是3秒,但要统计每1分钟的用户行为,那么就要在整个流式链条中维护一个状态来保存近1分钟的用户行为。
那么如果维护这样一个状态呢?一般情况下,主要通过以下几种方式:
1. spark内置算子:updateStateByKey、mapWithState
2. 第三方存储系统维护状态:如redis、alluxio、HBase这里主要以spark内置算子:updateStateByKey、mapWithState为例,通过一些示例代码(不涉及offset管理),来看看如何进行状态维护。
updateStateByKey
分析相关源码发现,这个算子的核心思想就是将之前有状态的RDD和当前的RDD做一次cogroup,得到一个新的状态的RDD,具有如下特点:
1. 可以设置初始状态
2. key超时删除。用updatefunc返回None值。updateFunc不管是否有已保存状态key的新数据到来,都会被已存在状态的key调用,新增的key也会调用3. 不适合大数据量状态存储,尤其是key的维度比较高、value状态比较大的
- /**
- * @author:微信公众号:大数据学习与分享
- */
- object StateOperator {
- private val brokers = "kafka-1:9092,kafka-2:9092,kafka-3:9092"
- private val topics = "test"
- private val groupId = "test"
- private val batchTime = "10"
- private val mapwithstateCKDir = "/mapwithstate"
- private val updateStateByKeyCKDir = "/mapwithstate"
- def main(args: Array[String]): Unit = {
- val ssc = StreamingContext.getOrCreate(updateStateByKeyCKDir, () => createContext(brokers, topics, groupId, batchTime, updateStateByKeyCKDir))
- ssc.start()
- ssc.awaitTermination()
- }
- def createContext(brokers: String, topics: String,
- groupId: String, batchTime: String,
- checkpointDirectory: String): StreamingContext = {
- val conf = new SparkConf().setAppName("testState").setMaster("local[*]")
- .set("spark.streaming.kafka.maxRatePerPartition", "5")
- .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
- val ssc = new StreamingContext(conf, Seconds(batchTime.toInt))
- val topicsSet = topics.split(",").toSet
- val kafkaParams = Map[String, String]("metadata.broker.list" -> brokers,
- "group.id" -> groupId,
- "auto.offset.reset" -> "smallest")
- val streams = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicsSet)
- .map(_._2).flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
- ssc.checkpoint("/redis/updateStateByKey")
- val initialRDD = ssc.sparkContext.parallelize(List(("word", 0)))
- //updateStateByKey 底层核心是对preStateRDD(之前数据状态的RDD)和当前批次的RDD进行cogroup
- val stateStreams = streams.updateStateByKey(updateFunc, new HashPartitioner(ssc.sparkContext.defaultParallelism), true, initialRDD)
- stateStreams.checkpoint(Duration(5))
- stateStreams.foreachRDD { rdd =>
- val res = rdd.map { case (word, count) => (count, word) }.sortByKey(false).take(10).map { case (v, k) => (k, v) }
- res.foreach(println)
- }
- ssc.checkpoint(checkpointDirectory)
- ssc
- }
- //无论当前批次RDD有多少key(比如preStateRDD有而当前批次没有)都需要对所有的数据进行cogroup并调用一次定义的updateFunc函数
- val updateFunc = (iterator: Iterator[(String, Seq[Int], Option[Int])]) => {
- iterator.flatMap(t => Some(t._2.sum + t._3.getOrElse(0)).map(v => (t._1, v)))
- }
- }
通过updateStateByKey获得的是整个状态的数据,而且每次状态更新时都要将当前批次过来的数据与之前保存的状态进行cogroup操作,并且对所有数据都调用自定义的函数进行一次计算。
随着时间推移,数据量不断增长,需要维护的状态越来越大,会非常影响性能。如果不能在当前批次将数据处理完成,很容易造成数据堆积,影响程序稳定运行甚至宕掉,这就引出了mapWithState。
mapWithState
支持输出全量的状态和更新的状态,还支持对状态超时管理,用户可以根据业务需求选择需要的输出,性能优于于updateStateByKey。
- def main(args: Array[String]): Unit = {
- //单词统计
- val ssc = StreamingContext.getOrCreate(mapwithstateCKDir,
- () => createContext(brokers, topics, groupId, batchTime, mapwithstateCKDir))
- ssc.start()
- ssc.awaitTermination()
- }
- def createContext(brokers: String, topics: String,
- groupId: String, batchTime: String,
- checkpointDirectory: String): StreamingContext = {
- val conf = new SparkConf().setAppName("testState").setMaster("local[*]")
- .set("spark.streaming.kafka.maxRatePerPartition", "5")
- .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
- val ssc = new StreamingContext(conf, Seconds(batchTime.toInt))
- val topicsSet = topics.split(",").toSet
- val kafkaParams = Map[String, String]("metadata.broker.list" -> brokers,
- "group.id" -> groupId,
- "auto.offset.reset" -> "smallest")
- val messages = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topicsSet)
- .map(_._2).flatMap(_.split(" ")).map((_, 1L)).reduceByKey(_ + _)
- val stateStreams = messages.mapWithState(StateSpec.function(mapFunc).timeout(Seconds(60))).stateSnapshots()
- //.checkpoint(Duration(5))
- stateStreams.foreachRDD { (rdd, time) =>
- println("========do something")
- }
- ssc.checkpoint(checkpointDirectory)
- ssc
- }
- //key为word,value为当前批次值,state为本批次之前的状态值
- val mapFunc = (key: String, value: Option[Long], state: State[Long]) => {
- //检测是否过期
- if (state.isTimingOut()) {
- println(s"$key is timing out")
- } else {
- val sum = state.getOption().getOrElse(0L) + value.getOrElse(0L)
- val output = (key, sum)
- //更新状态
- state.update(sum)
- output
- }
- }
- val mapFunction = (time: Time, word: String, count: Option[Int], state: State[Int]) => {
- val sum = count.getOrElse(0) + state.getOption().getOrElse(0)
- val output = (word, sum)
- state.update(sum)
- Option(output)
- }
虽然mapWithState相对于updateStateByKey性能更优,但仍然不适合大数据量的状态维护,此时就需要借用第三方存储来进行状态的维护了,redis、alluxio、HBase是常用的选择。
redis比较适合维护key具有超时处理机制的场景使用;alluxio的吞吐量更高,适合于数据量更大时的场景处理。
具体采用哪种方式,要结合实际的业务场景、数据量、性能等多方面的考量。
关注微信公众号:大数据学习与分享,获取更对技术干货
Spark流式状态管理(updateStateByKey、mapWithState等)的更多相关文章
- JAVA 流式布局管理器
//流式布局管理器 import java.awt.*; import javax.swing.*; public class Jiemian2 extends JFrame{ //定义组件 JBut ...
- JAVA流式布局管理器--JAVA基础
JAVA流式布局管理器的使用: FlowLayoutDeme.java: import java.awt.*;import javax.swing.*;public class FlowLayoutD ...
- Spark流式编程介绍 - 编程模型
来源Spark官方文档 http://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#programm ...
- 实时查询系统架构:spark流式处理+HBase+solr/ES查询
最近要做一个实时查询系统,初步协商后系统的框架 1.流式计算:数据都给spark 计算后放回HBase 2.查询:查询采用HBase+Solr/ES
- Spark Streaming状态管理函数updateStateByKey和mapWithState
Spark Streaming状态管理函数updateStateByKey和mapWithState 一.状态管理函数 二.mapWithState 2.1关于mapWithState 2.2mapW ...
- java 图形化小工具Abstract Window Toolit ;布局管理器FlowLayout流式布局;BorderLayout边界布局;GridLayout网格布局;CardLayou重叠卡片布局;BoxLayout方框布局;绝对定位
1.FlowLayout流式布局管理器: FlowLayout布局管理器中,组件像水流一样向某方向流动(排列),遇到障碍(边界)就折回,重头开始排列 .在默认情况下,FlowLayout局管理器从左向 ...
- 流式处理新秀Flink原理与实践
随着大数据技术在各行各业的广泛应用,要求能对海量数据进行实时处理的需求越来越多,同时数据处理的业务逻辑也越来越复杂,传统的批处理方式和早期的流式处理框架也越来越难以在延迟性.吞吐量.容错能力以及使用便 ...
- Redux/Mobx/Akita/Vuex对比 - 选择更适合低代码场景的状态管理方案
近期准备开发一个数据分析 SDK,定位是作为数据中台向外输出数据分析能力的载体,前端的功能表现类似低代码平台的各种拖拉拽.作为中台能力的载体,SDK 未来很大概率会需要支持多种视图层框架,比如Vue2 ...
- 如何使用 CODING 进行瀑布流式研发
你好,欢迎使用CODING!这份最佳实践将帮助你通过 CODING 更好地实践瀑布流式开发流程. 什么是瀑布流式研发 1970 年温斯顿·罗伊斯(Winston Royce)提出了著名的"瀑 ...
随机推荐
- 6. Spark SQL和Beeline
*以下内容由<Spark快速大数据分析>整理所得. 读书笔记的第六部分是讲的是Spark SQL和Beeline. Spark SQL是Spark用来操作结构化和半结构化数据的接口. 一. ...
- Effective Modern C++ ——条款6 当auto型别不符合要求时,使用带显式型别的初始化物习惯用法
类的代理对象 其实这部分内容主要是说明了在STL或者某些其他代码的容器中,在一些代理类的作用下使得最后的返回值并不是想要的结果. 而他的返回值则是类中的一个容器,看下面的一段代码: std::vect ...
- 【JVM第五篇--运行时数据区】方法区
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.栈.堆.方法区的关系 虚拟机运行时的数据区如下所示: 即方法区是属于线程共享的内 ...
- 论文学习笔记 - 高光谱 和 LiDAR 融合分类合集
A³CLNN: Spatial, Spectral and Multiscale Attention ConvLSTM Neural Network for Multisource Remote Se ...
- Python_获取cookie
获取cookie from selenium import webdriver from selenium.webdriver.common.by import By # 定位 from seleni ...
- MFC读写文件详解
1.CFile类提供了对文件进行打开,关闭,读,写,删除,重命名以及获取文件信息等文件操作的基本功能,足以处理任意类型的文件操作. 虽然使用CArchive类内建的序列化功能是保存和加载持久性数据的便 ...
- Guitar Pro怎么导出乐谱
使用Guitar Pro可以自由创作乐谱,也能根据演示效果来作出相应调整,算得上是公认的良心吉他谱制作软件.除了系统演示功能外,Guitar Pro还能给用户的实际练习提供便利.必要时,用户能将软件内 ...
- Vegas技巧分享,如何实现5.1立体声道
Vegas Pro 15除了有各种好用的功能之外,还可以使用此软件制作5.1立体声道的音乐.如果你家里有多个音箱,那么你再也不用担心找不到5.1声道的音乐了,Vegas完全可以帮你实现. 打开简体中文 ...
- leetcode187. 重复的DNA序列
所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:"ACGAATTCCG".在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助.编写一个函数 ...
- python的pip安装超时问题解决
使用pip install 安装python第三方库时出现了如下错误:pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionP ...