其他
Apache Flink在小米的发展和应用
By 大数据技术与架构
Flink 在小米的发展简介
Spark Streaming 迁移到 Flink 的效果小结
在业务从 Spark Streaming 迁移到 Flink 的过程中,我们也一直在关注着一些指标的变化,比如数据处理的延迟、资源使用的变化、作业的稳定性等。其中有一些指标的变化是在预期之中的,比如数据处理延迟大大降低了,一些状态相关计算的“准确率”提升了;但是有一项指标的变化是超出我们预期的,那就是节省的资源。信息流推荐业务是小米从 Spark Streaming 迁移到 Flink 流式计算最早也是使用 Flink 最深的业务之一,在经过一段时间的合作优化后,对方同学给我们提供了一些使用效果小结,其中有几个关键点:
对于无状态作业,数据处理的延迟由之前 Spark Streaming 的 16129ms 降低到 Flink 的 926ms,有 94.2% 的显著提升(有状态作业也有提升,但是和具体业务逻辑有关,不做介绍); 对后端存储系统的写入延迟从 80ms 降低到了 20ms 左右,如下图(这是因为 Spark Streaming 的 mini batch 模式会在 batch 最后有批量写存储系统的操作,从而造成写请求尖峰,Flink 则没有类似问题):
对于简单的从消息队列 Talos 到存储系统 HDFS 的数据清洗作业(ETL),由之前 Spark Streaming 的占用 210 个 CPU Core 降到了 Flink 的 32 个 CPU Core,资源利用率提高了 84.8%;
其中前两点优化效果是比较容易理解的,主要是第三点我们觉得有点超出预期。为了验证这一点,信息流推荐的同学帮助我们做了一些测试,尝试把之前的 Spark Streaming 作业由 210 个 CPU Core 降低到 64 个,但是测试结果是作业出现了数据拥堵。这个 Spark Streaming 测试作业的 batch interval 是 10s,大部分 batch 能够在 8s 左右运行完,偶尔抖动的话会有十几秒,但是当晚高峰流量上涨之后,这个 Spark Streaming 作业就会开始拥堵了,而 Flink 使用 32 个 CPU Core 却没有遇到拥堵问题。
很显然,更低的资源占用帮助业务更好的节省了成本,节省出来的计算资源则可以让更多其他的业务使用;为了让节省成本能够得到“理论”上的支撑,我们尝试从几个方面研究并对比了 Spark Streaming 和 Flink 的一些区别:
调度计算 VS 调度数据
对于任何一个分布式计算框架而言,如果“数据”和“计算”不在同一个节点上,那么它们中必须有一个需要移动到另一个所在的节点。如果把计算调度到数据所在的节点,那就是“调度计算”,反之则是“调度数据”;在这一点上 Spark Streaming 和 Flink 的实现是不同的。// RDD
/**
* Optionally overridden by subclasses to specify placement preferences.
*/
protected def getPreferredLocations(split: Partition): Seq[String] = Nil
// KafkaRDD
override def getPreferredLocations(thePart: Partition): Seq[String] = {
val part = thePart.asInstanceOf[KafkaRDDPartition]
Seq(part.host)
// host: preferred kafka host, i.e. the leader at the time the rdd was created
}
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
}
}
虽然“调度数据”和“调度计算”有各自的优势,但是在流式计算的实际生产场景中,“调度计算”很可能“有力使不出来”;比如一般流式计算都是消费消息队列 Kafka或 Talos 的数据进行处理,而实际生产环境中为了保证消息队列的低延迟和易维护,一般不会和计算节点(比如 Yarn 服务的节点)混布,而是有各自的机器(不过很多时候是在同一机房);所以无论是 Spark 还是 Flink,都无法避免消息队列数据的跨网络传输。所以从实际使用体验上讲,Flink 的调度数据模式,显然更容易减少损耗,提高计算效率,同时在使用上更符合用户“直觉”,不易出现重复创建资源的情况。
不过这里不得不提的一点是,Spark Streaming 的“调度计算”模式,对于处理计算系统中的“慢节点”或“异常节点”有天然的优势。比如如果 Yarn 集群中有一台节点磁盘存在异常,导致计算不停地失败,Spark 可以通过 blacklist 机制停止调度计算到该节点,从而保证整个作业的稳定性。或者有一台计算节点的 CPU Load 偏高,导致处理比较慢,Spark 也可以通过 speculation 机制及时把同一计算调度到其他节点,避免慢节点拖慢整个作业;而以上特性在 Flink 中都是缺失的。
Mini batch VS streaming
总之,通过对比可以看出,Flink 的 streaming 模式对于低延迟处理数据比较友好,Spark 的 mini batch 模式则于异常恢复比较友好;如果在大部分情况下作业运行稳定的话,Flink 在资源利用率和数据处理效率上确实更占优势一些。
数据序列化
相比于 Java 原生序列化方式,无论是在序列化效率还是序列化结果的内存占用上,Kryo 则更好一些(Spark 声称一般 Kryo 会比 Java 原生节省 10x 内存占用);Spark 文档中表示它们之所以没有把 Kryo 设置为默认序列化框架的唯一原因是因为 Kryo 需要用户自己注册需要序列化的类,并且建议用户通过配置开启 Kryo。虽然如此,根据 Flink 的测试,Kryo 依然比 Flink 自己实现的序列化方式效率要低一些;如图所示是 Flink 序列化器(PojoSerializer、RowSerializer、TupleSerializer)和 Kryo 等其他序列化框架的对比,可以看出 Flink 序列化器还是比较占优势的:那么 Flink 到底是怎么做的呢?网上关于 Flink 序列化的文章已经很多了,这里我简单地说一下我的理解。像 Kryo 这种序列化方式,在序列化数据的时候,除了数据中的“值”信息本身,还需要把一些数据的 meta 信息也写进去(比如对象的 Class 信息;如果是已经注册过的 Class,则写一个更节省内存的 ID)。但是在 Flink 场景中则完全不需要这样,因为在一个 Flink 作业 DAG 中,上游和下游之间传输的数据类型是固定且已知的,所以在序列化的时候只需要按照一定的排列规则把“值”信息写入即可(当然还有一些其他信息,比如是否为 null)。
// Not a POJO demo.public class Person { private Logger logger = LoggerFactory.getLogger(Person.class); public String name; public int age;}
final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
// register the serializer included with Apache Thrift as the standard serializer
// TBaseSerializer states it should be initialized as a default Kryo serializer
env.getConfig().addDefaultKryoSerializer(MyCustomType.class, TBaseSerializer.class);
本文小结
Flink 和 Spark Streaming 有非常大的差别,也有各自的优势,这里我只是简单介绍了一下自己浅薄的理解,不是很深入。不过从实际应用效果来看,Flink 确实通过高效的数据处理和资源利用,实现了成本上的优化;希望能有更多业务可以了解并试用Flink,后续我们也会通过 Flink SQL为更多业务提供简单易用的流式计算支持,谢谢!参考文献:
《Deep Dive on Apache Flink State》 - Seth Wiesman
https://www.slideshare.net/dataArtisans/webinar-deep-dive-on-apache-flink-state-seth-wiesman
Flink 原理与实现:内存管理https://ververica.cn/developers/flink-principle-memory-management
Batch Processing Apache Sparkhttps://blog.k2datascience.com/batch-processing-apache-spark-a67016008167
文章不错?点个【在看】吧! 👇