Както вече беше написано, няма правила като втората нормална форма за SQL.
Има обаче някои най-добри практики и често срещани клопки, свързани с оптимизацията за MongoDB, които ще изброя тук.
Прекомерна употреба на вграждане
Ограничението за BSON
Противно на общоприетото схващане, няма нищо лошо в препратките. Да приемем, че имате библиотека с книги и искате да проследявате наемите. Можете да започнете с модел като този
{
// We use ISBN for its uniqueness
_id: "9783453031456"
title: "Schismatrix",
author: "Bruce Sterling",
rentals: [
{
name:"Markus Mahlberg,
start:"2015-05-05T03:22:00Z",
due:"2015-05-12T12:00:00Z"
}
]
}
Въпреки че има няколко проблема с този модел, най-важният не е очевиден – ще има ограничен брой наеми поради факта, че BSON документите имат ограничение за размер от 16MB.
Проблемът с миграцията на документи
Другият проблем със съхраняването на наеми в масив би бил, че това би причинило относително чести миграции на документи, което е доста скъпа операция. BSON документите никога не се разделят и се създават с малко допълнително пространство, разпределено предварително, което се използва, когато нарастват. Това допълнително пространство се нарича подложка. Когато подложката е надвишена, документът се премества на друго място във файловете с данни и се разпределя ново място за подложка. Така честите добавяния на данни причиняват чести миграции на документи. Следователно, най-добрата практика е да предотвратите честите актуализации, увеличавайки размера на документа и вместо това да използвате препратки.
Така че за примера бихме променили единичния си модел и ще създадем втори. Първо, моделът за книгата
{
_id: "9783453031456",
title:"Schismatrix",
author: "Bruce Sterling"
}
Вторият модел за наем би изглеждал така
{
_id: new ObjectId(),
book: "9783453031456",
rentee: "Markus Mahlberg",
start: ISODate("2015-05-05T03:22:00Z"),
due: ISODate("2015-05-05T12:00:00Z"),
returned: ISODate("2015-05-05T11:59:59.999Z")
}
Същият подход, разбира се, може да се използва за автор или наемател.
Проблемът с прекомерната нормализация
Нека погледнем малко назад. Разработчикът ще идентифицира субектите, включени в бизнес казус, ще дефинира техните свойства и отношения, ще напише съответните класове на субекти, ще си блъска главата в стената за няколко часа, за да накара тройния вътрешен-външен-горе-и-отвъд JOIN да работи, необходим за случая на употреба и всички заживели щастливо досега. Така че защо да използвате NoSQL като цяло и MongoDB в частност? Защото никой не е живял щастливо досега. Този подход се мащабира ужасно и почти изключително единственият начин за мащабиране е вертикален.
Но основната разлика на NoSQL е, че моделирате данните си според въпросите, на които трябва да получите отговор.
Като се има предвид това, нека да разгледаме типична връзка n:m и да вземем връзката от автори към книги като наш пример. В SQL бихте имали 3 таблици:две за вашите обекти (книги и автори ) и един за връзката (Кой е авторът на коя книга? ). Разбира се, можете да вземете тези таблици и да създадете техните еквивалентни колекции. Но тъй като в MongoDB няма JOIN, ще ви трябват три заявки (една за първия обект, една за неговите отношения и една за свързаните обекти), за да намерите свързаните документи на обект. Това не би имало смисъл, тъй като подходът с три таблици за n:m релации е специално измислен, за да преодолее строгите схеми, наложени от SQL базите данни. Тъй като MongoDB има гъвкава схема, първият въпрос ще бъде къде да се съхранява релацията, запазвайки проблемите произтичащи от прекомерна употреба на вграждане в ума. Тъй като един автор може да напише доста книги през следващите години, но авторството на книга рядко, ако изобщо се променя, отговорът е прост:ние съхраняваме авторите като препратка към авторите в данните за книгите
{
_id: "9783453526723",
title: "The Difference Engine",
authors: ["idOfBruceSterling","idOfWilliamGibson"]
}
И сега можем да намерим авторите на тази книга, като направим две заявки:
var book = db.books.findOne({title:"The Difference Engine"})
var authors = db.authors.find({_id: {$in: book.authors})
Надявам се, че горното ще ви помогне да решите кога всъщност да „разделите“ колекциите си и да заобиколите най-честите клопки.
Заключение
Що се отнася до вашите въпроси, ето моите отговори
- Както е написано преди:Не , но имането предвид на техническите ограничения трябва да ви даде представа кога може да има смисъл.
- Не е лошо – стига да отговаря на вашия случай(и) на употреба . Ако имате дадена категория и нейния
_id
, лесно е да намерите свързаните продукти. Когато зареждате продукта, можете лесно да получите категориите, към които принадлежи, дори ефективно, като_id
се индексира по подразбиране. - Все още не съм намерил случай на употреба, който не може да бъде направен с MongoDB, въпреки че някои неща могат да станат малко по-сложни с MongoDB. Това, което трябва да направите imho, е да вземете сумата от вашите функционални и нефункционални изисквания и да проверите дали предимствата надвишават недостатъците. Моето основно правило:ако едно от „скалируемост“ или „висока наличност/автоматично преодоляване при отказ“ е в списъка ви с изисквания, MongoDB си струва повече от поглед.