流式计算未来的发展趋势

受 SIGMOD 会议的论文 Beyond Analytics: The Evolution of Streaming Processing Systems 的启发,聊一下流式计算未来的趋势。

有兴趣的同学可以读一下论文,论文前半部分主要讲的是流式计算的发展,说了很多流计算里特有的概念,在这里略过,从第 4 节开始看。

4.1 Emerging Applications

  1. Cloud Application
  2. Machine Learning
  3. Streaming Graphs

第一点,云原生的概念火热的飞起,不过确实,过往经验中使用过 AWS 的云服务和 IDC 的物理机,维护成本完全是不能比。随着资本从互联网向其他方向转移,融资困难的情况下,互联网公司的硬件成本想必也会在创业初期成为很重要的一个考虑因素。在以前使用 AWS 云服务的过程中,举两个简单的例子:

  • 对象存储按量收费,对象存储有点类似于冷存,因为云服务提供商有全局视角,所以可以很合理地来配置存储资源,使得资源利用率非常高,所以对象存储的存储费用极低,但是每次访问占用的带宽和 CPU 是要按量付费。我觉得这个就很合理了,将历史的明细数据作为冷存放入到对象存储中,只需要将近期数据和热点数据放在 HDFS 或者其他 OLAP 存储里,可以极大地压低大数据量公司的存储成本
  • 廉价的不稳定机型,这也是当时让我很吃惊的点。AWS 的机型中有一种很廉价,但是稳定性保证很差的机型,不适合常驻的服务,但是在做好推测执行、Shuffle Service 的情况下,非常适合跑 BATCH 任务,因为 BATCH 任务本身就是离线任务,且 DAG 的任务天生可以通过拓扑之间的关系进行容错。

第二点是机器学习,这个应该是没有争议的,就不解释了。

第三点是流式计算中特有的概念,值得说一下。以 Spark、Flink 为例,这些计算框架通常是把用户写的代码或者 SQL 转化为一个 DAG,然后根据 DAG 来进行调度。我们把这种预先生成的 DAG 叫做 Static Graph,即使是迭代计算的场景,其计算的拓扑和公式也都是不变的。而在目前深度学习的场景中,我们会在执行过程中不断调整计算的公式和模式,所以拓扑也会发生相应的变化,我们把这种拓扑叫做 Dynamic Graph,目前在 Ray 中有所提到。

4.2 The road ahead

Programming Models

这个也很值得一提,不管是流式还是批式作业,我们编写分布式应用的方式就两种,1 是用框架中的专属概念,比如 Spark 中的 RDD,Flink 中的 DataStream 等,2 是用 SQL。

使用代码来开发,需要了解很多分布式计算框架中专属的概念,而在流式计算中使用 SQL 上手门槛依旧很高(批式 SQL 还好),所以现在大数据领域也开始出现很多类似 Faas、Actor Model 的编程模式,比如 Flink 创始公司推的 Stateful Function、Ray 的 Actor 编程模型等。

长远来看,这确实是一个趋势。从和业务方打交道的经验来看,对于从事算法工作的同学来说,了解分布式的部署、执行、高可用等概念实在是太痛苦,而且学习成本太高,而站在他们的角度来看,他们只是想法自己的算法逻辑跑在远端的服务器上,本来并不需要了解这些事情。从这点看,我就特别喜欢 Ray 这种简洁的编程模型,直接一个注解就把函数跑在了分布式服务上,

import ray
ray.init()

@ray.remote
def f(x):
    return x * x

futures = [f.remote(i) for i in range(4)]
print(ray.get(futures))

Transactions

流计算的事务支持。这里主要指的是有状态的服务,需要对状态做到 ACID 的保证,只有做到这点之后,流计算才具有对线上业务提供服务的能力。

Advanced State Backends

