2011-01-13 16 views
144

Czy jest możliwe wygenerowanie identycznego obiektu Mongo ObjectId dla dokumentu w dwóch różnych kolekcjach? Zdaję sobie sprawę, że jest to bardzo mało prawdopodobne, ale czy to możliwe?Możliwość generowania duplikatów Mongo ObjectId w dwóch różnych kolekcjach?

Bez zbytniej szczegółowości, proszę o to, że z aplikacją, nad którą pracuję, pokazujemy publiczne profile wybranych urzędników, którzy mamy nadzieję przekształcić w pełnoprawnych użytkowników naszej strony. Mamy osobne kolekcje dla użytkowników i wybranych urzędników, którzy nie są obecnie członkami naszej witryny. Istnieje wiele innych dokumentów zawierających różne dane o wybranych urzędnikach, które wszystkie odwzorowują z powrotem do osoby używającej wybranego oficjalnego ObjectID.

Po utworzeniu konta nadal podświetlamy dane powiązane z wybranym urzędnikiem, ale teraz są one również częścią kolekcji użytkowników z odpowiednim użytkownikiem ObjectId, aby odwzorować ich profil na interakcje z naszą aplikacją.

Rozpoczęliśmy konwersję naszej aplikacji z MySql na Mongo kilka miesięcy temu i podczas gdy jesteśmy w okresie przejściowym przechowujemy starszy identyfikator MySql dla obu tych typów danych i zaczynamy teraz przechowywać wybrane oficjalnie Mongo ObjectId w dokumencie użytkownika, aby odwzorować wybrane oficjalne dane.

Zastanawiam się właśnie nad określeniem nowego obiektu ObjectId jako poprzedniego wybranego oficjalnego obiektu ObjectId, aby uprościć sprawę, ale chciałem się upewnić, że kolizja z istniejącym ObjectIdem użytkownika nie była możliwa.

Dzięki za twój wgląd.

Edytuj: Wkrótce po opublikowaniu tego pytania zdałem sobie sprawę, że moje proponowane rozwiązanie nie było dobrym pomysłem. Lepiej będzie po prostu zachować aktualny schemat, który mamy na miejscu i po prostu połączyć się z wybranym oficjalnym "_id" w dokumencie użytkownika.

+0

Zobacz http://www.mongodb.org/display/DOCS/Object+IDs –

+1

Przeczytałem tę stronę wcześniej. Jak na ironię, faktycznie linkowałem do tej samej strony w poprzedniej odpowiedzi. I widziałem "rozsądnie wysokie prawdopodobieństwo bycia unikatowym", ale nie byłem pewien, czy włożenie kolekcji w to zagrało. Sądzę, że nie jestem pewien, co dokładnie reprezentuje dwubajtowa część identyfikatora procesu ObjectId. Jeśli ma to coś wspólnego z kolekcją, unikalny byłby między dwoma różnymi dokumentami tworzonymi dokładnie w tym samym czasie na tej samej maszynie w różnych kolekcjach. –

+1

Identyfikator procesu 2-bajtowego to identyfikator procesu generującego identyfikator obiektu. Jako przykład, tutaj jest kod używany przez pymongo do generowania identyfikatorów obiektów: https://github.com/mongodb/mongo-python-driver/blob/master/bson/objectid.py#L118 – mstearn

Odpowiedz

255

Krótka odpowiedź

Wystarczy dodać bezpośrednią odpowiedź na swoje początkowe pytanie: Tak, jeśli użyj generowania identyfikatorów obiektów BSON, a następnie dla większości sterowników. Identyfikatory prawie na pewno będą niepowtarzalne w kolekcjach. Zobacz poniżej, co oznacza "prawie na pewno".

długa odpowiedź

BSON Obiekt identyfikatory generowane przez kierowców Mongo DB są wysoce prawdopodobne, aby być unikalny w całej kolekcji. Wynika to głównie z ostatnich 3 bajtów identyfikatora, który dla większości sterowników jest generowany przez statyczny licznik przyrostu. Ten licznik jest niezależny od kolekcji; jest globalny. Na przykład sterownik Java używa losowo zainicjowanego, statycznego AtomicInteger.

