007SparkStreaming
SparkStream的核心抽象是DStream
调优策略:
Sparkstreaming+kafka,用不到receiver,选择direct方式
- 基于receiver
- 基于direct
shuffle上游
shuffle下游 - 调整数据处理的并行度(task)
- 数据的序列化
SparkStreaming两种需要序列化的数据:
a. 输入的数据:默认是以StorageLevel.MEMORY_AND_DISK_SER_2的形式存储在executor上的内存中
b. 缓存的数据:默认是以StorageLevel.MEMORY_ONLY_SER的形式存储的内存中
使用Kryo序列化机制,比Java序列化机制性能好
val conf = new SparkConf().setMaster(...).setAppName(...)
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
val sc = new SparkContext(conf)
-
内存调优
(1)需要内存大小
和transformation的类型有关,如果使用的是updateStateByKey,Window这样的算子,那么内存就要设置得偏大
(2)数据存储级别
如果把接收到的数据设置的存储级别是MEMORY_DISK这种级别,也就是说如果内存不够可以把数据存储到磁盘上,其实性能还是不好的,性能最好的就是所有的数据都在内存里面,所以如果在资源允许的情况下,把内存调大一点,让所有的数据都存在内存里面。 -
output性能
数据存储到,mysql、redis/codis、hbase、kafka->sparkstreaming(实时ETL)->kafka
工作中遇到过的问题
两个kafka集群,一个是0.8版本的,一个是1.0版本,写了一个sparkstreaming程序将数据处理完了通过foreachRDD然后读到每个分区,写回0.8kafka。数据处理很慢,经常会受到延时告警
1.0kafka内部有批处理提交功能
0.8kafka没有批量处理功能,需要手动实现批量提交 -
Backpressure(压力反馈)
Spark1.5版本spark.streaming.backpressure.enabled = true
有点问题是:会对kafka限制数据接收的速率,导致kafka数据积压,然后不实时了。 -
Elastic Scaling(资源动态分配)
稳定高效(解决不了数据倾斜的问题)
动态分配资源:
批处理动态的决定这个application中需要多少个Executors:
1.当一个Executor空闲的时候,将这个Executor杀掉
2.当task太多的时候,动态的启动Executors
Streaming分配Executor的原则是比对 process time / batchInterval 的比率,如果延迟了,那么就自动增加资源
从Spark2.0有这个功能: spark.streaming.dynamicAllocation.enabled = true -
数据倾斜调优(重要)
SparkStreaming底层就是RDD,SparkCore的所有数据倾斜调优策略都适合于SparkStreaming。
以下并不重要(除非你们用到了)
- 调整BlockReceiver的数量
object MultiReceiverNetworkWordCount {
def main(args: Array[String]) {
val sparkConf = new SparkConf().setAppName("WordCount")
val sc = new SparkContext(sparkConf)
// Create the context with a 1 second batch size
val ssc = new StreamingContext(sc, Seconds(5))
//创建多个接收器(ReceiverInputDStream),这个接收器接收一台机器上的某个端口通过socket发送过来的数据并处理
val lines1 = ssc.socketTextStream("master", 9998, StorageLevel.MEMORY_AND_DISK_SER)
val lines2 = ssc.socketTextStream("master", 9997, StorageLevel.MEMORY_AND_DISK_SER)
val lines = lines1.union(lines2)
lines.repartition(100)
//处理的逻辑,就是简单的进行word count
val words = lines.repartition(100).flatMap(_.split(" "))
val wordCounts = words.map(x => (x, 1)).reduceByKey((a: Int, b: Int) => a + b, new HashPartitioner(10))
//将结果输出到控制台
wordCounts.print()
//启动Streaming处理流
ssc.start()
//等待Streaming程序终止
ssc.awaitTermination()
ssc.stop(false)
}
}
- 调整Block的数量
batchInterval : 触发批处理的时间间隔
blockInterval :将接收到的数据生成Block的时间间隔,spark.streaming.blockInterval(默认是200ms),那么,BlockRDD的分区数 = batchInterval / blockInterval,即一个Block就是RDD的一个分区,就是一个task
比如,batchInterval是2秒,而blockInterval是200ms,那么task数为10,如果task的数量太少,比一个executor的core数还少的话,那么可以减少blockInterval,blockInterval最好不要小于50ms,太小的话导致task数太多,那么launch task的时间久多了 - 调整Receiver的接收速率 QPS
pps:permits per second 每秒允许接受的数据量(QPS -> queries per second)
Spark Streaming默认的PPS是没有限制的,可以通过参数spark.streaming.receiver.maxRate来控制,默认是Long.Maxvalue - 调整数据处理的并行度
BlockRDD的分区数
a. 通过Receiver接受数据的特点决定
b. 也可以自己通过repartition设置
ShuffleRDD的分区数
a. 默认的分区数为spark.default.parallelism(core的大小)
b. 通过我们自己设置决定
val wordCounts = words.map(x => (x, 1)).reduceByKey((a: Int, b: Int) => a + b, new HashPartitioner(10))