【问题标题】:Load balancing SQL reads while batch-processing?批处理时负载平衡 SQL 读取?
【发布时间】:2020-04-22 22:36:06
【问题描述】:
给定一个带有时间戳记录的 SQL 表。每隔一段时间,应用程序App0 就会执行类似foreach record in since(certainTimestamp) do process(record); commitOffset(record.timestamp) 的操作,即它会定期消耗一批“新鲜”数据,按顺序处理并在每条记录后提交成功,然后只是休眠一段合理的时间(以积累另一个批)。这适用于单个实例。但是如何对多个实例进行负载平衡?
在完全相同的环境中App0 和App1 同时竞争新鲜数据。这个想法是App0 执行的就绪查询不得与App1 执行的相同读取查询重叠 - 这样它们就不会尝试处理相同的项目。换句话说,我需要基于 SQL 的保证,即并发读取查询返回不同的数据。这甚至可能吗?
附: Postgres 是首选。
【问题讨论】:
标签:
sql
transactions
load-balancing
batch-processing
【解决方案1】:
在 App0 处理先前选择的记录时,App1 应该做什么的问题描述相当模糊。
在这个答案中,我做了以下假设:
- 所有应用程序都知道最后一个
certainTimestamp是什么,并且所有应用程序在启动数据库查询时都是一样的。
- 当 App0 正在处理时,比如说它开始工作时发现的 10 条记录,新记录进来了。这意味着,关于
certainTimestamp 的新记录堆增加了。
- 当App1(或任何进一步的App)启动时,应该只处理那些尚未被其他处理的关于
certainTimestamp的新记录>应用程序。
- 然而,如果 App 上发生故障/崩溃,则应在下次运行另一个 App 时选择未完成的记录。
这可以通过锁定许多 SQL 数据库中的记录来实现。
解决这个问题的一种方法是使用
SELECT ... FOR UPDATE SKIP LOCKED
该语句结合范围选择since(certainTimestamp) 选择并锁定所有符合条件且当前未锁定的记录。
每当一个新的 App 实例运行此查询时,它只会得到“剩下的”要做的事情并且可以处理它。
这解决了“叠加”或处理相同数据的问题。
剩下的就是certainTimestamp的定义和更新。
为了保持这个答案简短,我不会在这里讨论,只是将指针留给需要正确考虑的 OP,以避免出现例如由于某种原因无法处理的单个记录将certainTimestamp 保持在永久最小值。