【问题标题】:MongoDB Concurrency IssueMongoDB并发问题
【发布时间】:2015-12-18 03:44:11
【问题描述】:

我的应用有一个场景,类似于在 Facebook 中发送好友请求。

当用户 A 向用户 B 发送好友请求时,内部会创建一个新的好友请求文档。稍后当用户B也想向A发送好友请求时,系统会发现存在好友请求文档,因此他们应该是彼此的好友,不会创建新的好友请求文档。

我试图找出用户 A 和用户 B 同时向对方发送好友请求的情况,这将创建 2 个好友请求文档并导致未确定的行为...

感谢您的建议.. 非常感谢!

编辑: 一些人建议使用请求队列来解决这个问题;然而, 我对使用队列感到困惑,因为我认为它会使我的 rest api 端点按顺序处理请求。通过使用队列,我不会失去多线程的所有好处吗?我不禁想象,如果我的服务仅仅因为这个问题而有数百万个请求排队等待一个一个执行,那将是多么糟糕。有人在生产中看到过类似问题吗?

【问题讨论】:

    标签: mongodb concurrency locking nosql


    【解决方案1】:

    我的客户端有类似的情况,在数据库中有并发写入,我实现的是队列服务。

    Create a request in the queue rather than writing in the database, a separate reader will 
    read one message from the queue at a time and check if it is valid to write it to 
    database, write only if there is no previous request.
    

    您可以实现自己的队列,也可以使用 AWS-SQS、rabbitmq、MSMQ 等服务。

    【讨论】:

    • 这是否意味着我的服务器将不再并行处理请求,这意味着它不会像以前那样高效?
    • 不,只有这个特定功能的写操作会经过一个队列。如果需要,您可以使用并行阅读器
    • 是的,但是如果有大量写入操作排队,响应最终会不会很慢甚至超时?我的意思是在这种罕见的情况下权衡性能是否值得?我希望有其他方法可以解决这种非常罕见的情况.. :(
    • 是的,这似乎是一个极端情况,但我怀疑响应缓慢。不确定是否有其他方法,如果您以其他方式解决问题,请分享您的实现:)
    • @user1955934 不需要多队列,单队列就可以解决问题
    【解决方案2】:

    // 具体到你的情况

    1. 在 mongodb 中,对单个文档的写入操作是原子的。
    2. mongodb具有唯一索引的特点。

    因此,如果您在插入文档之前插入带有人名 A 和 B 的 _id(或任何其他唯一索引)的文档,方法是在插入之前为两者创建唯一索引(例如通过按字典顺序对名称进行排序的“A_B”)。您本质上只能插入该文档的一个实例。

    // 通用

    本质上我们想要的是事务,但由于 mongodb 不支持这样的,截至目前。有一些技巧可以实现这一点:

    1. 2 阶段提交: https://docs.mongodb.org/v3.0/tutorial/perform-two-phase-commits/

    2. 使用外部源维护标志,例如使用支持以事务方式插入/比较和交换的 memcache。

    【讨论】:

    • 好友请求有方向,即标记哪个用户发送请求以及它的目标用户。因此它不能是只能表示为 A_B 的文档,而是 A_B 和 B_A 。这不是事务/原子性问题,因为主要问题是根据可能意外更改的条件知道要创建什么文档;在这种情况下,条件是“是否存在好友请求?”,但检查完成后,此条件可能不再成立......
    • 为了处理方向,总是可以有另一个字段,即requestFrom,requestTo。唯一索引应该是一个完全不同的字段,用来标记 A 和 B 之间的关系(它是唯一的,并且在 A 和 B 之间只有一次)。
    • 添加到上面的评论,让我解释一下为什么我认为这是一个事务问题:正如你提到的,“主要问题是知道根据条件创建什么文档”,这是一个非常简单的问题这可以通过在您的代码中进行简单检查来解决,但是“检查完成后此条件可能不再成立”此条件将改变的原因是无法知道在读取时是否有另一个线程可能正在写作,如果你能以某种方式获得文档的读/写锁,这个问题就可以解决。这本质上是交易。
    • 说了这么多,我建议的解决方案不是基于事务,而是基于mongodb的一个属性(唯一索引),实际上更简单,不需要使用外部队列或外部标志。您只需处理以后提交时可能出现的“重复 id”异常。
    • 我的朋友请求对象中有 requestFrom 和 requestTo 字段。但这不能是唯一的,因为 requestFrom A 和 requestTo B 与 requestFrom B 和 requestTo A 不同。如果 reqFrom 和 reqTo 字段不同,我应该如何创建唯一索引字段?而且我认为这不是事务问题,因为根据定义,事务是我们应该以原子方式做事的方式,即。 2 次写入操作,如果一次写入失败,则两者都将失败。在这种情况下,我只是基于读取操作进行插入...
    【解决方案3】:

    如果您在前端使用 系统调用 方法,那么当某个用户喜欢时,您应该从数据库向前端发出一个请求,我向您发送请求,然后在 sec 数据库内向您发送一个系统调用,然后您的前端代码立即更正按钮文本,如

    “添加朋友”到“传入请求”

    否则。

    如果您只是设置数据库,则只需进行系统调用,当好友请求到达或您说创建文档时将其发送到 UI,进一步的过程将由 UI 开发人员处理。 谢谢你。 如果您不喜欢这个答案,那么我很抱歉,但请不要对我投反对票,因为我是 Stack Overflow 社区的新手。

    【讨论】:

      猜你喜欢
      • 2019-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-17
      • 2019-06-23
      • 1970-01-01
      相关资源
      最近更新 更多