2つの異なるコレクションでMongo ObjectIdが重複して生成される可能性はありますか?


187

2つの異なるコレクションのドキュメントに対して、まったく同じMongo ObjectIdを生成することは可能ですか?確かにありそうもないことですが、それは可能ですか?

具体的になりすぎずに、私が取り組んでいるアプリケーションでは、当サイトの本格的なユーザーに転向したいと思っている選挙で選ばれた役人の公開プロフィールを表示するためです。ユーザーと現在当サイトのメンバーではない選出された役員のために、別々のコレクションがあります。選出されたオフィシャルに関するさまざまなデータを含む他のさまざまなドキュメントがあり、それらはすべて、選出されたオフィシャルObjectIdを使用する人物にマッピングされます。

アカウントを作成した後も、選出された役人に関連付けられているデータを強調表示しますが、これらのデータは、ユーザーのコレクションの一部であり、対応するユーザーのObjectIdを使用して、プロファイルをアプリケーションとの相互作用にマップします。

数か月前にアプリケーションをMySqlからMongoに変換し始めました。移行中は、これらの両方のデータ型のレガシーMySql IDを保存し、選出された公式のMongo ObjectIdをユーザーに保存するようになりました選出された公式データにマップするドキュメント。

私は前に選ばれた公式のObjectIdとして新しいユーザーObjectIdを指定することだけを考えていましたが、物事を簡単にするために、既存のユーザーObjectIdとの衝突が発生しないようにしました。

あなたの洞察をありがとう。

編集:この質問を投稿してすぐに、私が提案した解決策はあまり良いアイデアではないことに気付きました。現在のスキーマをそのまま保持し、ユーザードキュメントで選出された公式の '_id'にリンクすることをお勧めします。



1
私は以前そのページを読んだことがあります。皮肉なことに、私は以前の回答で実際に同じページにリンクしました。そして、私は「かなりユニークである可能性が高い」という免責事項を確認しましたが、コレクションが挿入されていることがこれに何らかの要因を果たしているかどうかはわかりませんでした。ObjectIdの2バイトのプロセスID部分が実際に何を表しているのか、よくわかりません。コレクションと関係がある場合は、異なるコレクションのまったく同じマシン上でまったく同時に作成された2つの異なるドキュメントの間に一意性があります。
アンソニージャック

1
2バイトのプロセスIDは、ObjectIDを生成するプロセスのPIDです。例として、pymongoがObjectIDを生成するために使用するコードを次に示します。github.com
mongodb

私が遭遇した1つの問題は、バッチ挿入です。10k文書のバッチを作成していて、カウンター部分が毎回ロールオーバーするので毎回衝突していました。
fawce

久しぶりのことですが、1万件の書類が転売されません。カウンター部分は3桁ではなく3バイトです。それは1600万を超えています。
Asya Kamsky 2014年

回答:


318

短い答え

最初の質問に直接回答を追加するだけです:はい、BSONオブジェクトID生成を使用する場合、ほとんどのドライバーでは、IDはほぼ確実にコレクション間で一意になります。「ほぼ確実に」の意味については、以下を参照してください。

長い答え

Mongo DBドライバーによって生成されたBSONオブジェクトIDは、コレクション全体で一意である可能性が非常に高いです。これは主に、IDの最後の3バイトが原因です。IDのほとんどは、静的インクリメントカウンターを介して生成されます。そのカウンターはコレクションに依存しません。グローバルです。たとえば、Javaドライバーは、ランダムに初期化された静的AtomicIntegerを使用します。

では、Mongoのドキュメントで、IDは一意であると明言するのではなく、IDが一意である可能性が「高い」と言うのはなぜですか。一意のIDを取得できない場合、3つの可能性があります(それ以上ある場合はお知らせください)。

この説明の前に、BSONオブジェクトIDは次のもので構成されていることを思い出してください。

[エポックから4バイト秒、3バイトのマシンハッシュ、2バイトのプロセスID、3バイトのカウンター]

ここに3つの可能性がありますので、あなたは自分でだまされてしまう可能性を判断します。

1)カウンターのオーバーフロー:カウンターには3バイトがあります。同じプロセスの同じマシンで、1秒間に16,777,216(2 ^ 24)を超えるドキュメントを挿入した場合、増加するカウンターバイトがオーバーフローし、同じ時間を共有する2つのオブジェクトIDがマシンに残る可能性があります。 、プロセス、およびカウンターの値。

2)インクリメントしないカウンター:一部のMongoドライバーは、カウンターバイトにインクリメントする数値の代わりに乱数を使用します。これらの場合、一意でないIDが生成される可能性は1 / 16,777,216ですが、これら2つのIDが同じ秒(つまり、IDの時間セクションが次の秒に更新される前)に生成される場合に限ります。機械、同じプロセスで。

3)ハッシュを処理して同じ値にハッシュします。マシンIDとプロセスIDの値は、非常にまれなシナリオでは、2つの異なるマシンの同じ値にマッピングされる場合があります。これが発生し、同時に2つの異なるマシンの2つのカウンターが同じ1秒間に同じ値を生成すると、IDが重複することになります。

これらは注意すべき3つのシナリオです。シナリオ1と3はほとんどありそうになく、適切なドライバーを使用している場合、シナリオ2は完全に回避できます。確実に知るには、ドライバのソースを確認する必要があります。


3バイトのカウンターは、マシンあたりのプロセスごとに1秒あたりに挿入された2 ^ 24 = 16777216のドキュメント数を受け入れる機能を表していませんか?
フォレストイェ

あなたは完全に正しい、私は誤ってビット数を半分にした-答えは修正された。
Raj Advani

