Використання UUID замість ObjectID в MongoDB


83

Ми переносимо базу даних з MySQL на MongoDB з міркувань продуктивності та розглядаємо, що використовувати для ідентифікаторів документів MongoDB. Ми дискутуємо між використанням ObjectID, що є за замовчуванням MongoDB, або використанням UUID (замість цього ми використовували до цього часу в MySQL). Наразі аргументами, які ми маємо підтримати будь-який із цих варіантів, є такі:

ObjectIDs: ObjectIDs - це за замовчуванням MongoDB, і я припускаю (хоча я не впевнений), що це з якоїсь причини, тобто, я сподіваюся, що MongoDB може обробляти їх ефективніше, ніж UUID, або має іншу причину віддавати їм перевагу. Я також знайшов цю відповідь stackoverflow, в якій згадується, що використання ObjectID робить індексацію більш ефективною, однак було б непогано мати деякі показники щодо того, наскільки ця "більш ефективна".

UUID: Наш основний аргумент на користь використання UUID (і це досить важливий) полягає в тому, що вони так чи інакше підтримуються практично будь-якою базою даних. Це означає, що якщо якимось чином буде вирішено перейти з MongoDB на щось інше з будь-якої причини, і ми вже маємо API, який отримує документи з БД на основі їх ідентифікаторів, для клієнтів цього API нічого не змінюється, оскільки ідентифікатори можуть продовжуватись щоб бути абсолютно однаковим. Якби ми використовували ObjectID, я не впевнений, як би ми перенесли їх в іншу БД.

Хтось має уявлення про те, чи може один із цих варіантів бути кращим за інший і чому? Ви коли-небудь використовували UUID в MongoDB замість ObjectID, і якщо так, то з якими перевагами / проблемами ви зіткнулися?

Відповіді:


42

Я думаю, що це чудова ідея, і Монго теж. вони перераховують UUID , як один із загальних варіантів в _idполе .

Міркування:

  • Ефективність - Як зазначається в інших відповідях, тести показують, що UUID викликають падіння продуктивності вставок. У найгіршому випадку, що вимірюється (від 10M до 20M документів у колекції), вони мають приблизно ~ 2-3 рази повільніше - різниця між вставкою 2000 (UUID) та 7500 (ObjectID) документів на секунду. Це велика різниця, але її значення повністю залежить від вашого випадку використання. Ви будете пакетно вставляти мільйони документів одночасно? Для більшості програм, які я будував, загальним випадком є ​​вставка окремих документів. У цьому тесті різниця набагато менша (6 250 - проти 7500; ~ 20%). Тип ідентифікатора просто не є обмежуючим фактором.
  • Переносимість - Інші БД, звичайно, мають хорошу підтримку UUID, тому портативність буде покращена. Крім того, оскільки UUID більші (більше бітів), можна перепакувати ObjectID у "форму" UUID . Цей підхід не такий приємний, як пряма портативність, але він дає вам шлях уперед.

Протилежність деяким іншим відповідям:

  • UUID мають рідну підтримку - Ви можете використовувати UUID()функцію в оболонці Mongo точно так само, як і раніше ObjectID(); перетворити рядок в еквівалентний об'єкт BSON.
  • UUID не особливо великі - вони 128 бітові в порівнянні з ObjectID, які є 96 бітовими. (Вони повинні кодуватися за допомогою двійкового підтипу 0x04.)
  • UUID можуть включати мітку часу - Зокрема, UUIDv1 кодує мітку часу з 60 бітами точності порівняно з 32 бітами в ObjectID. Це на 6 порядків більше точності, тобто наносекунди замість секунд. Насправді це може бути пристойним способом зберігання міток часу з більшою точністю, ніж підтримка об'єктів Mongo / JS Date, проте ...
    • Побудувати в UUID()функції тільки генерує v4 (випадкових) UUID , так, щоб використовувати це в цьому, ви б спертися на ваше додаток або драйвер Монго для створення ID.
    • На відміну від ObjectIDs, із- за способу фрагментування UUID мітка часу не дає вам природного порядку. Це може бути хорошим чи поганим залежно від вашого випадку використання.
    • Включення позначок часу у ваші посвідчення часто є поганою ідеєю. В кінцевому підсумку витікає створений час документів, де б не було виявлено посвідчення особи. Що ще гірше, UUID v1 також кодують унікальний ідентифікатор машини, на якій вони генеруються, що може надати додаткову інформацію про вашу інфраструктуру (наприклад, кількість серверів). Звичайно, ObjectID також кодують позначку часу, тому це частково вірно і для них.

49

_idПоле MongoDB може мати будь-яке значення , яке ви хочете так довго , як ви можете гарантувати , що вона є унікальною для колекції. Коли ваші дані вже мають природний ключ, немає причин не використовувати це замість автоматично згенерованих ObjectID.

Ідентифікатори об’єктів надаються як розумне рішення за замовчуванням для безпечного генерування власного унікального ключа (і для того, щоб відмовити новачкам намагатися копіювати SQL, AUTO INCREMENT що є поганою ідеєю в розподіленій базі даних).

Не використовуючи ObjectID, ви також пропускаєте ще одну зручну функцію: ObjectID також включає часову мітку unix, коли вона була створена, і багато драйверів пропонують функцію для її вилучення та перетворення на дату. Іноді це може зробити окреме create-dateполе зайвим.

Але коли вас не турбує ні те, ні інше, ви можете використовувати свої UUID як _idполе.


1
Дякую, правда полягає в тому, що мене насправді не хвилюють ідентифікатори, що містять інформацію про дату створення (у мене це вже є як окрема колонка). Чи є у вас якесь уявлення про різницю в ефективності між ними?
Крістіна

