Khả năng trùng lặp Mongo ObjectId đang được tạo trong hai bộ sưu tập khác nhau?


187

Có thể tạo cùng một đối tượng Mongo ObjectId chính xác cho một tài liệu trong hai bộ sưu tập khác nhau không? Tôi nhận ra rằng nó chắc chắn rất khó xảy ra, nhưng liệu nó có thể?

Không cần quá cụ thể, lý do tôi hỏi là với một ứng dụng mà tôi đang làm việc, chúng tôi hiển thị hồ sơ công khai của các quan chức được bầu mà chúng tôi hy vọng sẽ chuyển đổi thành người dùng chính thức của trang web của chúng tôi. Chúng tôi có các bộ sưu tập riêng cho người dùng và các quan chức được bầu hiện không phải là thành viên của trang web của chúng tôi. Có nhiều tài liệu khác chứa nhiều mẩu dữ liệu khác nhau về các quan chức được bầu mà tất cả đều ánh xạ lại cho người sử dụng ObjectId chính thức được bầu của họ.

Sau khi tạo tài khoản, chúng tôi vẫn nêu bật dữ liệu liên quan đến quan chức được bầu nhưng giờ đây họ cũng là một phần của bộ sưu tập người dùng với ObjectId người dùng tương ứng để ánh xạ hồ sơ của họ tới các tương tác với ứng dụng của chúng tôi.

Chúng tôi đã bắt đầu chuyển đổi ứng dụng của mình từ MySql sang Mongo vài tháng trước và trong khi chúng tôi đang chuyển đổi, chúng tôi lưu trữ id MySql kế thừa cho cả hai loại dữ liệu này và chúng tôi cũng bắt đầu lưu trữ Mongo ObjectId chính thức được bầu trong người dùng tài liệu để ánh xạ trở lại dữ liệu chính thức được bầu.

Tôi đã cân nhắc chỉ định ObjectId người dùng mới là ObjectId chính thức được bầu trước đó để làm cho mọi thứ đơn giản hơn nhưng muốn đảm bảo rằng không thể có xung đột với bất kỳ ObjectId người dùng hiện có nào.

Cảm ơn sự sáng suốt của bạn.

Chỉnh sửa: Ngay sau khi đăng câu hỏi này, tôi nhận ra rằng giải pháp đề xuất của tôi không phải là một ý tưởng rất tốt. Sẽ tốt hơn nếu chỉ giữ nguyên lược đồ hiện tại mà chúng ta có và chỉ liên kết với '_id' chính thức được bầu trong tài liệu người dùng.



1
Tôi đã đọc trang đó trước đây. Trớ trêu thay tôi thực sự liên kết đến cùng một trang trong một câu trả lời trước đó. Và tôi đã thấy từ chối trách nhiệm "xác suất cao là duy nhất" nhưng không chắc chắn nếu bộ sưu tập được đưa vào chơi bất kỳ yếu tố nào trong việc này. Tôi đoán điều tôi không chắc chắn là phần chính xác của phần ID ID 2 byte của ObjectId thực sự đại diện cho điều gì. Nếu nó có liên quan đến bộ sưu tập thì sẽ có sự duy nhất giữa hai tài liệu khác nhau được tạo cùng một lúc trên cùng một máy chính xác trong các bộ sưu tập khác nhau.
Anthony Jack

1
Id tiến trình 2byte là pid của quá trình tạo ObjectID. Ví dụ, đây là mã pymongo sử dụng để tạo ObjectID: github.com/mongodb/mongo-python-do/blob/master/bson/iêu
mstearn

Một gotcha tôi chạy vào là chèn hàng loạt. Tôi đang xây dựng các lô tài liệu 10k và va chạm mỗi lần vì phần quầy bị lật lại mỗi lần.
fawce

Tôi biết đã được một lúc, nhưng tài liệu 10K sẽ không được chuyển qua quầy. Phần truy cập là ba byte, không phải ba chữ số. Đó là hơn 16 triệu.
Asya Kamsky

Câu trả lời:


318

Câu trả lời ngắn

Chỉ cần thêm câu trả lời trực tiếp cho câu hỏi ban đầu của bạn: CÓ, nếu bạn sử dụng tạo ID đối tượng BSON, thì đối với hầu hết các trình điều khiển , ID gần như chắc chắn sẽ là duy nhất trên các bộ sưu tập. Xem bên dưới để biết "gần như chắc chắn" nghĩa là gì.

Câu trả lời dài

ID đối tượng BSON được tạo bởi trình điều khiển Mongo DB rất có thể là duy nhất trên các bộ sưu tập. Điều này chủ yếu là do 3 byte cuối cùng của ID, mà đối với hầu hết các trình điều khiển được tạo thông qua bộ đếm tăng tĩnh. Bộ đếm đó là độc lập với bộ sưu tập; nó là toàn cầu. Trình điều khiển Java, ví dụ, sử dụng một AtomicInteger tĩnh được khởi tạo ngẫu nhiên.

