【问题标题】:Syncing multiple mongoDBs and avoid duplicate _ids同步多个 mongoDB 并避免重复的 _id
【发布时间】:2015-12-08 05:03:40
【问题描述】:

问题

我有一个主寄存器和 x 个子寄存器,它们将数据流式传输到主寄存器。我在夜间将这些寄存器与主寄存器同步。所以白天会在x1,x2,...xn中创建数据,晚上所有数据都会发送到主寄存器。

这不太可能,但例如x1x2 可能生成相同的_id。现在,如果我同步它们,第一个寄存器将创建文档,第二个寄存器将 upsert(并因此覆盖)我的文档。

解决方案

当前

为了防止我目前将原始 _id 保存在字段 refId 下,这非常糟糕,因为我无法使用主寄存器上的所有引用和大部分函数。

选项 I - 自动增量

我计划切换到自动增量_ids,但我读到如果你打算扩展,这是你不应该做的事情:

https://blog.serverdensity.com/switching-to-mongodb-and-auto-increment/ https://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/

选项二——修改子寄存器_id

我已经保存了我的文档来自哪个子寄存器。因此我可以检查_id 是否存在但有另一个子寄存器。如果是这样,我可以为新文档生成一个新的_id,并将新的_id 还给我的子寄存器。乍一看这听起来很简单,但我的文档中确实有很多参考资料。

问题)

有什么好的解决方案来处理这个可能存在重复 ID 的问题?有没有更简单但有效的方法来解决这个问题?例如。自动为 24 位 ObjectId 添加前缀。

【问题讨论】:

标签: mongodb mongoose


【解决方案1】:

As it is unlikely(尽管并非不可能)在不同的子寄存器中生成了两个相同的 ObjectId,您可以在错误处理程序中处理重复的 ObjectId。如果存在唯一密钥违规并且源寄存器存在差异,您可以生成新的 ObjectId。由于错误处理程序不会被频繁调用,您可以在不影响整体性能的情况下添加更复杂的逻辑。

例如,您可以通过克隆源记录并删除原始记录来生成新的 ObjectId,或者您可以创建自己的 ObjectId 并将 machineId 替换为 registerId。

【讨论】:

  • 好吧,这将是选项 II。如果对象相同,我会更改原始_id?或者更容易发回一些错误消息,而不是保存并实现逻辑。我不知道 Id 生成的这么聪明。
  • 可以,最简单的解决冲突的方法,就是克隆源记录,删除原来的(不能更新ObjectId)。
  • 如果我有这两个_ids:555d8f f842 e7470b 447d8028 & 555d8f f842 e7470b 447d8029 我可以说447d8029 是时间戳,e7470b 是机器,f842 是进程和555d8f 是计数器。 docs.mongodb.org/manual/reference/object-id/…,计数器到底是什么时候修改的?两个条目似乎相等。
  • 有没有办法一次遍历所有集合并更新对该 _id 的每个引用?或者我是否必须搜索每个集合并使用例如修改它findAndUpdate。因为我有 6 个集合,它们都相互引用。所以我必须为每个集合中的每个参考都这样做。
  • 您将搜索每个集合并更新 id。它很慢,但不应该经常调用。
【解决方案2】:

最终的解决方案是创建一个自定义的 mongoose ObjectId。正如本文https://docs.mongodb.org/manual/reference/object-id/#ObjectIDs-BSONObjectIDSpecification 中提到的,猫鼬_id 由几个部分组成,即使它们是在不同的机器上生成的,两个_id 碰撞也不太可能发生。为了更好地控制这个过程,我控制了_id 中的 processId 部分。每个子寄存器都有自己的 processID。

为了实现这一点,我生成了两个函数:

1) 检查_id 的覆盖值是否可用、有效以及寄存器是否为本地寄存器(无需对主寄存器进行修改)。条目仅在我的本地寄存器中生成。

2) 修改_id

mongoose = require('mongoose')
ObjectId = mongoose.Types.ObjectId
registerType = require('./register-config.js').getType()#function gives back local/national

exports.useProcessId = ()->
  return process.env.REGISTER_PROCESS_ID? && process.env.REGISTER_PROCESS_ID.length == 4 && registerType == 'local'

exports.changeMongooseId = (data, next) ->
  id = data._id.toString()
  data._id = new ObjectId(id.slice(0,6) + process.env.REGISTER_PROCESS_ID + id.slice(10,24))
  return next()

在我将新文档保存到集合之前调用此函数:

instituteSchema.pre('save', (next) ->
  data = @
  async.parallel
    eprd: (next)->
      validateEPRD(data, next)
    changeMongooseId: (next)->
      if useProcessId then processIdConfig.changeMongooseId(data, next) else return next()
    (err)->
      return next new Error(err) if err?
      return next()
)

我不需要在更新等时调用该函数,因为此时它已经具有唯一的_id

【讨论】:

    猜你喜欢
    • 2020-09-14
    • 2012-08-24
    • 1970-01-01
    • 2019-10-06
    • 2018-12-19
    • 1970-01-01
    • 1970-01-01
    • 2018-09-04
    相关资源
    最近更新 更多