私はこれに足を踏み入れたばかりなので、一部のドライバー(Cなど)は増分を使用しますが、アトミックに増分しないため、時々、競合状態のため同じ
Oid

39
ObjectIdマシンハッシュ、プロセスID、およびカウンターがすべて同じである限り、136年後には以前と同じように別のショットを生成するという事実を完全にスキップしました
jamylak

25
@jamylak緊急になったときにその問題に対処します(70年代にYYMMDDの日付形式を標準化した人々によると)
Philipp

14

ObjectIdはUUIDと同様の方法でクライアント側で生成されますが、大まかに増加する順序や作成時間を無料でエンコードするなど、データベースに格納するためのいくつかの優れたプロパティがあります。ユースケースの重要な点は、異なるマシンで生成された場合でも、高い確率で一意性を保証するように設計されていることです。

一般に_idフィールドを参照している場合は、コレクション全体で一意性を必要としないため、古い_idを再利用しても安全です。具体的な例として、2つのコレクションがcolorsとのfruits場合、両方に同時にのようなオブジェクトを含めることができます{_id: 'orange'}

ObjectIdsの作成方法について詳しく知りたい場合は、次の仕様をご覧ください。http//www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification


11

Mongo ObjectIDの重複で問題が発生している場合は、Mongo自体で重複が発生する可能性は低いものの、MongoのPHPで重複した_idが生成される可能性があることを知っておく必要があります。

これが定期的に発生したユースケースは、データセットをループしてデータをコレクションに挿入しようとしたときです。

注入データを保持する配列は、_id値を指定していない場合でも、反復ごとに明示的にリセットする必要があります。何らかの理由で、INSERTプロセスは、Mongo _idをグローバル変数であるかのように配列に追加します(配列にグローバルスコープがない場合でも)。これは、通常、配列の値が呼び出し元の関数に永続化されないことを期待する別の関数呼び出しで挿入を呼び出している場合でも影響を与える可能性があります。

これには3つの解決策があります。

  1. unset()配列の_idフィールドを使用できます
  2. array()データセットをループするたびに、配列全体を再初期化できます
  3. _id値を明示的に自分で定義できます(自分で重複を生成しないように定義するように注意してください)。

私の推測では、これはPHPインターフェースのバグであり、Mongoの問題ではありませんが、この問題が発生した場合は、_idの設定を解除するだけで問題ありません。


ここを参照してください:php.net/manual/en/mongocollection.insert.php: "注:パラメータに_idキーまたはプロパティがない場合、新しいMongoIdインスタンスが作成され、それに割り当てられます。この特別な動作は、パラメータは参照によって渡されることを意味します。」、それはバグではなく機能であり、そのようにすることを意図しています
Oliver Konig

1
ここで説明しているシナリオが理解できません。おそらくあなたはバグを示すいくつかのコードを示すことができますか?
Mark Amery、2015年

-7

コレクション間でのObjectIdの一意性については、何の保証もありません。確率的に非常に低い可能性があるとしても、コレクション全体の_idの一意性に依存するアプリケーション設計は非常に貧弱です。

mongoシェルでこれを簡単にテストできます。

MongoDB shell version: 1.6.5
connecting to: test
> db.foo.insert({_id: 'abc'})
> db.bar.insert({_id: 'abc'})
> db.foo.find({_id: 'abc'})
{ "_id" : "abc" }
> db.bar.find({_id: 'abc'})
{ "_id" : "abc" }
> db.foo.insert({_id: 'abc', data:'xyz'})
E11000 duplicate key error index: test.foo.$_id_  dup key: { : "abc" }

そのため、コレクション全体で_idが一意であることを絶対に信頼しないでください。また、ObjectId生成関数を制御しないので、それに依存しないでください。

UUIDのようなものを作成することは可能であり、それを手動で行うと、一意性の保証が向上します。

同じコレクションに異なる「タイプ」のオブジェクトを置くことができるので、2つの「テーブル」を同じコレクションに入れないでください。それらは同じ_idスペースを共有するため、一意であることが保証されます。「見込み」から「登録済み」に切り替えると、フィールドが単純に反転します...


1
一般的に、_idフィールドとObjectIDタイプを混同していると思います。ObjectIDタイプは、UUIDのように扱えるようにすることを目的として、一意性のために特別に設計されました。ただし、_idフィールドは任意のタイプにすることができ、例の文字列など、キーに他のタイプを使用する場合にのみ、単一のコレクションの一意性を保証します。
mstearn、2011年

@mstearn(Nitpick)UUIDは本質的に一意であるという概念に欠陥があります。優れたUUID /シーケンス生成戦略では、衝突が発生する可能性は低くなりますが、ジェネレーター間の絶対的な一意性を保証するために、固有のジェネレーター(たとえば、一意の場所)を考慮する必要があります。確かに、ほとんどの確率は非常に低いため、該当する問題はありません:-) GUID。一つの問題はないものの出てくるが、重複/代わりに新しい世代のIDのコピーです。

1
@pst:MongoDBs ObjectIDには、生成プロセスのpidと、ホスト名のハッシュに基づくいくつかのバイトの両方が含まれます。これらをタイムスタンプとインクリメントカウンターと組み合わせると、個別に生成された2つのObjectIDがグローバル/ユニバーサルで一意になる可能性が非常に高くなります。もちろん、あなたが言ったようにそれは新しく生成されたObjectIDにのみ適用されます。
mstearn、2013年

1
私はObjectIdタイプを参照しています。「_id」に文字列値を指定していません。もちろん、手動で完全に同じ文字列に設定すると、それらは同じになり、競合します。
アンソニージャック

ええ、私は私のポストで物事を明確にしました。_idは確かに一意ではありません。ObjectId生成関数を制御しないため、これに依存することはおそらくお勧めできません。
slacyは2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.