Dlaczego więc w dokumentach Mongo mówią, że identyfikatory są "bardzo prawdopodobne", że są unikatowe, zamiast wprost powiedzieć, że będą wyjątkowe? Trzy możliwości mogą występować gdzie nie dostaniesz unikalny identyfikator (proszę dać mi znać, jeśli jest ich więcej):

Przed tej dyskusji, przypomnieć, że identyfikator BSON Obiekt składa się z:

[4 bajty sekund od epoka, 3 bajty maszyna mieszająca, 2 bajty proces ID, 3 bajty licznik]

Oto trzy możliwości, więc sam ocenić, jakie jest prawdopodobieństwo, aby uzyskać duplikatem:

1) Licznik przepełnienia: nie są 3 bajty w liczniku. Jeśli zdarzy ci się wstawić ponad 16,777,216 (2^24) dokumentów w ciągu jednej sekundy, na tym samym komputerze, w tym samym procesie, możesz przelać rosnące liczniki bajtów i zakończyć się dwoma identyfikatorami obiektów, które mają ten sam czas, maszynę , proces i wartości liczników.

2) Nieinwazyjne liczniki: niektóre sterowniki Mongo używają liczb losowych zamiast liczb zwiększających licznik bajtów. W takich przypadkach istnieje 1/16,777,216 szansa na wygenerowanie nieunikalnego identyfikatora, ale tylko wtedy, gdy te dwa identyfikatory są generowane w tej samej sekundzie (tj. Przed sekcją czasu aktualizacji identyfikatora do następnej sekundy), na tym samym maszyna, w tym samym procesie.

3) Skrypt maszynowy i procesowy mają te same wartości. Identyfikator komputera i wartości identyfikatora procesu mogą, w niektórych bardzo nieprawdopodobnych scenariuszach, odwzorowywać te same wartości dla dwóch różnych komputerów. Jeśli tak się stanie, a jednocześnie dwa liczniki na dwóch różnych komputerach, podczas tej samej sekundy, wygenerują tę samą wartość, to otrzymasz duplikat identyfikatora.

Oto trzy scenariusze, na które należy zwrócić uwagę. Scenariusze 1 i 3 wydają się mało prawdopodobne, a scenariusz 2 można całkowicie uniknąć, jeśli używasz właściwego sterownika. Musisz sprawdzić źródło sterownika, aby wiedzieć na pewno.

+0

Czy licznik 3-bajtowy nie reprezentuje możliwości przyjęcia 2^24 = 16777216 liczby dokumentów wstawionych na sekundę na proces na maszynę? –

+0

Masz całkowitą rację, przypadkowo zmniejszyłem liczbę bitów - odpowiedź została zmieniona. –

+0

Ponieważ właśnie wkroczyłem w to, dodam, że niektóre sterowniki (np. C), choć używa przyrostów, nie zwiększają atomowo, więc od czasu do czasu generuje ten sam oid z powodu stanu wyścigu –

-2

Nie ma żadnej gwarancji co do wyjątkowości obiektów ObjectId w kolekcjach. Nawet jeśli jest to probabilistycznie mało prawdopodobne, byłby to bardzo kiepski projekt aplikacji, który polegałby na unikalności kolekcji.

Można łatwo to sprawdzić w powłoce 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" } 

Więc absolutnie nie opierają się na _id użytkownika jest unikalny w całej kolekcji, a ponieważ nie kontrolują funkcję objectID generacji, nie należy polegaj na tym.

Możliwe jest stworzenie czegoś, co bardziej przypomina uuid, a jeśli zrobisz to ręcznie, możesz mieć lepszą gwarancję wyjątkowości.

Pamiętaj, że możesz umieszczać obiekty z różnych "typów" w tej samej kolekcji, więc dlaczego nie po prostu umieścić swoje dwa "stoły" w tej samej kolekcji. Będą dzielić tę samą przestrzeń _id, a zatem będą zagwarantowane unikatowe. Przełączenie z "prospektywnego" na "zarejestrowany" byłoby zwykłym odwróceniem pola ...

+1

Myślę, że możesz pomylić pole _id w ogóle z typem ObjectID. Typ ObjectID został specjalnie zaprojektowany pod kątem unikalności, mając na uwadze to, że można go traktować jak UUID. Jednak pole _id może być dowolnego typu i gwarantuje tylko unikalność pojedynczego zbioru, jeśli użyjesz innych typów dla klucza, na przykład ciąg w twoim przykładzie. – mstearn