Vậy tại sao, trong các tài liệu Mongo, họ có nói rằng ID "rất có thể" là duy nhất, thay vì nói thẳng rằng họ S be là duy nhất? Ba khả năng có thể xảy ra khi bạn sẽ không nhận được một ID duy nhất (vui lòng cho tôi biết nếu có nhiều hơn):

Trước cuộc thảo luận này, hãy nhớ rằng ID đối tượng BSON bao gồm:

[4 byte giây kể từ epoch, băm máy 3 byte, ID tiến trình 2 byte, bộ đếm 3 byte]

Dưới đây là ba khả năng, vì vậy bạn tự đánh giá khả năng bị lừa bịp:

1) Tràn bộ đếm: có 3 byte trong bộ đếm. Nếu bạn tình cờ chèn hơn 16.777.216 (2 ^ 24) tài liệu trong một giây, trên cùng một máy, trong cùng một quy trình, thì bạn có thể tràn các byte truy cập tăng dần và kết thúc bằng hai ID đối tượng chia sẻ cùng một lúc, máy , quá trình và các giá trị truy cập.

2) Bộ đếm không tăng: một số trình điều khiển Mongo sử dụng số ngẫu nhiên thay vì tăng số cho byte truy cập. Trong các trường hợp này, có 1 / 16.777.216 cơ hội tạo ID không duy nhất, nhưng chỉ khi hai ID đó được tạo trong cùng một giây (tức là trước phần thời gian của ID cập nhật sang giây tiếp theo), trên cùng một Máy, trong quá trình tương tự.

3) Máy và xử lý băm đến cùng các giá trị. ID máy và giá trị ID quá trình có thể, trong một số trường hợp rất khó xảy ra, ánh xạ tới cùng các giá trị cho hai máy khác nhau. Nếu điều này xảy ra, đồng thời hai bộ đếm trên hai máy khác nhau, trong cùng một giây, tạo ra cùng một giá trị, thì bạn sẽ kết thúc bằng một ID trùng lặp.

Đây là ba kịch bản để xem ra. Kịch bản 1 và 3 dường như rất khó xảy ra và kịch bản 2 là hoàn toàn có thể tránh được nếu bạn sử dụng đúng trình điều khiển. Bạn sẽ phải kiểm tra nguồn của trình điều khiển để biết chắc chắn.


Không phải bộ đếm 3 byte thể hiện khả năng chấp nhận 2 ^ 24 = 16777216 số lượng tài liệu được chèn mỗi giây trên mỗi quy trình trên mỗi máy?
Forrest Ye

Bạn hoàn toàn đúng, tôi vô tình giảm một nửa số bit - câu trả lời đã được sửa đổi.
Raj Advani

Vì tôi mới bước vào vấn đề này, hãy để tôi thêm rằng một số trình điều khiển (ví dụ C), mặc dù sử dụng số gia, không tăng theo nguyên tử, do đó, đôi khi nó tạo ra tình trạng tương tự do điều kiện cuộc đua
Pawel Veselov

39
Bạn đã hoàn toàn bỏ qua thực tế là trong 136 năm qua, bạn sẽ có một phát bắn khác để tạo ra cùng một thứ ObjectIdbạn đã có trước đó, miễn là băm máy, ID xử lý và bộ đếm đều biến thành như nhau
jamylak

25
@jamylak Chúng tôi sẽ giải quyết vấn đề đó khi nó trở nên cấp bách (những người đã chuẩn hóa định dạng ngày YYMMDD trong thập niên 70)
Philipp

14

ObjectIds được tạo phía máy khách theo cách tương tự như UUID nhưng với một số thuộc tính đẹp hơn để lưu trữ trong cơ sở dữ liệu, chẳng hạn như tăng thứ tự và mã hóa thời gian tạo miễn phí. Điều quan trọng cho trường hợp sử dụng của bạn là chúng được thiết kế để đảm bảo tính duy nhất với xác suất cao ngay cả khi chúng được tạo trên các máy khác nhau.

Bây giờ nếu bạn đang đề cập đến trường _id nói chung, chúng tôi không yêu cầu tính duy nhất trên các bộ sưu tập nên việc sử dụng lại _id cũ là an toàn. Ví dụ cụ thể, nếu bạn có hai bộ sưu tập,colorsfruitscả hai có thể đồng thời có một đối tượng như{_id: 'orange'} .

Trong trường hợp bạn muốn biết thêm về cách tạo ObjectIds, đây là thông số: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification


11

Trong trường hợp bất kỳ ai gặp vấn đề với các đối tượng Mongo trùng lặp, bạn nên biết rằng mặc dù không có khả năng xảy ra lỗi trong chính Mongo, nhưng có thể sao chép _id được tạo bằng PHP trong Mongo.

Trường hợp sử dụng xảy ra với sự đều đặn đối với tôi là khi tôi lặp qua một tập dữ liệu và cố gắng đưa dữ liệu vào bộ sưu tập.

