2011-01-10 119 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來滿足博客文章的需求,儘管我不知道你的情況和推理的細節。