В предишния ми блог, Как да използвам MongoDB моделиране на данни за подобряване на операциите с пропускателна способност, обсъдихме 2-те основни подхода за моделиране на данни, тоест вграждане и препращане. Мащабируемостта на MongoDB е доста зависима от неговата архитектура и по-конкретно, моделирането на данни. Когато проектирате NoSQL DBM, основната точка на внимание е да се гарантират документи без схеми, освен малък брой колекции с цел лесна поддръжка. Добър интегритет на данните, приемане на валидиране на данни чрез някои дефинирани правила преди съхранението се насърчава. Архитектурата и дизайнът на базата данни трябва да бъдат нормализирани и разложени на множество малки колекции като начин за избягване на повторение на данни, подобряване на целостта на данните и улесняване на моделите за извличане. С това вие можете да подобрите последователността на данните, атомарността, издръжливостта и целостта на вашата база данни.
Моделирането на данни не е закъсняло начинание във фаза на разработка на приложение, а първоначално разглеждане, тъй като много аспекти на приложението всъщност се реализират по време на етапа на моделиране на данни. В тази статия ще обсъдим кои фактори трябва да се вземат предвид по време на моделирането на данни и ще видим как те влияят на производителността на база данни като цяло.
Много пъти ще трябва да разположите клъстер от вашата база данни като един от начините за увеличаване на наличността на данни. С добре проектиран модел на данни можете по-ефективно да разпределяте дейности към разделен клъстер, като по този начин намалявате операциите с пропускателна способност, насочени към един mongod екземпляр. Основните фактори, които трябва да се вземат предвид при моделирането на данни, включват:
- Мащабируемост
- Атомност
- Ефективност и използване на данни
- Разделяне
- Индексиране
- Оптимизация на хранилището
- Структура и растеж на документа
- Жизнен цикъл на данните
1. Мащабируемост
Това е увеличаване на натоварването на приложение, движещо се от увеличения трафик. Много приложения винаги очакват увеличаване на броя на своите потребители. Когато има толкова много потребители, обслужвани от един екземпляр на база данни, производителността не винаги отговаря на очакванията. Като мениджър на база данни, вие имате мандат да проектирате тази DBM така, че колекциите и обектите от данни да се моделират въз основа на настоящите и бъдещите изисквания на приложението. Структурата на базата данни като цяло трябва да бъде представителна, за да се подобри лесният процес на репликация и разделяне. Когато имате повече фрагменти, операциите по запис се разпределят между тези фрагменти, така че за всяка актуализация на данни се извършва в рамките на сегмента, съдържащ тези данни, вместо да се търси в един голям набор от данни, за да се направи актуализация.
2. Атомност
Това се отнася до успех или неуспех на операция като единична единица. Например може да имате операция за четене, която включва операция за сортиране след извличане на резултата. Ако операцията по сортиране не се изпълни правилно, цялата операция няма да премине към следващия етап.
Атомните транзакции са поредица от операции, които не са нито делими, нито намаляващи, следователно възникват като единични обекти или се провалят като единични операции. Версиите на MongoDB преди 4.0 поддържат операции за запис като атомарни процеси на ниво един документ. С версия 4.0 вече може да се реализират транзакции с множество документи. Модел на данни, който подобрява атомарните операции, има тенденция да има голяма производителност по отношение на латентността. Закъснението е просто продължителността, в рамките на която се изпраща заявка за операция и когато се връща отговор от базата данни. За да бъде секантен, лесно е да се актуализират данни, които са вградени в един документ, а не този, който е препратен.
Нека например разгледаме набора от данни по-долу
{
childId : "535523",
studentName : "James Karanja",
parentPhone : 704251068,
age : 12,
settings : {
location : "Embassy",
address : "420 01",
bus : "KAZ 450G",
distance : "4"
}
}
Ако искаме да актуализираме възрастта, като я увеличим с 1 и променим местоположението на Лондон, можем да направим:
db.getCollection(‘students’).update({childId: 535523},{$set:{'settings.location':'London'}, $inc:{age:1}}).
Ако например операцията $set е неуспешна, тогава автоматично операцията $inc няма да бъде приложена и като цяло цялата операция е неуспешна.
От друга страна, нека разгледаме референтните данни, като например, че има 2 колекции, едната за студенти, а другата за настройки.
Студентска колекция
{
childId : "535523",
studentName : "James Karanja",
parentPhone : 704251068,
age : 12
}
Колекция от настройки
{
childId : "535523",
location : "Embassy",
address : "420 01",
bus : "KAZ 450G",
distance : "4"
}
В този случай можете да актуализирате стойностите за възрастта и местоположението с отделни операции за запис, т.е.
db.getCollection(‘students’).update({childId: 535523},{$inc:{age:1}})
db.getCollection('settings’).update({childId: 535523 } , {$set: { 'settings.location':'London'}})
Ако една от операциите е неуспешна, това не засяга непременно другата, тъй като те се извършват като различни обекти.
Транзакции за множество документи
С MongoDB версия 4.0 вече можете да извършвате множество транзакции с документи за набори от реплики. Това подобрява производителността, тъй като операциите се издават към редица колекции, бази данни и документи за бърза обработка. Когато транзакцията е завършена, данните се запазват, докато ако нещо се обърка и транзакцията се провали, направените промени се отхвърлят и транзакцията обикновено ще бъде прекратена. Няма да има актуализация на наборите реплики по време на транзакцията, тъй като операцията се вижда отвън само когато транзакцията е напълно ангажирана.
Колкото и да можете да актуализирате множество документи в множество транзакции, това идва с намалена производителност в сравнение с записването на един документ. Освен това този подход се поддържа само за механизма за съхранение на WiredTiger, поради което е недостатък за механизмите за съхранение в паметта и MMAPv1.
3. Производителност и използване на данни
Приложенията са проектирани по различен начин, за да отговарят на различни цели. Има някои, които служат само за текущите данни като приложения за новини за времето. В зависимост от структурата на приложението, човек трябва да може да проектира съответна оптимална база данни за сървър на необходимия случай на употреба. Например, ако някой разработи приложение, което извлича най-новите данни от базата данни, използването на ограничена колекция ще бъде най-добрият вариант. Ограничената колекция подобрява операцията с висока пропускателна способност точно като буфер, така че когато се използва разпределеното пространство, най-старите документи се презаписват и документите могат да бъдат извлечени в реда, в който са били вмъкнати. Като се има предвид извличането на поръчка за вмъкване, няма да е необходимо да се използва индексиране, а липсата на служебни индекси също ще подобри пропускателната способност на запис. С ограничена колекция, свързаните данни са доста малки, тъй като могат да се поддържат в RAM за известно време. Временните данни в този случай се съхраняват в кеша, който се чете доста, отколкото се записва, което прави операцията за четене доста бърза. Въпреки това, ограничената колекция идва с някои недостатъци, като например, не можете да изтриете документ, освен ако не изхвърлите цялата колекция, всяка промяна в размера на документ ще провали операцията и накрая не е възможно да се раздели ограничена колекция.
Различни аспекти са интегрирани в моделирането на данни на база данни в зависимост от нуждите от използване. Както се вижда, приложенията за отчети ще бъдат по-интензивни за четене, следователно дизайнът трябва да бъде по начин, който подобрява пропускателната способност на четене.
4. Раздробяване
Производителността чрез хоризонтално мащабиране може да бъде подобрена чрез разделяне, тъй като работните натоварвания за четене и запис се разпределят между членовете на клъстера. Разгръщането на клъстер от фрагменти има тенденция да разделя базата данни на множество малки колекции с разпределени документи в зависимост от някакъв ключ на фрагмента. Трябва да изберете подходящ ключ за разделяне, който може да предотврати изолирането на заявката, освен да увеличи капацитета за запис. По-добрият избор обикновено включва поле, което присъства във всички документи в рамките на целевата колекция. С разделянето има увеличено съхранение, тъй като с нарастването на данните се установяват повече фрагменти, които да държат подмножество от този клъстер.
5. Индексиране
Индексирането е един от най-добрите подходи за подобряване на натоварването при писане, особено когато полетата се срещат във всички документи. Когато правите индексиране, трябва да имате предвид, че всеки индекс ще изисква 8KB пространство за данни. Освен това, когато индексът е активен, той ще заеме известно дисково пространство и памет, поради което трябва да се проследява за планиране на капацитета.
Severalnines Станете DBA на MongoDB – Пренасяне на MongoDB в Производството Научете какво трябва да знаете, за да внедрите, наблюдавате, управлявате и мащабирате MongoDB Изтеглете безплатно6. Оптимизиране на съхранение
Много малки документи в колекция ще заемат повече място, отколкото когато имате няколко документа с подвградени документи. Следователно при моделиране трябва да се групират свързаните данни преди съхранение. С няколко документа може да се извърши операция с база данни с няколко заявки, следователно намален произволен достъп до диск и ще има по-малко свързани ключови записи в съответния индекс. Следователно съображенията в този случай ще бъдат:използвайте вграждане, за да имате по-малко документи, което от своя страна намалява разходите за всеки документ. Използвайте по-кратки имена на полета, ако в колекцията са включени по-малко полета, за да не правите значителни допълнителни разходи за документи. По-кратките имена на полета намаляват изразителността, т.е.
{ Lname : "Briston", score : 5.9 }
ще спести 9 байта на документ, вместо да използва
{ last_name : "Briston", high_score: 5.9 }
Използвайте полето _id изрично. По подразбиране клиентите на MongoDB добавят поле _id към всеки документ, като присвояват уникален 12-байтов ObjectId за това поле. Освен това полето _id ще бъде индексирано. Ако документите са доста малки, този сценарий ще заема значително място в общия номер на документа. За оптимизиране на съхранението ви е позволено да посочите стойността за полето _id изрично, когато вмъквате документи в колекция. Уверете се обаче, че стойността е уникално идентифицирана, защото служи като основен ключ за документи в колекцията.
7. Структура на документа и растеж
Това се случва в резултат на операцията на натискане, при която поддокументите се избутват в поле на масив или когато към съществуващ документ се добавят нови полета. Разрастването на документите има някои пречки, т.е. за ограничена колекция, ако размерът е променен, операцията автоматично ще се провали. За MMAPv1 механизъм за съхранение, версиите преди 3.0 ще преместят документа на диска, ако размерът на документа е надвишен. Въпреки това, в по-късните версии от 3.0 има концепция за мощност на 2 размера, която намалява шансовете за такова преразпределение и позволява ефективно повторно използване на освободеното пространство за записи. Ако очаквате вашите данни да нарастват, може да искате да преработите модела си на данни, за да използвате препратки между данни в различни документи, вместо да използвате денормализиран модел на данни. За да избегнете растежа на документите, можете също да помислите за използването на стратегия за предварително разпределение.
8. Жизнен цикъл на данните
За приложение, което използва само наскоро вмъкнатите документи, помислете за използването на ограничена колекция, чиито функции бяха обсъдени по-горе.
Можете също да зададете функцията Time to Live за вашата колекция. Това е доста приложимо за токени за достъп във функцията за нулиране на парола за приложения.
Време за живот (TTL)
Това е настройка за събиране, която позволява на mongod автоматично да премахва данни след определено време. По подразбиране тази концепция се прилага за генерирани от машина данни за събития, регистрационни файлове и информация за сесиите, които трябва да се съхраняват за ограничен период от време.
Пример:
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } )
Създадохме индекс createdAt и посочихме някаква стойност на expireAfterSeconds от 3600, което е 1 час след момента на създаване. Сега, ако вмъкнем документ като:
db.log_events.insert( {
"createdAt": new Date(),
"logEvent": 2,
"logMessage": "This message was recorded."
} )
Този документ ще бъде изтрит след 1 час от момента на вмъкване.
Можете също да зададете конкретен час, когато искате документът да бъде изтрит. За да направите това, първо създайте индекс, т.е.:
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
Сега можем да вмъкнем документ и да посочим времето, когато трябва да бъде изтрит.
db.log_events.insert( {
"expireAt": new Date(December 12, 2018 18:00:00'),
"logEvent": 2,
"logMessage": "Success!"
} )
Този документ ще бъде изтрит автоматично, когато стойността на expireAt е по-стара от броя секунди, посочени в expireAfterSeconds, т.е. 0 в този случай.
Заключение
Моделирането на данни е обширно начинание за всеки дизайн на приложение с цел подобряване на производителността на неговата база данни. Преди да вмъкнете данни във вашата db, помислете за нуждите на приложението и кои са най-добрите модели на модела на данни, които трябва да приложите. Освен това, важни аспекти на приложенията не могат да бъдат реализирани, докато не се приложи правилен модел на данни.