MySQL : splitting the processing of a particular table between different nodes

标签 mysql database algorithm


我有一个难题,也许你能帮帮我。

我有一个用作工作队列的表。记录已插入,需要处理。处理完一条记录后,它会从队列中删除。 有一些限制:

  • 在任何给定时间只有一个实体可以处理记录(“实体”,我的意思是:一个线程,或连接到同一数据库的单独计算机)
  • 实体有些动态。它们可能会改变(实体数量或特征)
  • 一个实体在 1 笔交易中处理一条记录
  • 处理必须并行进行(如果 entity1 选择 batch1,entity2 必须能够并行处理 batch2,而无需等待 entity1 完成处理)
  • 一旦某个实体选择了要处理的记录,则该记录所属的整个“批处理”记录不得被其他实体选择。当我说“批处理”时,我的意思是该表(逻辑上)组织如下:
    • 第 1 行(第 1 批)
    • 第 2 行(第 1 批)
    • 第 3 行(第 2 批)
    • 第 4 行(第 2 批)
    • 第 5 行(第 2 批)
    • ....等等。

假设 entity1 和 entity2 都想从表中选择一个处理切片。 如果 entity1 选择 row1,则 entity2 可以选择除 batch1 之外的任何其他内容(除 row1 和 row2 之外的任何其他内容)。

让我们抽象出处理部分,因为实际处理是什么并不重要。我很想知道如何仅使用 mysql 数据库来阻止实体相互冲突,同时保持处理的并行性。

从我的角度来看,我看到两个非常笼统的方向:

  1. 使用某种状态字段,指示特定实体已经挑选了一批,而这批必须从 future 的挑选中排除。这个想法的缺点是,如果选择批处理的实体崩溃,那么其他实体恢复处理就有点困难。
  2. 使用mysql锁,缺点是难以保证并行处理,而且不是顺序的。例如,我可以为实体 1 选择...进行更新。但是 entity2 不能执行相同的 select...for update,因为这将等待第一个实体完成处理,然后再获取它需要的批处理。

我很想知道:

  • 哪个方向的编码工作量最小
  • 这里还有我遗漏的任何其他说明吗(请记住,实体之间无法相互通信,除非通过数据库)
  • 是否有针对此类问题的标准模式
  • 如果你能给我指出一篇讨论此类问题的文章。
  • 解决这个问题最有效的方法是什么。

所以我这里的是,数据库必须在不同的实体之间拆分一个表,以便进行处理,并且想知道最好的方法来做到这一点。我几乎不认为我是第一个处理这个问题的人,并且想知道你的想法。 另外,请注意,可以通过相当简单的标准(例如,batchId)将记录分批分割

亲切的问候,
安德烈。

最佳答案

嗯,根据您的描述,我看不出数据库记录锁定如何满足您的要求。数据库锁说“如果这条记录不可用,请稍等”,而不是“如果这条记录不可用,请选择另一条”。

问题:当一个批处理被处理时,它就“完成”了吗?我的意思是,假设节点 A 开始处理批处理 1。然后节点 B 出现,不管你怎么做,发现批处理 1 正在工作,所以它从批处理 2 开始。然后节点 A 完成。然后节点 C 出现了。当前未处理第 1 批。节点 C 应该获得批处理 1 吗?或者第 1 批已经完成,第 2 批正在工作,所以我们必须继续第 3 批?如果批处理完成,那么任何数据库锁定显然都不会起作用,因为它不记得过去访问过记录,只记得现在发生的事情。

也许其他人有更聪明的解决方案,但我认为您将不得不使用状态标志来做到这一点。我会说:

创建一个单独的 Batch 表,每个批处理有一条记录。在指向 Batch 表的 Work-queue 表中放置一个外键。这样我们就可以保持数据标准化。

在批处理表中,添加一个状态标志,其中包含工作中和非工作的值;或未处理、在制品和已处理。 (取决于批处理是否一劳永逸地“完成”。)还创建一个“处理实体”字段,如果它正在工作,则标识正在处理该批处理的实体,否则为空。 (如果状态只有 in-work 和 not-in-work,则此字段可以兼作 in-work 标志:null=not in-work,not null=in-work。)

当实体开始处理批处理时,它会将处理实体字段设置为实体 ID。完成后,它将字段重置为空。当一个实体正在寻找要处理的批处理时,它会选择“where processing_entity is null”(当然还有任何其他条件)。

然后为了确保实体没有崩溃而留下标志设置,每次实体启动时,它都会检查是否有批处理记录表明它正在处理它。如果是这样,它一定是崩溃了,所以它会重置标志并进行任何清理工作。

这只有在实体集是固定的情况下才有效,例如如果实体是服务器,或者它是来自线程池的线程。如果它们是可以任意来来去去的线程,那将无法正常工作,因为您不能指望线程重新启动来表明它还有未完成的工作。

我使用的另一种方法是在批处理记录中放置一个时间戳,说明我们何时开始处理。然后我有另一个不时运行的进程,检查时间戳,并与一些合理的最大时间进行比较。比方说,如果我们知道该进程应该花费几秒钟,而我们看到一个时间戳为一小时前的进程,那么该进程要么终止要么挂起,我们应该适本地进行清理。这确实有一个问题,即它要求您能够说出最长时间是多少。

关于MySQL : splitting the processing of a particular table between different nodes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10488902/

相关文章:

PHP如何计算单元格数

MySQL死锁与存储过程生成UID

c# - 使用两个表生成 Crystal 报表

php - 运行多个查询,然后根据加权重要性显示它们,例如 Facebook Wall

sql - 即使在同一个 LUN 上也有多个数据/日志文件是一件好事吗?

arrays - 在优于 O(N*M) 的时间内找到数组中多个区间的最小元素

php - 为什么 MySQL INSERT 查询会出错?错误是什么意思?

MySQL查询恢复

algorithm - 多目标整数规划

string - 如何从一堆字符串中提取常用词