+0

@mstearn (Nitpick) Pojęcie, że identyfikator UUID jest * z natury * unikalny, jest wadliwe. Dobra strategia generowania UUID/sekwencji może spowodować, że kolizja jest mało prawdopodobna, ale musi uwzględniać * unikatowe generatory * (na przykład unikalne lokalizacje), aby zagwarantować * absolutną wyjątkowość * między generatorami. Oczywiście większość ma tak niskie prawdopodobieństwo, że nie dotyczy :-) [GUID] (http://en.wikipedia.org/wiki/Globally_unique_identifier). Jedną kwestią, która * nie * wymyślić, jest jednak powielanie/kopiowanie identyfikatorów zamiast nowej generacji. –

+1

@ pst: MongoDBs ObjectID zawierają zarówno pid procesu generującego, jak i niektóre bajty na podstawie skrótu nazwy hosta. Te w połączeniu ze znacznikiem czasowym i licznikiem inkrementującym sprawiają, że niezwykle prawdopodobne jest, że dowolne dwa oddzielnie generowane identyfikatory obiektów będą globalnie/uniwersalnie unikalne. Oczywiście, jak powiedziałeś, dotyczy to tylko świeżo wygenerowanych ObjectIDów. – mstearn

13

ObjectIds są generowane po stronie klienta w sposób podobny do UUID, ale z pewnymi ładniejszymi właściwościami do przechowywania w bazie danych, takich jak przybliżanie porządkowanie i kodowanie czasu ich tworzenia za darmo. Kluczową sprawą dla twojego przypadku użycia jest to, że są zaprojektowane, aby zagwarantować wyjątkowość z dużym prawdopodobieństwem, nawet jeśli są generowane na różnych maszynach.

Jeśli teraz odnosisz się do pola _id, nie wymagamy wyjątkowości w kolekcjach, więc można bezpiecznie użyć starego _id.Jako konkretny przykład, jeśli masz dwie kolekcje, colors i fruits, obie mogą jednocześnie mieć obiekt taki jak {_id: 'orange'}.

w przypadku chcesz wiedzieć więcej o tym, jak ObjectIds są tworzone, oto specyfikacja: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

11

Jeśli ktoś ma problemy z duplikowaniem identyfikatorów obiektów Mongo, to powinieneś wiedzieć, że pomimo nieprawdopodobieństwa duplikacji w Mongo, możliwe jest wygenerowanie duplikatu _id w PHP w Mongo.

Przypadku użycia, w przypadku gdy stało się to z regularnością dla mnie jest, gdy przechodzę pętli przez zestaw danych i próby wstrzyknięcia danych do kolekcji.

Tablica zawierająca dane wtrysku musi być jawnie resetowana w każdej iteracji - nawet jeśli nie określa się wartości _id. Z jakiegoś powodu proces INSERT dodaje Mongo _id do tablicy tak, jakby była zmienną globalną (nawet jeśli tablica nie ma zasięgu globalnego). Może to wpłynąć na ciebie, nawet jeśli wywołujesz wstawienie w oddzielnym wywołaniu funkcji, w którym normalnie oczekuje się, że wartości tablicy nie będą się utrzymywać z powrotem do funkcji wywołującej.

Istnieją trzy rozwiązania to:

  1. Można unset() pola _id z tablicy
  2. można ponownie zainicjować całą tablicę z array() każdym razem, gdy pętla za pośrednictwem zestawu danych
  3. Można wyraźnie zdefiniuj wartość _id (uważając, aby zdefiniować ją w taki sposób, aby nie generować dups samemu).

Domyślam się, że jest to błąd w interfejsie PHP i nie stanowi to problemu z Mongo, ale jeśli napotkasz na ten problem, po prostu zdezaktywuj _id i powinieneś być w porządku.

+0

patrz tutaj: http://php.net/manual/en/mongocollection.insert.php: "Uwaga: Jeśli parametr nie ma klucza lub właściwości _id, zostanie utworzona i przypisana nowa instancja MongoId. To specjalne zachowanie nie oznacza, że ​​parametr jest przekazywany przez odniesienie. ", to funkcja, a nie błąd, ma na celu być w ten sposób –

+1

Nie rozumiem scenariusza, który tu opisujesz; może mógłbyś pokazać kod, który eksponuje błąd? –

Powiązane problemy