2011-01-10 120 views
12

我使用MongoDB的,我想生成博客文章独特的cryptical标识(将在宁静的网址都必须使用),如s52ruf6wstxR2ru286zjIMongoDB的自定义和唯一ID

你认为最好的和更可扩展的方式来生成这些ID?

我想下面的架构:

  • 定期(每天?)批次运行时要产生大量的随机性和唯一身份标识,并与InsertIfNotPresent
  • ,每次将其插入专用的MongoDB集合我想生成一个新的博客帖子,我从此集合中获取一个ID并将其标记为“已采用”UpdateIfCurrent原子操作

WDYT?

回答

32

这就是为什么MongoDB中的开发人员构建自己的对象ID的(在_id)他们的方式... 规模跨节点

一个BSON对象ID是一个12字节的值包括4字节时间戳 (自历元起的秒数),3字节机器ID,2字节进程ID和3字节计数器组成的组。请注意, 时间戳和计数器字段必须是 存储的大端序列号,与其他的 BSON不同。这是因为他们是 逐字节比较,我们想要 确保大多数增加的顺序。 这里的模式:


time machine pid inc 

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

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

所以我说只是使用对象ID的

时转换为字符串,他们也不差(这些都是后对方右插)...

例如:

4d128b6ea794fc13a8000001 
4d128e88a794fc13a8000002 

他们看第一眼是“猜测的”,但他们真的不是那么容易猜...

4d128 b6e a794fc13a8000001 
4d128 e88 a794fc13a8000002 

而对于一个博客,我认为这不是什么大问题......我们在各地都使用它。

+6

你也可以base64编码,大约有16个字符。或base85有12左右。 – Tower 2012-04-19 06:17:25

+5

ObjectIds是否“容易”猜测取决于谁在猜测。对于一个偶然的网络浏览器来说,不,但对于任何值得他们加盐的黑客来说,这些都是微不足道的猜测。如果您需要安全的ID,请不要使用ObjectIds,basewhatever-encoded或其他方式 - 使用像安全生成的UUID一样真正随机的东西。 – Leopd 2013-04-09 16:07:41

1

制作一个Web服务,返回一个全球唯一的ID,以便您可以让许多Web服务器参与并知道您不会碰到任何重复项?

如果您的每日批次没有分配足够的物品?你中午吃了吗?

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

+0

它确实有道理,但我记得很确定这是一个明确的答案:)如果我想确保身份证唯一性,这个Web服务只有一个实例必须运行,使其成为一个真正的SPOF(不能创建任何更多的博客文章,如果下降)。 – Chris 2011-01-10 19:12:31

+0

嗯,是的,你应该只有一个服务运行的实例。如果很多,你可以有一个系统前缀,或者你可以让不同的系统知道对方,并以一种可行的方式进行分配。 – 2011-01-10 20:53:17

0

这是一个古老的问题,但任何人都可以寻找另一种解决方案。

一种方法是使用简单而快速的替换密码。 (下面的代码是基于别人的代码 - 我忘了,我把它从所以不能给予适当的信贷。)

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) 

请注意,这是只会加密az,AZ,0-9和一个空格,使其他字符保持不变。 BSON ID足够了。

-1

“正确”的答案,这不是一个很好的解决方案恕我直言,是生成一个随机ID,然后检查DB的碰撞。如果是碰撞,请重新执行。重复,直到找到未使用的匹配。大多数情况下,第一个将会工作(假设您的生成过程足够随机)。

需要注意的是,只有在您担心基于时间的UUID或基于计数器的ID的安全影响时,才需要执行此过程。其中任何一种都会导致“可猜测性”,这在任何情况下都可能成为问题,也可能不成问题。我会考虑一个基于时间或者基于计数器的ID来满足博客文章的需求,尽管我不知道你的情况和推理的细节。