DynamoDB 中的事务表
Transactions {transaction_id, customer_id, statment_id, transaction_date, transaction_amount}
DynamoDB 中的语句表
Statements {statement_id, customer_id, start_time, end_time, statement_amount}
每天都会发生数百万笔交易。我正在考虑使用 Flink 使用 DynamoDB 流将交易金额聚合为报表金额。
在任何给定的时间点,我都需要知道属于报表的所有交易金额是否都已汇总。也就是说,显示报表金额是否过时。 从本质上讲,我说的是和解。如何在 Flink 中实现这一点?
使用类似KeyedProcessFunction
的东西来不断更新一些 Flink 状态很容易,该状态在摄取新事务时聚合每个statement_id
的statement_amount
。但据我了解,问题是如何知道聚合何时完成,或者换句话说,Flink 何时处理了给定statement_id
的所有事务。
流处理应用程序始终面临此问题。与批处理不同,批处理可以简单地处理所有数据,然后生成结果,而通过流处理,我们一次处理一条记录,不知道将来会发生什么,也不知道延迟多少。
这导致我们在延迟和完整性之间进行权衡。一般来说,人们总是可以等待更长的时间,看看有什么额外的数据到达,从而增加一个人根据(更多)完整信息产生结果的机会。水印是这种权衡的技术表现。任何使用事件时间的流式处理应用程序都必须生成水印,每个水印都使用时间戳标记流中的一个点,并声明流在该点可能完成到该时间戳。
对于某些应用程序,快速生成可能正确的结果是可以的,事实上,可能比等待更长的时间才能产生更有可能正确的结果要好。但在其他应用程序中,必须完全准确(无论这可能意味着什么,精确)。
确切地说,您应该做的不是技术问题,而是业务流程问题。最终,这取决于对账的声明对您的业务意味着什么。也许您应该以重现当前存在的任何过程的语义为目标。
话虽如此,Flink 提供了一组工具,你可以组合起来以多种方式解决这个用例,这取决于你希望它如何工作的细节。以下是这些部分如何组合在一起:
每个语句都有一个end_time
。当事务流的水印达到该end_time
时,这是人们可能认为该语句的交易聚合已完成的第一个时刻。
这种水印(通常)将基于指定交易流可以无序的数量的界限来完成。但你要预料到,不管你多么悲观,一些异常的交易都会违反这个假设,相对于水印来说,是迟到的。
为了适应这种情况,您可以增加水印延迟以尝试涵盖所有可以想象的延迟(人们可能会认为这通常是不可能的),或者决定在某些时候您必须继续生成一个声明,声称已协调,但实际上可能需要将来更新或修改。这种任意延迟的问题是否是一个真正的问题(就像在银行业一样,一些国际交易可能会经历很长时间的延迟),还是仅仅是理论上的问题,这取决于你的实际用例。
为了能够适应延迟事务,需要您 (1) 将语句数据保留在 Flink 的托管状态中,以便添加延迟事务,然后可用于更新语句,或者 (2) 以特殊方式处理延迟事件,通过从 DB 读取先前生成的结果,然后在数据库中更新该记录(这需要以事务方式完成)。方法 #2 可以在单独的作业中实现,该作业使用第一个作业生成的延迟事务流。
您可以通过在语句上包含一个时间戳来定义解决此问题的方法,该时间戳指定该语句精确地包含截至该时间点已处理的那些事务。