这块跟我从事的方向也很有关系。一个是刚刚上面提到的事务能力,二是对 State Backend 能力上的增强。目前还不存在一个比较完美的和流式计算相匹配的状态存储,Flink 的话使用 RocksDB 作为状态存储,在 update 的性能上很成问题。

我对 State Backend 中比较好的设想是

  • 状态使用的介质可以是类似单机 RocksDB 这样 Embedded 的,也可以是 HBase 这样 Remote 形式的,做到一个完全的可插拔,并且数据序列化的方式不依赖于存储介质,用户可以选择在不同的存储介质进行切换。
  • 状态随时可查,就像一个分布式存储一样,这样流计算中产生的状态不止可以作为输出结果使用,还能做真正实时的分析,搭配应用里一些抽象的逻辑,我们可以在实时分析、计算产生状态、结果输出这三个方面形成一个服务线上业务的闭环。

Loops & Cycles

这里提到的是一个反馈闭环的问题,倒是更属于 Stateful Function 的范畴。在已有的大数据计算框架上去实现,我觉得还是非常困难的一个事情。

Elasticity & Reconfiguration

Cloud Native,弹性伸展,目前对于无状态的服务,使用 Kubernetes 或是其他的计算框架都比较容易实现,而对于有状态的服务,如何重新分配状态,并且不中断对线上的服务,这也是比较难的。

目前工业界的主要做法,

  • 停止作业、修改配置、重启作业,将这个流程自动化,去除一些冗余的步骤,但整体的 cost 跟手动操作起来差别不会太大。
  • 双跑作业,同时跑两个作业,Hot 和 Standby,通过 zk 选主,线上服务不中断,但是资源使用需要翻倍,编写代码时需要考虑双跑的容错,比如两个作业的消费起点能否对齐等,使用难度还是偏大。

Dynamic Topologies

需求决定框架实现,上面讲了,总之还是因为深度学习、神经网络的流行,对计算调度的灵活性要求未来会越来越高,尤其是机器人对环境实时反馈的场景,每秒可能需要调度成千上万的小型任务,完成各个传感器对环境中不同因素的感知。

Shared Mutable State

现有大数据计算框架的缺点之一,JobManager 和 TaskManager 的交互模式,使得 Task Manager 之间的资源、状态无法共享,在 Apache Flink 中甚至不能互相通信。那么自然,Task A 计算得到的状态 X 不能给 Task B 用,如果需要实现这样的需求,往往是将发往 Task A 的数据多 copy 一份发送到 Task B,在 Task B 中实现一个同样的逻辑生成状态 X。

如果需要让 Task 之间共享状态,那么一致性保证将会是个难题,这种情况下使用 Embeded 模式的 State Backend 肯定就不太合适了,在 Ray 中使用的是 Plasma 共享内存存储。

Queryable State

之前提到过,skip。

State Versioning

这个需求的话个人感觉还好,如果是对 State 的版本有要求,其实用现有的 Snapshot 和 Checkpoint 也可以实现。

Hardware Acceleration

硬件的加速。

总结

用一句话概括就是,流计算的发展还是得益于用户对实时性的需求的增长。

文中所指的流计算本来并不单纯是 Apache Flink,甚至还包括了 Akka Stream 这类 Streaming Service,但是又所讲的内容又基本上和大数据相关的事情。从大数据领域的角度来讲,流计算在数据分析,实时报表展示方面,已经是作为一个比较成熟的技术栈在广泛使用,工业界中的各大互联网公司也都基本上完成了数仓、报表从离线到实时的转换,也都基本具备了这样的能力。

所以流计算的下一个趋势,必然是从实时分析到赋能业务,具体来看,会产出两个方向:

  • 在已有的流计算模型之上,支持更复杂的数据计算,并且做到更快、更稳定,有一些功能的增强,但不对整体的架构做出大的改变。
  • 支持深度学习、神经网络等赋能业务的技术栈,这就需要一定程度上颠覆我们过去有的一些东西,比如 DAG。