Модель данных - moevm/nosql-2017-bandmap GitHub Wiki
В одной коллекции хранятся сущности разных профилей, т.к. по сути это одно и то же, но для каждого типа добавляются специфические поля.
Модель документа музыканта:
{
"_id": ObjectId,
"log": string //логин (уникальный)
"user": string // логин пользователя-создателя
"n": string //имя
"t": int32 //тип профиля (Enum)
"loc": GeoJsonPoint //координаты местоположения
"a": string //адрес
"p": ObjectId, //фото, ссылка на бинарный файл в gridfs,
"pm": ObjectId, //мини-фото, ссылка на бинарный файл в gridfs,
"ytv":[
{
"_id":string// id видео на ютуб или вимео
"vn": string // кастомное имя видео
"vd": string // кастомное описание видео
},...
], // список вложенных объектов описания видео ()
"d": string, //описание профиля
"vk": string, //ссылка на ВКонтакте
"ia": boolean, //флаг активности профиля
"fav": Array, //пользователи, отметившие данный профиль как избранное
"instr": int32, //тип инструмента (Enum)
"sex": int32, //пол музыканта (Enum)
"band": Array //ссылки на группы, в которых играет музыкант
}
- Оценка удельного объема информации, хранимой в модели (сколько потребуется памяти, чтобы сохранить)
-
"user"- string - содержит в среднем 10 символов - 10 байт
-
"n"- string - содержит в среднем 10 символов - 10 байт
-
"t"- int32 4 байта
-
"loc" - GeoJsonPoint - содержит два значения double - 16 байт
-
"a" - string - содержит в среднем 20 символов - 20 байт
-
"p" - ObjectId - 30 байт
-
"pm"- ObjectId - 30 байт
-
"ytv":[
{ 8.1. "_id"- string - содержит в среднем 20 символов - 20 байт 8.2. "vn"- string - содержит в среднем 20 символов - 20 байт 8.3. "vd"- string - содержит в среднем 20 символов - 20 байт },...
], - занимает 60*n байт. где n - количество вложенных объектов. Будем считать что 300 байт.
-
"d" - string - содержит в среднем 100 символов - 100 байт
-
"vk" -string- содержит в среднем 17 символов - 17 байт
-
"ia" - boolean - 1 байт
-
"fav" - Array - 10*n байт, где n - размер списка. Будем считать что 100 байт.
-
"instr" - int32 4 байта
-
"sex" - int32 4 байта
-
"band" - Array - 30*n байт, где n - размер списка. Будем считать что 120 байт в среднем.
Размер, который занимает коллекция из 15 элементов, равен 12561 байт, что равняется в среднем 837,4 байта на одну сущность.
Коллекция в которой 20000 элементов занимает 16 мегабайт
Из этого можно сделать вывод, что размер базы дынных увеличивается линейно при добавлении новых данных
Такие же данные могли бы выглядеть в виде SQL-таблиц следующим образом
-
Оценка удельного объема информации, хранимой в модели (сколько потребуется памяти, чтобы сохранить)
-
Таблица Band(2609223 байт)
-
"id" - счетчик, 8 байт
-
"EntityType" - TINYINT, 1 байта
-
"Login" - varchar(20), 20 байт
-
"Latitude" - DOUBLE - 8 байт
-
"Longitude" - DOUBLE - 8 байт
-
"Photo"
6.1. "PhotoFileData" - blob - 2 Мб
6.2. "PhotoFileName" - varchar(10), 10 байт
6.3. "PhotoFileType" - varchar(3), 3 байт
-
"PhotoMini"
7.1. "PhotoMiniFileData" - blob - 500 Кб
7.2. "PhotoMiniFileName" - varchar(10), 10 байт
7.3. "PhotoMiniFileType" - varchar(3), 3 байт
- Таблица Musician(2609263 байт)
-
"id" - счетчик, 8 байт
-
"EntityType" - TINYINT, 1 байта
-
"Login" - varchar(20), 20 байт
-
"Latitude" - DOUBLE - 8 байт
-
"Longitude" - DOUBLE - 8 байт
-
"Photo"
6.1. "PhotoFileData" - blob - 2 Мб
6.2. "PhotoFileName" - varchar(10), 10 байт
6.3. "PhotoFileType" - varchar(3), 3 байт
-
"PhotoMini"
7.1. "PhotoMiniFileData" - blob - 500 Кб
7.2. "PhotoMiniFileName" - varchar(10), 10 байт
7.3. "PhotoMiniFileType" - varchar(3), 3 байт
-
"Address" - varchar(20), 20 байт
-
"Website" - varchar(20), 20 байт
- Таблица Shop(2609263 байт)
-
"id" - счетчик, 8 байт
-
"EntityType" - TINYINT, 1 байта
-
"Login" - varchar(20), 20 байт
-
"Latitude" - DOUBLE - 8 байт
-
"Longitude" - DOUBLE - 8 байт
-
"Photo"
6.1. "PhotoFileData" - blob - 2 Мб
6.2. "PhotoFileName" - varchar(10), 10 байт
6.3. "PhotoFileType" - varchar(3), 3 байт
-
"PhotoMini"
7.1. "PhotoMiniFileData" - blob - 500 Кб
7.2. "PhotoMiniFileName" - varchar(10), 10 байт
7.3. "PhotoMiniFileType" - varchar(3), 3 байт
-
"Address" - varchar(20), 20 байт
-
"Website" - varchar(20), 20 байт
- Таблица Studio(2609263 байт)
-
"id" - счетчик, 8 байт
-
"EntityType" - TINYINT, 1 байта
-
"Login" - varchar(20), 20 байт
-
"Latitude" - DOUBLE - 8 байт
-
"Longitude" - DOUBLE - 8 байт
-
"Photo"
6.1. "PhotoFileData" - blob - 2 Мб
6.2. "PhotoFileName" - varchar(10), 10 байт
6.3. "PhotoFileType" - varchar(3), 3 байт
-
"PhotoMini"
7.1. "PhotoMiniFileData" - blob - 500 Кб
7.2. "PhotoMiniFileName" - varchar(10), 10 байт
7.3. "PhotoMiniFileType" - varchar(3), 3 байт
-
"Address" - varchar(20), 20 байт
-
"Website" - varchar(20), 20 байт
- Таблица Rehearsal(2609263 байт)
-
"id" - счетчик, 8 байт
-
"EntityType" - TINYINT, 1 байта
-
"Login" - varchar(20), 20 байт
-
"Latitude" - DOUBLE - 8 байт
-
"Longitude" - DOUBLE - 8 байт
-
"Photo"
6.1. "PhotoFileData" - blob - 2 Мб
6.2. "PhotoFileName" - varchar(10), 10 байт
6.3. "PhotoFileType" - varchar(3), 3 байт
-
"PhotoMini"
7.1. "PhotoMiniFileData" - blob - 500 Кб
7.2. "PhotoMiniFileName" - varchar(10), 10 байт
7.3. "PhotoMiniFileType" - varchar(3), 3 байт
-
"Address" - varchar(20), 20 байт
-
"Website" - varchar(20), 20 байт
Документы являются оптимальными в данном случае по сравнению с реляционными данными по следующим причинам:
- позволяют хранить данные похожей, но все же отличной, структуры в одном хранилище (коллекции). В случае реляционной базы надо было бы создавать по таблице на каждый профиль. При условии что количество профилей будет расти, будет увеличиваться количество запросов, а так же сложность поддержки при сложных агрегирующих, фильтрующих запросах. При документах всегда выполняется один запрос с любой сложностью.
Запрос поиска по тексту профилей всех типов:
db.bases.find({n:{$regex: /Кирилл/}})
Агрегирующий запрос статистик по количеству каждого типа сущности:
db.bases.aggregate({$group: { _id: "$t", count: {$sum: 1} } })
- позволяет хранить вложенные элементы, массивы. Видео у музыкантов и групп в случае реляционной модели необходимо было бы создавать дополнительные таблицы для хранение этих данных и использовать затратные операции JOIN, или как в примере выше хранить данные в видео строки в полях сущностей, что было бы сложно поддерживать и при каждом запросе дополнительно сериализовать.
Запрос профиля со всей информацией о нем, включая вложенные элементы:
db.bases.find({log:"rogulenkoko"})
- позволяет делать оптимизированную выборку используя индексы геолокации и специальный тип MongoDB 2dsphere, с помощью которого при запросе не будет просматриваться вся коллекция, в отличие от реляционных баз, а будет выбирать только профили, подходящие под сложные географические критерии.
Запрос первых 20 профилей в кругу радиуса 1 радиан с центром в точке (50,30):
db.bases.find({ loc: { $nearSphere: [50, 30], $minDistance: 0, $maxDistance: 1 } }).limit(20)
- позволяет хранить и запрашивать данные через специальный инструмент GridFs, который хранит больший бинарные данные в виде небольших блоков. Запросы оптимизированы сравнительно с sql базами. Преимуществом от хранения файлов в фаловой системе является удобство переноса данных по разным серверам и обновление данных
Реляционными данные являются оптимальными в данном случае по сравнению с документам по следующим причинам:
- связи профилей музыкантов и групп, позволяет каскадно удалять сущности и их связи
По выше указанным причинам была выбрана нереляционная база данных MongoDb, так как MongoDb имеет большее количество достоинств и меньшее количество недостатков в данном случае в отличие от любой реляционной СУБД.
В одной колллекции хранятся статистики (по одному документу на каждый тип статистики)
Модель документа статистики по типам профилей
{
"_id": ObjectId,
"d": Date //дата обновления
"t": int32 //тип статистики (Enum)
"cs": [{
"key":int32, // тип профиля
"count": int32 //количество профилей данного типа
},...]
}
-
"_id"- ObjectId - 30 байт
-
"d" - Date - 5 байт
-
"t" - int32 - 4 байт
-
"cs": [{
4.1. "key" - int32 - 4 байт
4.2. "count"- int32 - 4 байт },...] - 24 байт
вес таблицы занимает 63 байта
Такие же данные могли бы выглядеть в виде SQL-таблиц следующим образом
- CityStatistics(16 байт)
-
"id" - счетчик, 8 байт
-
"UpdateData" - DATE - 3 байта
-
"Type" - TINYINT - 1 байт
-
"CountStatistics" - INT - 4 байт
- EntityTypeStatistics(16 байт)
-
"id" - счетчик, 8 байт
-
"UpdateData" - DATE - 3 байта
-
"Type" - TINYINT - 1 байт
-
"CountStatistics" - INT - 4 байт
- TopProfileStatistics(16 байт)
-
"id" - счетчик, 8 байт
-
"UpdateData" - DATE - 3 байта
-
"Type" - TINYINT - 1 байт
-
"CountStatistics" - INT - 4 байт
Документы являются оптимальными в данном случае по сравнению с реляционными данными по следующим причинам:
-
позволяют хранить данные похожей, но все же отличной (отличается тип ключевого поля количественной статистики), структуры в одном хранилище (коллекции).
-
позволяет забрать все виды статистики одним запросом, что в реляционной надо было бы делать в n запросов, где n-количество типов статистик
-
позволяет хранить вложенные элементы (специфические количественные статистики для каждого типа статистик). В некоторых могут хранится дополнительные данные, например для статистики "Топ профилей" необходимо хранить логин профиля и его тип
-
для каждого типа статистики будет хранится только один экземпляр (за весь период). Поэтому нецелесообразно было бы делать n таблиц, зная, что в каждой из них будет по одной записи