摘要:本文整理自微财资深数据开发工程师穆建魁在 Flink Forward Asia 2024 论坛中的分享。主要分为以下三个部分:
-
微财科技基于 Flink 构建时变量池分享
-
选择 Flink 进行流式计算的架构选型和开发效率提升策略
-
实时变量池架构与多流关联优化实践
01.微财科技基于 Flink 构建时变量池分享
![]()
本次分享的的主题是微财基于 Flink 构造实时变量池。首先,我简单的介绍一下我们的公司。微财科技是一家专注于互联网金融的公司,其核心业务是通过 APP为用户提供借款服务。当用户下载登录 APP 后申请借款时,系统会根据一套复杂的风险评估机制来决定是否批准该申请。这套风险评估机制主要依赖于两个关键组成部分:模型与策略。其中,变量作为这些模型和策略的重要输入数据,对于确保风险评估的准确性至关重要,从而直接影响到用户的借款申请能否获得批准。
![]()
什么是变量呢?简而言之,变量就是描述用户行为或属性的数据。例如,用户的年龄、性别以及收入水平等。在变量的众多分类中,有一类被称为实时变量,它指的是通过实时数据计算得出的变量。
![]()
为什么需要实时变量呢?或者在哪些场景下会需要实时变量呢?这里将通过公司两个简单的场景来为大家举例说明。首先,考虑一个 T0 的新用户,即一个在当天注册并完成进件流程的用户,他此时没有历史数据可供参考。因此,当这位用户发起借款请求时,我们需要对其进行风险评估,而这时只能依赖他的实时数据来进行评估。
在另外一个场景下,即老用户的 T0 变异情况。如果仅依赖用户 T-1的数据来对其进行风险评估,那么评估结果很可能是错误的,或者会导致误放的情况。在这种场景下,这样的评估结果对公司来说是无法接受的。因为这样的错误评估将直接导致公司的现金损失。
如何产出实时变量呢?或公司原先的计算方案,以及业内普遍采用的解决方案是什么?答案是通过即时计算,即请求来一条处理一条。每当有用户需要进行风险评估时,我们就会从数据库中提取与其相关的数据。获取到这些数据后,在代码层面进行加工和计算,最终将计算出的变量提供给风险评估系统,以便其进行风险评估。
![]()
这个方案存在以下几个痛点。首先,其 QPS 上限不高。随着用户量和业务量的增长, QPS 的压力会反向传导至前端的数据库组件,如 MySQL 和 MongoDB 。为了加速查询,就只能在原有的数据库组件上添加索引,而且这些索引只能添加在存库上,因为在主库上添加会影响线上业务的正常运行。然而,添加索引并非短时间内可以完成的任务。此外当存库发生故障需要新建时,代价非常高昂,会直接影响线上服务的SLA。在这种场景下,实时变量计算与数据库组件的耦合度非常高。
![]()
为了解决上述痛点,将决定采用 Flink 流式计算方案。在数据同步阶段,首先利用 Flink CDC 将数据采集到 ODS 层。随后,通过流式数据驱动下游的 Flink 任务来生成变量,并将这些最终变量写入一个 OLAP 引擎中。这样一来, QPS 的压力就主要集中在 OLAP 引擎上了。同时,由于采用了 Flink CDC 进行数据同步,因此不再依赖于原有的数据库索引。值得一提的是,自今年年初完成上云后, CDC 已经支持 GTID 同步。一旦数据库发生故障,便可以在云端迅速启动一个新实例,并从之前的 binlog 同步位置继续数据同步。这样我们就能完成和业务组件的解耦,显著提升整体变量 SLA 的稳定性。
02.选择 Flink 进行流式计算的架构选型和开发效率提升策略
![]()
在选定 Flink 作为公司的流式计算引擎之后,面临的首要问题是架构选型,这是一个需要仔细考量并编制具体场景的问题。 Lambda 和 Kappa 这两种架构各有其独特的优势和适用场景。当时选择 Kappa 架构而没有选择 Lambda 架构的关键原因在于, Lambda 架构本质上仍是一个离线加实时的解决方案。由于变量要求具有高度的准确性,即需要达到百分百精确一致,而 Lambda 架构无法有效解决这个问题。此外,如果采用 Lambda 架构,对于同一个变量,需要同时开发离线和实时两套系统,这无疑会降低开发效率。相比之下, Kappa 只需开发一套计算逻辑,因此相较于 Lambda 架构开发效率会有所提升。并且,随着 Flink 的快速迭代,可以利用 Flink 的 Exactly-Once 语义来严格保证变量的一致性。
![]()
在选择 Kappa 之后,又遇到了新的挑战,即开发效率的问题。这里所说的开发效率慢,并非指 Flink 本身运行缓慢,而是指相较于之前的批处理计算方式,采用 Kappa 架构后,开发流程受到了较大的影响。有人可能会说 Flink 的性能并不差,但问题在于 Flink 无法满足当前业务的特定需求。主要问题包括:一是快速迭代的风险变量更新过程中,使用Flink SQL无法有效从现有状态恢复;二是处理长时间跨度(如半年到一年)的用户行为数据时,多流关联操作容易导致状态膨胀。此外,虽然转向DataStream API可以解决部分问题,但这也增加了学习成本,尤其是对于习惯于Java开发的团队来说,需要额外掌握Flink的各种算子及其状态管理机制。该如何解决这个问题呢?是否存在一种方案,既能利用 Flink SQL 的快速开发能力,又能避免直接操作细粒度 state 所带来的问题呢?
![]()
解决方案是实施数据分层,因为数据分层对于数据开发人员来说通常比较熟悉。在变量计算层面,主要分为两层:变量原子层和完整变量计算层。在变量原子层,完全采用 DataStream API 的方式对数据进行清洗、加工,以及多流关联和数据打宽等操作。同时,针对不同的数据源严格控制其生命周期,以避免state 无限制地膨胀。在加工完变量原子层后,在上层进行变量计算时,便可以专注于变量的加工逻辑本身。这意味着即使需求快速迭代,也能利用 Flink SQL 快速完成变量的加工,迅速适应需求的变化。采用这种变量分层策略后,开发效率相较于以前的即使变量计算,我们的整体开发效率大约提升了 30% 。
03.实时变量池架构与多流关联优化实践
![]()
在提到使用 DataStream API 构建原子层时也涉及了多流关联的问题。这确实是实时开发中一个难以避免的挑战。多流关联的主要难题在于, Flink 仅提供了 connect API ,若要进行多流关联,可能会导致状态冗余。此外,使用 connect API 会使代码变得复杂且冗余,增加了维护难度。在优化多流关联的场景中经历了长时间的探索,并尝试了许多方法。最终通过使用 Union 加 keyBy 的方式,将多个流合并后,再进行状态管理,从而解决了大状态的问题。同时,由于我们在原子变量层严格控制了不同数据源的生命周期,帮助我们避免了大状态问题的出现。
![]()
这就是完整的变量池架构,在实时变量池完成变量的加工后,所有的变量都被存储到了 Doris 中。而选择 Doris 的原因在于,一些变量场景需要进行观察点的计算,比如计算用户从注册至今的天数。因此,在选择 OLAP 引擎时,除了要考虑其高并发点查能力外,还希望该引擎具备一定的 SQL 查询能力。为此在 OLAP 引擎外部还封装了一层查询接口,用于处理线上的实时查询请求,并将这些实时查询日志记录到Paimon中。
![]()
由于我们公司本身就是一家互联网金融公司,因此对数据的时效性和线上数据的质量要求都非常严格。在将线上查询日志记录到Paimon之后,通过离线任务设置了按小时级的定时调度。这一调度主要对线上变量调用的结果进行实时的质量监控。这里主要关注四个重要指标: PSI 、缺失率、均值以及方差。针对每个变量都设定了相应的告警阈值。一旦触发告警就能实时地通知相关人员。以下图表展示了我们线上的实时质量监控结果。
![]()
在这套实时变量架构落地之后,今年除了风险场景外,已经成功将应用场景极大地扩展到了公司的其他业务领域。目前,营销市场、客服部门,甚至财务部门都在使用这套实时变量来辅助业务决策。
![]()
在未来展望方面,目前使用的是自建的 Doris 作为线上的 OLAP 引擎。同时,我们也在积极接触阿里云上的云原生产品,如 StarRocks 和 SelectDB 。目前正在对这两个产品进行深度的测试,并期望将来 StarRocks 或 SelectDB 能够替代自建的 Doris ,以确保线上服务的稳定性。