0

我有处理 JSON 对象的协作 Web 应用程序,如下所示:

var post = {
  id: 123,
  title: 'Sterling Archer',    
  comments: [
    {text: 'Comment text', tags: ['tag1', 'tag2', 'tag3']},
    {text: 'Comment test', tags: ['tag2', 'tag5']}
  ]  
};

我的方法是使用带有jsonpatch库的rfc6902 (JSONPatch) 规范来修补 JSON 文档。所有此类文档都存储在 MongoDB 数据库中,如您所知,最后一个文档对于频繁写入非常慢。

为了获得更快的速度和高负载的应用程序,我使用 redis 作为队列来执行补丁操作,如下所示:

{ "op": "add", "path": "/comments/2", "value":  {text: 'Comment test3', tags: ['tag4']}" }

我只是将所有此类补丁操作存储在队列中,并在午夜运行 cron 脚本以获取所有补丁并构建完整文档并在 MongoDB 数据库中更新它。

我还不明白如果补丁损坏,我应该怎么做:

{ "op": "add", "path": "/comments/0/tags/5", "value": 'tag4'}

上面的补丁不适用于上面的文档,因为tags数组的长度只有 3(根据官方文档https://www.rfc-editor.org/rfc/rfc6902#page-5

 The specified index MUST NOT be greater than the number of elements in the array.

因此,当用户在线时,他不会收到任何错误,因为他的补丁操作存储在 redis 队列中,但第二天由于未在 cron 脚本中应用的损坏的补丁,他得到了损坏的文档。

所以我的问题是如何保证存储在 redis 队列中的所有补丁都是正确的并且不会破坏主文档?

4

2 回答 2

3

与任何可能变得不一致的系统一样,如果您希望尽快发现冲突并降低遇到冲突的可能性,则必须允许尽快应用补丁。如果您没有尽快通知其他客户端任何更新的数据(并且只是等待 CRON 运行以更新其他客户端可以访问的共享数据),那么这可能是您的主要问题。

正如其他人所问的那样,首先要了解“坏”补丁是如何进入操作队列的,这一点很重要。以下是我的一些猜测:

  1. 用户应用了一些在翻译中丢失的操作。如何?我不知道,但它可以解释这种差异。
  2. 未按正确顺序应用操作。如何?我不知道。我没有代码可以关闭。

虽然我没有代码可以关闭,但我可以在黑暗中拍摄并帮助您分析后一点。我们需要分析的第一件事是更新“共享”资源可能出现的不同场景。重要的是要注意,在任何最终必须保持一致的系统中,我们关心的是:

  1. 操作顺序。
  2. 我们将如何处理冲突。

后者真的取决于你,你需要一个好的通知/消息系统来更新客户看到的“真相”。

方案 1

用户 A 应用操作 1 和 2。文档在服务器上更新,然后通知用户 B。用户 B 将应用操作 3 和 4,但这些操作(按此顺序)与操作 1 和 2 不冲突。一切都很好。这是一个很好的情况。

方案 2

用户 A 应用操作 1 和 2。用户 B 应用操作 3 和 4。

如果您以原子方式为每个用户应用操作,则可以获得以下队列:

[1,2,3,4] [3,4,1,2]

沿线的任何地方,如果存在冲突,您必须根据“谁先到达那里”(或您希望使用的任何其他加权语义)通知用户 A 或用户 B。同样,如何处理冲突取决于您。如果您还没有阅读矢量时钟,您应该这样做。

如果您不对每个用户自动应用操作,则可以获得以下队列:

[1,2,3,4] [3,4,1,2] [1,3,2,4] [3,1,4,2] [3,1,2,4] [1,3, 4,2]

如您所见,放弃每个用户的原子更新会增加更新的组合,因此会增加发生冲突的可能性。我敦促您确保将每个用户原子地添加到队列中。

回顾

您应该记住的一些重要事项:

  1. 确保对每个用户自动应用对队列的更新。
  2. 弄清楚您将如何处理由来自不同客户端的多个突变引起的共享资源的多个版本(我再次建议您阅读矢量时钟)。
  3. 不要将可能被多个客户端实时访问的共享资源更新为 cron 作业。
  4. 当发生无法解决的冲突时,请弄清楚您将如何处理它。
  5. 作为第 3 点的结果,您将需要提出一个通知系统,以便客户可以快速获得更新的资源。作为第 4 点的结果,您可以选择包括告诉客户他们的更新出了点问题。我刚刚想到的是,您已经在使用 Redis,它具有发布/订阅功能。

编辑

Google Docs 似乎通过转换处理冲突解决方案。也就是说,通过移动整个字符/行来为所有操作的混合应用程序让路:https ://drive.googleblog.com/2010/09/whats-different-about-new-google-docs_22.html

正如我之前所说,这完全取决于您要如何处理自己的冲突,这在很大程度上应由应用程序/产品本身及其用例决定。

于 2014-09-19T11:32:02.010 回答
2

恕我直言,您正在引入不必要的复杂性,而不是更简单的解决方案。这些将是我的替代建议,而不是您的 json 补丁 cron 方法,这种方法很难保持一致和原子。

  1. 仅使用 mongodb:通过适当的数据库设计和索引,以及适当的 hdarware 分配/分片,mongodb 的写入性能非常快。mongodb BSON 文档及其查询语言本身支持您在 jsonpatch 中使用的操作类型。例如 $push、$set、$inc、$pull 等。也许您不想通过同步写入 Mongodb 来中断用户活动,因为该解决方案使用第 2 点中提到的异步队列。

    2.使用任务队列 & mongodb:你可以将补丁任务推送到任务队列中,而不是像现在这样将补丁存储在redis中,任务队列将异步执行mongodb更新,用户不会遇到任何性能下降的问题。一个非常好的任务队列是Celery,它可以使用 Redis 作为代理和消息传递后端。因此,每个用户更新都会得到一个任务,并且会被任务队列应用到 mongodb,并且不会影响性能。

于 2014-09-20T10:35:51.283 回答