天天看點

MongoDB 使用 ObjectId 代替時間

An ObjectId is a 12-byte unique identifier consisting of:

  • a 4-byte value representing the seconds since the Unix epoch,
  • a 3-byte machine identifier,
  • a 2-byte process id, and
  • a 3-byte counter, starting with a random value.

由于前 4 個 byte 表示時間,是以 ObjectId 可以反映時間。預設情況下,前 4 個 byte 表示的時間為檔案建立時伺服器的時間。

如果你使用 ObjectId 代替時間,那麼導入過去存在的資料時 ObjectId 可能發生沖突[1]。

針對同一時刻建立 ObjectId

根據過去的某一時刻建立 ObjectId 會重複,因為前 3 種 byte 都一樣,隻有最後 3 個 byte 不一樣。

對于 PyMongo Driver:

from bson import ObjectId

# timestamp
str(ObjectId())[:8]
'5b2b0778'
str(ObjectId())[:8]
'5b2b0779'

# machine id 和 process id
str(ObjectId())[8:18]
'0e118bd5c7'
str(ObjectId())[8:18]
'0e118bd5c7'

# counter
str(ObjectId())[18:]
'a5b135'
str(ObjectId())[18:]
'a5b136'
           

假如針對 timestamp

5b2b0778

生成訂單,由于是同一機器、程序,是以前 3 種 byte 都相同。而最後的 counter 是在程序啟動時初始化一個随機值,然後進行遞增。任意兩個 ObjectId 相同的機率為 1677 萬分之一:

# 3 byte 為 24 位
2 ** 24 / 10000
1677
           

但随着訂單的增多,沖突的機率會越來越大。

是以不能采用針對某一時刻建立 ObjectId 的方法。

分散建立

設定一個時間區間,然後将檔案分散到每一秒建立。如果時間區間足夠大,那麼效果就和正常建立 ObjectId 一樣。時間區間可以根據理想沖突率和檔案數倒推出來。

參考

  1. https://dzone.com/articles/using-and-abusing-mongodb