大多数开发者已经习惯了无状态服务的理念,倾向于将所有数据存放在远端数据库中,难以理解流式计算中为何需要「局部状态」的存在。此文将阐述流计算中「局部状态」的含义、动机、适用场景和优劣势。

什么是状态?

想象你在使用 SQL 执行一些操作。

如果所有请求都只需要操作单行数据(如使用主键ID执行基本的 select 检索操作),那么此服务对数据的依赖可以称之为是「无状态」的。

然而现实场景中往往存在各类聚合(aggregation)、联合(join)操作。

  • 聚合操作如:在某段时间窗口内,对若干页面的广告点击率(CTR,click-through rate)进行统计。

  • 流连接(streaming join)操作如:广告展示(ad impression)数据流同广告点击率数据流两者存在时间先后顺序,但又是强关联的,需进行流连接操作。

  • 字段填充(enrichment)操作如:给仅包含用户ID的广告点击率数据流,补充更详细的用户属性值,以便下游分析系统处理。

远程状态

一种常见的处理模式是,从输入流中依次获取记录,对每条记录执行若干次针对远程分布式数据库的请求。

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

如上图可见,数据流被分派至多个机器上的多个处理单元(processor)上进行处理,每个处理单元都会发起对远端的分布式数据库的请求;由于分布式数据库的特性,这些请求最终落在位于不同机器的各个数据库分区(database partition)上。另一种可能的做法是,将处理单元和数据库分区「绑起来」(co-located),让请求不用兜一个大圈子、而是直接在本机上进行处理,以提速数据流的处理。这种做法中,同处理单元绑定的数据存储分区,我们称之为「局部状态」(local state)。

局部状态

满足如下条件的,都可以称之为局部状态。

  • 同处理单元位于同一台机器上。

  • 处理单元可根据输入数据流,查询/修改其中的状态数据。

  • 存储于内存或者磁盘中。

在阐述局部状态的好处前,我们先尝试回答一个显而易见的问题:局部状态如何做到高可用,如果机器挂了怎么办?

容错机制

Samza 对于局部状态提供的容错机制是,将局部状态的变更(local state changes)建模成一种提交日志/变更日志(commit/change log),从而利用提交日志的可重放(replay)特性来保障容错。

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

处理单元执行变更状态时,将变更日志写入 Kafka topic 中。如果机器挂掉,那么新启动的处理单元从上述 Kafka topic 中回放变更日志,从而重建机器挂掉前的局部状态。利用 Kafka 周期性的日志压缩(log compaction)操作,能够将日志量控制在合理的大小、而不会随着时间日益