9
Привіт, Крістіно, насправді в драйвері Java MongoDB є цікава фотографія, яка показує час вставки у порівнянні між значеннями ObjectId та UUID jira.mongodb.org/browse/JAVA-403 . Захоплений слуханням про підхід, який ти врешті-решт застосував.
Роман Блахман,

1
UUIDv1 також включає відмітку часу та з ~ 6 порядків більшої точності. UUIDv1 кодує 60 біт часу (наносекунди) порівняно з ObjectIDs 32 біта (секунди).
Моломбі

8

Враховуйте обсяг даних, який ви б зберігали в кожному конкретному випадку.

MongoDB ObjectID має розмір 12 байт, упакований для зберігання, а його частини організовані для продуктивності (тобто позначка часу зберігається першою, що є логічним критерієм упорядкування).

І навпаки, стандартний UUID становить 36 байт, містить тире і зазвичай зберігається у вигляді рядка. Крім того, навіть якщо ви знімаєте нечислові символи і маєте намір зберігати числово, ви все одно повинні задовольнятись його "індексною" частиною (частина UUID v1, яка базується на часовій мітці), знаходиться посередині UUID і не т добре підходить для сортування. Проведено дослідження, які дозволяють швидкодіюче зберігання UUID, і я навіть написав бібліотеку Node.js для допомоги в управлінні нею.

Якщо ви маєте намір використовувати UUID, спробуйте реорганізувати його для оптимального індексування та сортування; інакше ви, ймовірно, вдаритесь до стіни виступу.


мабуть, додав би, що це слід уважно розглянути, оскільки не у всіх випадках ви хотіли б чогось сортувати / передбачати. наприклад, під час створення ідентифікаторів сеансу ви повинні взяти версію uuid v4 (випадкову).
Робін Ф.

Як щодо шардінгу, чи можете ви використовувати не хешований UUID для шардінгу або у вас буде така ж проблема, як і для ObjectID, де нові записи все опиняться в одному осколку?
mjaggard

1
немає причини зберігати UUID як рядок ... стандартний UUID - це рівно 16 байт і зазвичай зберігається як необроблений байт навіть у mongo. Ніхто не використовує v1 UUID, лише v4 (випадковий) та v5 (sha1).
Дмитро Гусаров

3
Як зазначає @Dmitry, UUID складають 16 байт (128 біт) і, як правило, не зберігаються у вигляді рядка. MongoDB має рідну підтримку і зберігає їх як бінарний підтип 0x04. Ви маєте рацію щодо нещасного відмітки часу, хоча це справжній біль. Я хотів би, щоб була офіційна версія UUID, яка діяла більше як SQUUIDs.
Моломбі

1

Я знайшов ці Тести колись тому, коли у мене було таке саме запитання. Вони в основному показують, що використання Guid замість ObjectId спричиняє зниження продуктивності індексу.

Я б у будь-якому разі рекомендував налаштувати Тести для імітації Вашого конкретного сценарію реального життя та подивитися, як виглядають цифри, не можна покладатися на 100% на загальні Тести.


1

Ми повинні бути обережними, щоб розрізнити вартість вставки речі MongoDB у порівнянні з витратами на генерування речі, в першу чергу плюс ці витрати відносно розміру корисного навантаження. Нижче наведено невелику матрицю, яка показує метод генерування _idперехрещеного за розміром додаткового додаткового байта корисного навантаження. Тести використовують лише javascript, проведений на MacBook Pro localhost для 100 000 вставок із використанням insertManyпартій по 100 без транзакцій, щоб спробувати видалити мережеві, балакучі та інші фактори. Також було зроблено два прогони з batch = 1, щоб лише підкреслити різку різницю.


Method                                                                                         
A  :  Simple int:          _id:0, _id:1, ...                                                   
B  :  ObjectId             _id:ObjectId("5e0e6a804888946fa61a1976"), ...                       
C  :  Simple string:       _id:"A0", _id:"A1", ...                                             

D  :  UUID length string   _id:"9575edcc-cb70-4d63-97ed-ee5d624de87b0", ...                    
      (but not actually                                                                        
      generated by UUID()                                                                      

E  :  Real generated UUID  _id: UUID("35992974-21ea-4f61-b715-2dfaed663b73"), ...              
      (stored UUID() object)                                                                   

F  :  Real generated UUID  _id: "6b16f733-ff24-4172-83f9-e4f96ace6775"                         
      (stored as string, e.g.                                                                  
      UUID().toString().substr(6,36)                                                           

Time in milliseconds to perform 100,000 inserts on fresh (empty) collection.

Extra                M E T H O D   (Batch = 100)                                                               
Payload   A     B     C     D     E     F       % drop A to F                                  
--------  ----  ----  ----  ----  ----  ----    ------------                                   
None      2379  2386  2418  2492  3472  4267    80%                                            
512       2934  2928  3048  3128  4151  4870    66%                                            
1024      3249  3309  3375  3390  4847  5237    61%                                            
2048      3953  3832  3987  4342  5448  5888    49% 
4096      6299  6343  6199  6449  7634  8640    37%                                            
8192      9716  9292  9397 10816 11212 11321    16% 

Extra              M E T H O D   (Batch = 1)                                          
Payload   A      B      C      D      E      F       % drop A to F              
--------  -----  -----  -----  -----  -----  -----                              
None      48006  48419  49136  48757  50649  51280   6.8%                       
1024      50986  50894  49383  49373  51200  51821   1.2%                       


Це був Quicky тест , але представляється очевидним , що основні струни і Інтс , як _idприблизно з тієї ж швидкість , але на самому справі генерації UUID додає час - особливо якщо взяти версію строкового UUID()об'єкта, наприклад , UUID().toString().substr(6,36) Крім того , варто відзначити , що побудувавши ObjectIdз'явиться щоб бути таким же швидким.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.