Mảng chứa dữ liệu tiêm phải được đặt lại rõ ràng trên mỗi lần lặp - ngay cả khi bạn không chỉ định giá trị _id. Vì một số lý do, quy trình INSERT thêm Mongo _id vào mảng như thể nó là biến toàn cục (ngay cả khi mảng không có phạm vi toàn cục). Điều này có thể ảnh hưởng đến bạn ngay cả khi bạn đang gọi chèn trong một lệnh gọi hàm riêng biệt, nơi bạn thường mong muốn các giá trị của mảng không tiếp tục quay lại hàm gọi.

Có ba giải pháp cho vấn đề này:

  1. Bạn có thể unset()trường _id từ mảng
  2. Bạn có thể xác định lại toàn bộ mảng với array()mỗi lần bạn lặp qua tập dữ liệu của mình
  3. Bạn có thể tự xác định rõ giá trị _id (chú ý xác định giá trị đó theo cách mà bạn không tự tạo ra dups).

Tôi đoán rằng đây là một lỗi trong giao diện PHP và không có vấn đề gì với Mongo, nhưng nếu bạn gặp phải vấn đề này, chỉ cần bỏ đặt _id và bạn sẽ ổn thôi.


xem tại đây: php.net/manual/en/mongocollection.insert.php : "Lưu ý: Nếu tham số không có khóa _id hoặc thuộc tính, một phiên bản MongoId mới sẽ được tạo và gán cho nó. Hành vi đặc biệt này không có nghĩa rằng tham số được truyền bằng tham chiếu. ", đó là một tính năng, không phải là lỗi, nó có nghĩa là như vậy
Oliver Konig

1
Tôi không hiểu kịch bản bạn mô tả ở đây; có lẽ bạn có thể hiển thị một số mã thể hiện lỗi?
Đánh dấu Amery

-7

Không có gì đảm bảo về tính độc đáo của ObjectId trên các bộ sưu tập. Ngay cả khi nó rất khó xảy ra, đó sẽ là một thiết kế ứng dụng rất kém dựa trên tính độc đáo của _id trên các bộ sưu tập.

Người ta có thể dễ dàng kiểm tra điều này trong vỏ 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" }

Vì vậy, tuyệt đối không dựa vào _id là duy nhất trên các bộ sưu tập và vì bạn không kiểm soát chức năng tạo ObjectId, không nên dựa vào nó.

Bạn có thể tạo ra thứ gì đó giống như một uuid và nếu bạn làm điều đó bằng tay, bạn có thể có một số đảm bảo tốt hơn về tính độc đáo.

Hãy nhớ rằng bạn có thể đặt các đối tượng thuộc các "loại" khác nhau trong cùng một bộ sưu tập, vậy tại sao không đặt hai "bảng" của bạn vào cùng một bộ sưu tập. Họ sẽ chia sẻ cùng một không gian _id, và do đó, sẽ được đảm bảo duy nhất. Chuyển từ "tiềm năng" sang "đã đăng ký" sẽ là một thao tác đơn giản ...


1
Tôi nghĩ rằng bạn có thể nhầm lẫn trường _id nói chung với loại ObjectID. Loại ObjectID được thiết kế đặc biệt cho tính duy nhất với mục tiêu mà nó có thể được đối xử như một UUID. Tuy nhiên, trường _id có thể là bất kỳ loại nào và chỉ đảm bảo tính duy nhất trên một bộ sưu tập nếu bạn sử dụng các loại khác cho khóa, chẳng hạn như một chuỗi trong ví dụ của bạn.
hãy học

@mstearn (Nitpick) Khái niệm rằng UUID là vốn là duy nhất là thiếu sót. Chiến lược tạo UUID / trình tự tốt có thể khiến va chạm không xảy ra nhưng cần phải tính đến các máy phát duy nhất (ví dụ: các vị trí duy nhất) để đảm bảo tính duy nhất tuyệt đối giữa các máy phát. Cấp, hầu hết có xác suất thấp đến mức không có mối quan tâm áp dụng :-) HƯỚNG DẪN . Một vấn đề mà không đưa ra tuy nhiên, là sự trùng lặp / sao chép của id thay vì một thế hệ mới.

1
@pst: Các ObjectID của MongoDB bao gồm cả pid của quá trình tạo và một số byte dựa trên hàm băm của tên máy chủ. Các kết hợp này với bộ đếm thời gian và bộ đếm tăng dần làm cho rất có khả năng rằng bất kỳ hai ObjectID nào được tạo riêng biệt sẽ là duy nhất trên toàn cầu / toàn cầu. Tất nhiên như bạn đã nói rằng chỉ áp dụng cho các ObjectID mới được tạo.
hãy học

1
Tôi đang đề cập đến loại ObjectId. Không chỉ định giá trị chuỗi cho '_id'. Tất nhiên chúng sẽ giống nhau và xung đột nếu bạn đặt chúng thành cùng một chuỗi chính xác theo cách thủ công.
Anthony Jack

Vâng, tôi đã làm rõ những điều trong bài viết của tôi. _id chắc chắn không phải là duy nhất và vì bạn không điều khiển chức năng tạo ObjectId, có lẽ nên dựa vào nó.
Slacy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.