【问题标题】:How should i guarantee consistency in database involving finance transaction operations我应该如何保证涉及金融交易操作的数据库的一致性
【发布时间】:2019-10-08 11:28:29
【问题描述】:

我试图弄清楚如何处理数据库中的一致性。 在场景中:

  • 用户 A 在数据库中有一个会计凭证,其中包含一个余额字段,表示他当前的金额。 (假设最初他有 100 美元)

  • 我的系统有很多方法可以从他的账户中扣款。

  • 假设同时发生 2 个方法,每个方法向他收取 10 美元的费用,这些步骤按以下顺序同时发生:
    • 方法1读取他的余额并存储在内存中(100美元)
    • 方法 2 读取他的余额并存储在内存中 (100$) ...一些业务逻辑
    • 方法 1 通过将内存中的变量减去 10 (100$ - 10$) 来更新余额,然后保存
    • 方法 2 通过将内存中的变量减去 10 (100$ - 10$) 来更新余额,然后保存

这意味着他只被收取了 10 美元而不是 20 美元。

我搜索了一段时间,无法弄清楚(对不起我的愚蠢)。 真的很感谢你的帮助启发了我的小聪明。 :)

【问题讨论】:

  • 在我看来,问题主要源于业务层中应该在数据库级别完成的事情。与其记住原来的 100,减去 10,然后将 90 写入数据库,不如让 DBMS 减去 10:update account set balance = balance - 10 where account_id = 123;
  • 另一种选择是锁定帐户。如果您的进程可以锁定帐户,则继续,否则等待。所以只有一个进程看到原始的 100 并将其减少到 90,然后解锁帐户。另一个进程将看到 90 而不是 100,因为它必须等到该进程再次解锁。

标签: node.js database mongodb algorithm backend


【解决方案1】:

您刚刚发现金融交易为何如此复杂 :-)

您有没有想过为什么要花时间更新银行帐户中的余额?或者为什么你实际上有两个余额,而不是一个?

那是因为您的帐户实际上可能会变成负数,并且(在一定程度上)这会很好。

因此,在现实生活场景中,您的余额为 100 美元,您支付 10 美元,在该交易被收款人处理并确认之前,您仍然拥有 100 美元。如果您进行 20 笔每笔 10 美元的交易,您将能够完成它们,因为系统很可能无法注意到。 老实说,它不应该。想想信用卡,你现在可能没有足够的钱,但也许你知道在信用卡到期时你会有足够的钱。

因此,您描述的竞争条件只有在您实际读取该值然后更新它时才有效。

有几种方法:

  • 读取当前余额,并使用旧余额作为 where 语句中的字段更新行。这样,如果它没有更新您知道需要重新读取和更新的行。
  • 不要更新余额,只根据时间更新,比如每小时一次。是的,您可能仍需要进行一些检查,但系统总体上会响应更快。
  • 第一步是锁定数据库行。这可行,但有可能会使应用变慢。

【讨论】:

    【解决方案2】:

    您描述的竞争条件是低级设计问题。使用像 Node 这样的后端引擎,它将以先到先服务的方式处理传入请求,您无需考虑这种情况。如果您尊重触发数据库更新回调的顺序,则您描述的竞争条件是不可能的。它们按照它们发出的相同顺序被触发。所以你应该只在前一个更新完成时调用下一个更新。 Promises 是实现这一点的好方法。

    【讨论】:

    • 我的意思是,如果我的后端和数据库有超过 1 个 docker 实例,但有一个数据持久数据文件夹。可以将请求推送到队列中。由于节点或其他一些语言具有其非异步特性。
    • 您可能应该将关注范围向上转移到负载均衡器。它将确保仅在一个实例上处理请求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 2010-12-21
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 2011-01-09
    • 1970-01-01
    相关资源
    最近更新 更多