としたにあんの左脳

備忘録です.

mongodbのObjectIdの生成規則

MongoDBのマイグレーションとかしてたら,MongoDBのObjectIdって本当にユニークだっけ?っていう疑問がわいた.

確かいろいろな要素を使って,ユニークになるようにできてたよなぁ...と思って,ちゃんと調べてみた.

とりあえず公式のObjectIdの説明.ObjectId - MongoDB Manual 2.4.9

  • 4バイトの,Unixエポックからの経過秒数(Unix時間)
  • 3バイトのマシンID
  • 2バイトのプロセスID
  • 3バイトのカウンタ(開始番号はランダム)

からできているらしい.

f:id:toshitanian:20140214011947p:plain

↑がんばって書いた.

実験!

同じマシンで生成してみる.

以下の2つのObjectIdは同じマシン上で生成したObjectId.

52fcf106 0af12b af9e 8d5bba
52fcf108 0af12b af9e 8d5bbb

確かに最初のUnix時間は2秒ずれていることがわかる.

マシンIDは同じ.

プロセスIDも同じ.

カウンタは1だけ上がっていることがわかる.

違うマシンで生成してみる.

今度は別のマシンで生成してみる.

確かにマシンIDプロセスIDが上のObjectIdとは違っている!

52fcebd1 9a5c4e a066 dbfa12

ちなみに,このObjectIdのUnix時間部分

52fcebd1(16進数) → 1392307153(10進数) → 2014/2/14 01:00:10

ちゃんとUnix時間になっていることがわかる.

マシンIDの仕組み

なんとなくユニークになるっぽいっていうのはわかった.

しかし,やはり疑問に思うのは,マシンIDって何だよ!って所だと思う.

Google Group-mongodb-user ›How to get machine id

で,このメーリングリストの投稿によると,マシンIDの生成はクライアント依存らしい.だけど,だいたいのクライアントではhostnameで決めてるらしい.

ということでpymongoの実装部分をみてみた.

ObjectIdの生成部分- pymongo

MachineIdの取得 - pymongo

マシンIDの生成にはhostnameを使っている...マシンIDに関して言えばユニークじゃないんだね.

もっとユニークなものを使ってマシンIDは生成されると思ってた...

だって,同じhostname持ってるマシンあるよ.ubuntuとかさ.

以下の2つはhostnameが同じ別のマシンでpymongoを使って生成したObejectId

確かにマシンIDは同じだ.

52fcf47b 1d41c8 2e90 e33611
52fcf461 1d41c8 75fe aff3e3

ただし,純正のmongoクライアントはhostnameでマシンIDを生成している訳では無いようで,マシンIDは異なるものになっていた.

ObjectIdは本当にユニークなのか?

ObjectIdの構成要素となっている4つの要素

  • Unix時間

  • マシンID

  • プロセスID

  • カウンタ

はそれぞれは十分重複する可能性があることがわかった.

しかし,これらが組み合わさるとユニークだと言える.

同一のmongoクライアントのプロセスではUnix時間カウンタがあるので重複することは不可能.

同一マシン上ではmongoクライアントのプロセスIDが異なるので不可能.

別マシン上ではマシンIDが異なるので不可能(hostnameが同じならば可能性がある).

重複するIDを生成するためには以下の条件が必要になる.

  • 同じ時刻の1秒間で,

  • 同じhostnameを持つマシンで,

  • 同じプロセスIDを持つmongoクライアントのプロセスを起動し,

  • 乱数を調整してカウンタをあわせる.

普通に使っている分にはユニークですね.

普通に使いましょう.