数据库与微服务的一致性

odi*_*ino 29 database soa microservices

在基于微服务的系统中实现数据库一致性的最佳方法是什么?

柏林GOTO,Martin Fowler谈论微服务,他提到的一个"规则"是保留"每服务"数据库,这意味着服务不能直接连接到另一个服务"拥有"的数据库.

这是超级优雅和优雅,但在实践中它变得有点棘手.假设您有一些服务:

  • 一个前端
  • 订单管理服务
  • 忠诚度计划服务

现在,客户在您的前端进行购买,这将调用订单管理服务,这将保存数据库中的所有内容 - 没问题.此时,还会拨打忠诚度计划服务,以便从您的帐户中记入/记入积分.

现在,当所有内容都在同一个DB/DB服务器上时,一切都变得简单,因为您可以在一个事务中运行所有内容:如果忠诚度计划服务无法写入数据库,我们可以回滚整个事情.

当我们在多个服务中执行数据库操作时,这是不可能的,因为我们不依赖于一个连接/利用运行单个事务.什么是保持事物一致并过上幸福生活的最佳模式?

我非常渴望听到你的建议!并提前致谢!

Udi*_*han 11

这是超级优雅和优雅,但在实践中它变得有点棘手

"在实践中"的含义是,您需要设计您的微服务,以便在遵循规则时满足必要的业务一致性:

该服务无法直接连接到另一个服务"拥有"的数据库.

换句话说 - 不要对他们的责任做任何假设,并根据需要改变边界,直到找到一种方法来完成这项工作.

现在,问你的问题:

什么是保持事物一致并过上幸福生活的最佳模式?

对于不需要立即一致性且更新忠诚度积分似乎属于该类别的事物,您可以使用可靠的发布/订阅模式从一个微服务调度事件以供其他人处理.可靠的一点是,您需要对事件处理内容进行良好的重试,回滚和幂等性(或事务性).

如果您在.NET上运行,那么支持这种可靠性的基础架构示例包括NServiceBusMassTransit.完全披露 - 我是NServiceBus的创始人.

更新:关于忠诚度积分问题的评论:"如果延迟处理余额更新,客户实际上可以订购的商品数量超过他们的积分".

许多人为了强烈的一致性而努力满足这些要求.问题在于,通常可以通过引入其他规则来处理这些情况,例如,如果用户最终通过负忠诚点通知他们.如果T在没有忠诚点被分类的情况下经过,则通知用户他们将根据某些转换率向他们收费M. 当客户使用积分购买东西时,该政策应该是客户可见的.

  • 非常好的写作!我们实际上使用你提到的东西(即幂等,异步) - 虽然我认为我有一个不那么简单的例子可能会刺激对话.假设一旦订单处理完毕,您必须更新库存服务的可用库存,您将如何处理?我假设订单管理系统需要将订单写入数据库,然后通过HTTP调用库存服务将产品从库存中取出 - 并说这需要超快/健壮(以便人们不要添加到购物车/购买实际缺货的产品). (2认同)

ick*_*fay 7

我通常不会处理微服务,这可能不是一个很好的做事方式,但这是一个想法:

为了重申这个问题,该系统由三个独立但相互沟通的部分组成:前端,订单管理后端和忠诚度计划后端.前端希望确保在订单管理后端和忠诚度计划后端中保存一些状态.

一种可能的解决方案是实现某种类型的两阶段提交:

  1. 首先,前端将记录放在自己的数据库中,包含所有数据.将此称为前端记录.
  2. 前端向订单管理后端询问事务ID,并将其传递给完成操作所需的任何数据.订单管理后端将此数据存储在暂存区域中,将新数据与新的事务ID相关联并将其返回到前端.
  3. 订单管理事务ID存储为前端记录的一部分.
  4. 前端向忠诚度计划后端询问交易ID,并将其传递给完成操作所需的任何数据.忠诚度计划后端将此数据存储在暂存区域中,将新数据库与新的事务ID相关联并将其返回到前端.
  5. 忠诚度计划交易ID存储为前端记录的一部分.
  6. 前端告诉订单管理后端完成与前端存储的事务ID相关联的事务.
  7. 前端告诉忠诚度计划后端完成与前端存储的事务ID相关联的事务.
  8. 前端删除其前端记录.

如果实现了这一点,那么更改不一定是原子的,但最终会保持一致.让我们想想它可能失败的地方:

  • 如果在第一步中失败,则不会更改任何数据.
  • 如果它在第二个,第三个,第四个或第五个失败时,当系统重新联机时,它可以扫描所有前端记录,查找没有相关事务ID(任何一种类型)的记录.如果遇到任何此类记录,它可以从步骤2开始重播.(如果步骤3或5中出现故障,后端会留下一些遗弃的记录,但它永远不会移出暂存区域,所以没关系.)
  • 如果在第六步,第七步或第八步失败,当系统重新联机时,它可以查找填写了两个事务ID的所有前端记录.然后可以查询后端以查看这些事务的状态 - 已提交或未提交.根据已提交的内容,可以从适当的步骤恢复.