ObjectId是"_id"的默認類型。它設計成輕量型的,不同的機器都能用全局唯一的同種方法方便地生成它。
這是MongoDB采用ObjectId,而不是其他比較常規的做法(比如自動增加的主鍵)的主要原因,因為在多個
服務器上同步自動增加主鍵值既費力還費時。MongoDB從一開始就設計用來作為分布式數據庫,處理多個節
點是一個核心要求。后面會講到ObjectId類型在分片環境中容易生成得多。
ObjectId使用12字節的存儲空間,每個字節兩位十六進制數字,是一個24位的字符串。由於看起來很長,不
少人會覺得難以處理。但關鍵是要知道這個長長的ObjectId是實際存儲數據的兩倍長。
如果快速連續創建多個ObjectId,會發現每次只有最后幾位數字有變化。另外中間的幾位數字也會變化(要
是在創建的過程中停頓幾秒鍾)。這是ObjectId的創建方式導致的。12字節按照如下方式生成:
0|1|2|3 | 4|5|6 | 7|8 | 9|10|11
時間戳 | 機器 | PID | 計數器
前4字節是從標准紀元開始的時間戳,單位為秒。這會帶來一些有用的屬性。
時間戳,與隨后的5個字節組合起來,提供了秒級別的唯一性。
由於時間戳在前,這意味着ObjectId大致會按照插入的順序排列。這對於某些方面很有用,如將其作為索引提
高效率,但是這個是沒有保證的,僅僅是"大致"。這4個字節也隱含了文檔創建的時間。絕大多數驅動都會公開
一個方法從ObjectId獲取這個信息。
因為使用的是當前時間,很多用戶擔心要對服務器進行時間同步,其實這個沒有必要,因為時間戳的實際值並不
重要,只要其總是不停增加就好了(每秒一次)。
接下來的三個字節是所在主機的唯一標識符。通常是機器主機名的散列值。這樣就可以確保不同主機生成不同的
ObjectId,不產生沖突。
為了確保在同一台機器上並發的多個進程產生的ObjectId是唯一的。后3個字節就是一個自動增加的計數器,確
保相同進程同一秒產生的ObjectId也是不一樣的。同一秒鍾最多允許每個進程擁有256(16777216)個不同的ObjectId。
2.自動生成_id
前面講到,如果插入文檔的時候沒有"_id"鍵,系統會幫你自動創建一個。可以由MongoDB服務器來做這件事情,但
通常會在客戶端由驅動程序完成。理由如下:
雖然ObjectId設計成輕量型的,易於生成,但是畢竟生成的時候還是產生開銷。在客戶端生成體現了MongoDB的設計
理念:能從服務器端轉移到驅動程序來做的事,就盡量轉移。這種理念背后的原因是,即便是像MongoDB這樣的可擴
展數據庫,擴展應用層也要比擴展數據庫層容易的多。將事務交由客戶端來處理,就減輕了數據庫擴展的負擔。
在客戶端生成ObjectId,驅動程序能夠提供更加豐富的API。例如,驅動程序可以有自己的insert方法,可以返回生
成的ObjectId,也可以直接將其插入文檔。如果驅動程序允許服務器生成ObjectId,那么將需要單獨的查詢,以確
定插入的文檔中的"_id"值。