【问题标题】:MongoDB custom and unique IDsMongoDB 自定义和唯一 ID
【发布时间】:2011-06-06 17:25:11
【问题描述】:

我正在使用 MongoDB,我想为博客文章生成唯一且神秘的 ID(将用于静态 URL),例如 s52ruf6wstxR2ru286zjI .

您认为生成这些 ID 的最佳和更具可扩展性的方式是什么?

我正在考虑以下架构:

  • 定期(每天?)批处理运行以生成大量随机和唯一 ID,并使用 InsertIfNotPresent 将它们插入到专用 MongoDB 集合中
  • 每次我想生成新的博客文章时,我都会从该集合中获取一个 ID,并使用 UpdateIfCurrent 原子操作将其标记为“已获取”

WDYT ?

【问题讨论】:

    标签: mongodb


    【解决方案1】:

    这正是 MongoDB 的开发人员按照他们的方式构建 ObjectID(_id)的原因...跨节点扩展等等。

    BSON ObjectID 是一个 12 字节的值 由一个 4 字节的时间戳组成 (自纪元以来的秒数),一个 3 字节 机器 ID,一个 2 字节的进程 ID,和一个 3 字节计数器。请注意, 时间戳和计数器字段必须是 与其他存储大端不同 布森。这是因为他们是 逐字节比较,我们想要 确保大部分是递增的顺序。 这是架构:

    0123   456      78    91011
    time   machine  pid   inc
    

    传统数据库经常使用 单调递增序列 主键的数字。在 MongoDB 中, 首选的方法是使用 而是对象 ID。对象 ID 是 更协同分片和 分布。

    http://www.mongodb.org/display/DOCS/Object+IDs

    所以我想说只使用 ObjectID 的

    它们在转换为字符串时并没有那么糟糕(它们是一个接一个地插入的)...

    例如:

    4d128b6ea794fc13a8000001
    4d128e88a794fc13a8000002
    

    它们看起来乍一看是“可以猜到的”,但它们真的没那么容易猜到......

    4d128 b6e a794fc13a8000001
    4d128 e88 a794fc13a8000002
    

    对于博客,我认为这没什么大不了的……我们在整​​个生产环境中都使用它。

    【讨论】:

    • 你也可以base64编码,大约有16个字符。或者 base85 大约有 12 个。
    • ObjectIds 是否“容易”猜测完全取决于谁在猜测。对于普通的网络浏览器来说,不,但对于任何称职的黑客来说,这些都是微不足道的。如果您需要安全的 id,请不要使用 ObjectIds、basewhatever-encoded 或其他方式 - 使用真正随机的东西,例如安全生成的 UUID。
    • 如果 OP 中提到的 RESTful URL 是公开的,那么正如 Leopd 所强调的,不要使用 ObjectID。您永远不想将您的数据库密钥暴露给世界。
    • @Darkean:安全最佳实践。不要告诉世界您的数据库密钥。
    • @Mark,我的错,我不知道这是最佳实践。现在这一切对我来说都非常清楚,谢谢。我会赞成你明智的回答,但是,你知道,讽刺。
    【解决方案2】:

    如何使用 UUID?

    http://www.famkruithof.net/uuid/uuidgen 为例。

    【讨论】:

    • 您的回答非常缺乏细节,更适合作为评论。
    【解决方案3】:

    创建一个返回全球唯一 ID 的 Web 服务,这样您就可以让许多 Web 服务器参与并知道您不会遇到任何重复项?

    如果您的每日批次没有分配足够的项目?你中午跑吗?

    我会将 Web 服务客户端实现为一个队列,可以由本地进程查看并根据需要重新填充(当服务器速度较慢时),并且可以在队列中保留足够的项目而不需要在高峰使用期间运行。有意义吗?

    【讨论】:

    • 这确实有点道理,但我很确定这是明确的答案 :)如果我想确保 ID 唯一性,则必须运行此 Web 服务的一个实例,使其成为真正的SPOF(如果关闭,则无法创建更多博客文章)。
    • 嗯,是的,您应该只运行一个服务实例。如果有很多,你可以有一个系统前缀,或者你可以让不同的系统相互了解并以一种有效的方式进行分配。
    【解决方案4】:

    这是一个老问题,但对于任何可能正在寻找其他解决方案的人来说。

    一种方法是使用简单快速的替换密码。 (下面的代码是基于别人的代码——我忘记了我从哪里得到的,所以不能给予适当的信任。)

    class Array
      def shuffle_with_seed!(seed)
        prng = (seed.nil?) ? Random.new() : Random.new(seed)
        size = self.size
    
        while size > 1
          # random index
          a = prng.rand(size)
    
          # last index
          b = size - 1
    
          # switch last element with random element
          self[a], self[b] = self[b], self[a]
    
          # reduce size and do it again
          size = b;
        end
    
        self
      end
    
      def shuffle_with_seed(seed)
        self.dup.shuffle_with_seed!(seed)  
      end
    end
    
    class SubstitutionCipher
    
      def initialize(seed)
        normal = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + [' ']
        shuffled = normal.shuffle_with_seed(seed)
        @map = normal.zip(shuffled).inject(:encrypt => {} , :decrypt => {}) do |hash,(a,b)|
          hash[:encrypt][a] = b
          hash[:decrypt][b] = a
          hash
        end
      end
    
      def encrypt(str)
        str.split(//).map { |char| @map[:encrypt][char] || char }.join
      end
    
      def decrypt(str)
        str.split(//).map { |char| @map[:decrypt][char] || char }.join
      end
    
    end
    

    你可以这样使用它:

    MY_SECRET_SEED = 3429824
    
    cipher = SubstitutionCipher.new(MY_SECRET_SEED)
    
    id = hash["_id"].to_s
    encrypted_id = cipher.encrypt(id)
    decrypted_id = cipher.decrypt(encrypted_id)
    

    请注意,它只会加密 a-z、A-Z、0-9 和一个空格,而其他字符则保持不变。这对于 BSON id 来说已经足够了。

    【讨论】:

      【解决方案5】:

      “正确”的答案,恕我直言,这并不是一个很好的解决方案,它是生成一个随机 ID,然后检查数据库是否存在冲突。如果是碰撞,再做一次。重复直到找到未使用的匹配项。大多数情况下,第一个会起作用(假设您的生成过程足够随机)。

      需要注意的是,仅当您担心基于时间的 UUID 或基于计数器的 ID 的安全隐患时,才需要此过程。这些中的任何一个都会导致“可猜测性”,这在任何给定情况下可能是也可能不是问题。我认为基于时间或基于计数器的 ID 足以用于博客文章,但我不知道您的情况和推理的详细信息。

      【讨论】:

      • 这样的尝试/重试模式将无法实现 MongoDB 的大部分目的。 MongoDB的主要思想是让客户端生成ID,而ObjectID就是用来做这个的。由于 MongoDB 将是“最终一致的”,因此在分片 MongoDB 系统上,由于同步原因,冲突检查甚至可能失败。
      • 我同意这是一个不理想的解决方案,但是,发布 ObjectID 具有许多微妙的安全隐患。如果客户端应用程序构建得不好,它也可能存在非常微妙的问题,尽管这属于应用程序本身,这在某种程度上超出了这个特定问题的关注范围。是的,检查可能会失败。应该有一些东西可以使 ID 具有客户唯一性,但是,使用可以轻松反转的基于时间的 ID 的概念揭示了可能用于针对个人或组织的信息。
      • 随机 ID,正如您建议实现的那样,不能与 MongoDB 一起使用,这最终是一致的。所以它不会更安全,因为它会被打破。 ObjectID 的安全问题?它只是泄漏了时间创建...Not a security issue, IMHO - and I'm not the only one to say that。如果您需要 URI 更安全: 1. 使用私钥加密 ObjectID,然后将其序列化为 Base64 URI,会非常安全。 2. 使用具有真正随机 GUID/UUID 的单独 URI 字段作为辅助键 - 需要额外的存储空间。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 1970-01-01
      • 2018-02-04
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2019-06-06
      相关资源
      最近更新 更多