分布式分析系统数据一致性的体系结构设计

时间:2014-07-16 18:44:12

标签: sql-server architecture message-queue distributed distributed-caching

我正在重构一个将进行大量计算的分析系统,我需要一些关于可能的架构设计的想法,以解决我面临的数据一致性问题。

当前架构

我有一个基于队列的系统,其中不同的请求应用程序创建最终由工作人员使用的消息。

每个“请求应用”将大型计算细分为较小的部分,这些部分将发送到队列并由工作人员处理。

完成所有部分后,原始“请求应用”将合并结果。

此外, workers 使用来自集中式数据库(SQL Server)的信息来处理请求(重要:工作人员不会更改数据库上的任何数据,只会消耗它)。

Current Architecture

问题

确定。到现在为止还挺好。当我们包含更新数据库信息的Web服务时,就会出现问题。这可能在任何时候发生,但至关重要的是,源自相同“请求应用程序”的每个“大计算”在数据库上看到相同的数据。

例如:

  1. App A 生成消息A1和A2,将其发送到队列
  2. 工人 W1 选择消息A1进行处理。
  3. Web服务器更新数据库,从状态 S0 更改为 S1
  4. 工人 W2 选择消息A2进行处理
  5. 我不能让工人W2使用数据库的状态S1。为了使整个计算保持一致,它应该使用先前的S0状态。

    思想

    1. 锁定模式,以防止Web服务器在工作人员消耗数据时更改数据库。

      • 缺点:锁可能会长时间打开,因为不同的“请求应用”计算形式可能会重叠(A1,B1,A2,B2,C1,B3等)。
    2. 在数据库和worker(通过req.app控制数据库缓存的服务器)之间创建 new layer

      • 缺点:添加另一个层可能会带来很大的开销(可能?),这需要很多工作,因为我必须重写工作者的持久性(很多代码)。
    3. 我正在等待第二个解决方案,但对此并不十分有信心。

      有什么好主意吗?我设计错了,还是错过了什么?

      OBS:

      • 这是我们正在尝试的巨大的2层遗留系统(在C#中) 以最小的努力演变成更具可扩展性的解决方案 可能的。
      • 每个工作人员都可能在不同的服务器上运行。

3 个答案:

答案 0 :(得分:1)

您可以对数据库进行版本控制吗?

让我们说请求的应用程序用ct1标记计算的开始。现在,此计算生成的每条消息都标有相同的时间戳。

此外,每个数据库更新都会在更新时标记DB状态。因此状态S0在时间t0开启,状态S1在t1等。

现在,当工作人员收到消息时,需要获取数据库状态,其中更新时间最大值小于或等于消息时间。在您的示例中,如果A1和A2标有ct1,并且t1> ct1,两个工人都将检索S0而不是S1。

这当然意味着您需要在数据库中保留多个版本。如果您知道计算必须在一段时间之后完成,则可以在一定时间后清理这些版本。

答案 1 :(得分:1)

我喜欢选项2,特别是如果整套计算所需的数据量不合理地大。我假设有一种方法可以关联(通过id)属于同一个整体工作的计算?

当一组计算的第一条消息进入时,拾取它的工作人员会查询数据库以及执行所有计算所需的所有数据并创建临时数据存储。这个数据存储的外观取决于很多因素(大小,结构等),但它可能是一个blob /文档,一个关系模式中的一组数据(由correlationId隔离),一个条目企业缓存等

当Worker 1和Worker 2都处理同一组计算时,您需要小心这种情况,因为只有其中一个应该创建数据存储,并且两者都需要等到在继续之前,商店已完全填充。

答案 2 :(得分:0)

感谢大家的帮助。

由于我认为这个问题可能在其他情况下很常见,我想分享我们选择的解决方案。

更彻底地思考这个问题,我理解它的真实含义。

  • 我需要为每项工作提供某种会话控制
  • 进程中有一个缓存用作每个作业的会话控件

现在计算已经发展到分布式,我只需要改进我的缓存以便分发。

为了做到这一点,我们选择使用内存数据库(哈希值),部署为单独的服务器。 (在这种情况下Redis)。

现在,每当我开始工作时,我都会为作业创建一个ID并将其传递给他们的消息

当每个工作人员都需要数据库中的某些信息时,它会:

  1. 在Redis中查找数据(带有作业ID)
  2. 如果数据位于Redis中,请使用数据
  3. 如果不是,请从SQL加载,并将其保存在redis中(使用作业ID)。
  4. 在作业结束时,我清除与作业ID相关联的所有哈希值。