-
Notifications
You must be signed in to change notification settings - Fork 0
Модель данных
- id (string): Уникальный идентификатор пользователя.
- email (string): Электронная почта пользователя.
- created_at (timestamp): Дата регистрации пользователя.
- updated_at (timestamp): Дата и время последнего обновления профиля.
- avatar_url (string): Ссылка на аватар пользователя.
- id (string): Уникальный идентификатор точки.
- coordinates (GeoJSON): Координаты точки на карте.
- description (string): Описание точки.
- availability (number): Уровень доступности.
- id (string): Уникальный идентификатор маршрута.
- points (array of references): Массив ссылок на точки маршрута (Point).
- author (reference to User): Идентификатор автора маршрута.
- popularity_score (number): Рейтинг маршрута на основе обращений в поддержку и популярности.
- id (string): Уникальный идентификатор озера.
- name (string): Название озера.
- description (string): Описание озера.
- coordinates_boundary (GeoJSON): Координаты границ озера.
- availability_score (number): Оценка доступности озера (количество доступных к нему маршрутов).
- max_depth (number): Максимальная глубина озера.
- inflowing_rivers (array of strings): Список названий рек, впадающих в озеро.
- outflowing_rivers (array of strings): Список названий рек, вытекающих из озера.
- salinity (number): Уровень солености озера.
- id (string): Уникальный идентификатор обращения.
- author (reference to User): Автор обращения.
- route_reference (reference to Route): Связь с маршрутом, если обращение касается маршрута.
- lake_reference (reference to Lake): Связь с озером, если обращение касается озера.
- subject (string): Тема обращения.
- text (string): Текст отзыва.
- created_at (timestamp): Дата обращения.
- photo (base64): Фото, закодированное в base64.
Пусть:
- ( N ) — количество пользователей.
- В среднем на один маршрут приходится 10 точек.
- Сообщения в поддержку пишет каждый 10-й пользователь.
- Каждый пользователь совершает около 10 маршрутов.
- Озер всего 100.
- id (строка, 8 байт)
- email (строка, 50 байт)
- дата регистрации (дата, 8 байт)
- дата обновления профиля (timestamp, 8 байт)
- аватар (строка, 100 байт)
Итого:
- ( 8 + 50 + 8 + 8 + 100 = 174 ) байта на пользователя
- id (строка, 8 байт)
- координаты (GeoJSON, 16 байт)
- описание (строка, 100 байт)
- доступность (целое число, 4 байта)
Итого:
- ( 8 + 16 + 100 + 4 = 128 ) байт на точку
- id (строка, 8 байт)
- список точек (массив ссылок на узлы точек, в среднем 10 ссылок, по 8 байт каждая): ( 10 \times 8 = 80 ) байт
- автор (ссылка на User, 8 байт)
- рейтинг (число, 4 байта)
Итого:
- ( 8 + 80 + 8 + 4 = 100 ) байт на маршрут
- id (строка, 8 байт): Уникальный идентификатор озера.
- name (строка, 50 байт): Название озера.
- description (строка, 200 байт): Описание озера.
- coordinates_boundary (GeoJSON, 50 байт): Координаты границ озера.
- availability_score (число, 4 байта): Оценка доступности озера.
- max_depth (число, 4 байта): Максимальная глубина озера.
- inflow_rivers (строка[], 100 байт): Список впадающих рек (примерное значение для массива строк).
- outflow_rivers (строка[], 100 байт): Список вытекающих рек.
- salinity (число, 4 байта): Соленость озера.
Итого для Lake: 8 + 50 + 200 + 50 + 4 + 4 + 100 + 100 + 4 = 520 { байт}
- id (строка, 8 байт)
- автор (ссылка на User, 8 байт)
- связь с маршрутом (ссылка на Route, 8 байт)
- связь с озером (ссылка на Lake, 8 байт)
- тема (строка, 50 байт)
- отзывы (массив объектов):
- текст (строка, 200 байт)
- дата (timestamp, 8 байт)
- фото (base64, 2 796 202 байта)
- User-Creates-Route
- Route-Belongs-To-User
- Route-Includes-Point
- Point-Belongs-To-Route
- User-Creates-SupportRequest
- SupportRequest-Belongs-To-User
- Lake-Connected-To-SupportRequest
- Route-Connected-To-SupportRequest
- User-Creates-Point
- Point-Belongs-To-Lake
- Point-Includes-Lake
Теперь давайте подытожим объем памяти для всех указанных связей: 37+37+37+37+37+37+39+39+37+37+37= 398 байт
- Пользователь (User): 174 байта
- Точки (Point): 12800 байт (10 точек по 128 байт в 10 разных маршрутах)
- Маршруты (Route): 1000 байт (10 маршрутов по 100 байт)
- Обращения в поддержку (SupportRequest): 2,796,492 байт делим на 10
- **Связи: 398 байт
- Озера: 520 байт
Итого:
- Общий объем данных на одного пользователя = 174 + 12800 + 1000 + 279,649 + 398 + 520*100 = 346,047 байта = 0,346 мбайт
Итоговая формула объема данных:
Объем = N * 0,346 мбайт
Таким образом, объем данных напрямую зависит от количества пользователей ( N ).
В графовых СУБД, таких как Neo4j, избыточность может возникать из-за различных факторов, включая индексы, ссылки и дублирующиеся данные. Предположим, что избыточность составляет 30%. Тогда общий объем данных с учетом избыточности можно рассчитать следующим образом:
- Общий объем данных без учета избыточности: ( 293,623 ) байта на пользователя.
- Избыточность: ( 30% = 0.3 ).
Расчет общего объема с учетом избыточности:
Итак, общий объем данных с учетом избыточности составляет примерно ( 382,710 ) байт.
Основные факторы роста модели: Количество пользователей (N):
- При увеличении числа пользователей (N) возрастает объем хранимых данных, таких как учетные записи пользователей и обращения в поддержку. Процент обращений в поддержку:
- При росте числа пользователей также возрастает количество обращений в службу поддержки. Увеличение количества отзывов очень сильно влияет на количество данных.
- Получение всех пользователей
MATCH (u:User)
RETURN u.id AS userId, u.email AS email, u.registration_date AS registrationDate
- Поиск точки по идентификатору
MATCH (p:Point {id: 'point001'})
RETURN p.id AS pointId, p.coordinates AS coordinates, p.description AS description, p.availability AS availability
- Получение всех маршрутов с их точками
MATCH (r:Route)-[:CONTAINS]->(p:Point)
RETURN r.id AS routeId, collect(p.id) AS points
- Получение всех точек, доступных для маршрута
MATCH (r:Route {id: 'route001'})-[:CONTAINS]->(p:Point)
RETURN p.id AS pointId, p.description AS description
- Добавление нового пользователя
CREATE (u:User {id: 'user003', email: '[email protected]', registration_date: '2024-02-03'})
RETURN u
- Добавление новой точки
CREATE (p:Point {id: 'point003', coordinates: '59.9342, 30.3351', description: 'Невский проспект', availability: ['Доступно']})
RETURN p
- Создание маршрута с точками
CREATE (r:Route {id: 'route003'})
WITH r
CREATE (p1:Point {id: 'point001'})-[:CONTAINS]->(r)
CREATE (p2:Point {id: 'point002'})-[:CONTAINS]->(r)
RETURN r
- Получение обращений в поддержку по пользователю
MATCH (u:User)-[:MADE]->(sr:SupportTicket)
WHERE u.id = 'user001'
RETURN sr.id AS supportRequestId, sr.subject AS subject, sr.reviews AS reviews
- Получение статистики обращений в поддержку
MATCH (sr:SupportTicket)
RETURN COUNT(sr) AS totalSupportTickets
- Обновление темы обращения в поддержку
MATCH (sr:SupportTicket {id: 'support001'})
SET sr.subject = 'Обновленная тема обращения'
RETURN sr
- Поиск самых популярных маршрутов и отзывов (по количеству обращений в поддержку)
*MATCH (route:Route)<-[:route_reference]-(request:SupportTicket)
*RETURN route.id AS RouteID, COUNT(request) AS SupportTicketsCount
*ORDER BY SupportTicketsCount DESC
*LIMIT 10;
- Самые длинные маршруты
MATCH (route:Route)
WITH route, SIZE(route.points) AS pointCount
RETURN route.id AS RouteID, pointCount
ORDER BY pointCount DESC
LIMIT 10;
- Самые недоступные / доступные озера (с точки зрения близости к ним маршрутов) недоступные:
MATCH (lake:Lake)<-[:points]-(route:Route)
WITH lake, COUNT(route) AS RouteCount
RETURN lake.id AS LakeID, RouteCount
ORDER BY RouteCount ASC LIMIT 10;
доступные:
MATCH (lake:Lake)<-[:points]-(route:Route)
WITH lake, COUNT(route) AS RouteCount
RETURN lake.id AS LakeID, RouteCount
ORDER BY RouteCount DESC LIMIT 10;
- Подсчет доступности в виде числа и запись его в avialability для всех озер
MATCH (route:Route)-[:points]->(point:Point)
WHERE point.coordinates.latitude > 59.5 AND point.coordinates.latitude < 60.0
WITH route, point
MATCH (lake:Lake)
WHERE distance(point.coordinates, lake.coordinates_boundary) < 100 // Ближе 100 метров
WITH lake, COUNT(route) AS AccessibleRoutesCount
SET lake.availability_score = AccessibleRoutesCount;
RETURN lake.id AS LakeID, AccessibleRoutesCount;
- Юзкейс: Регистрация Количество запросов: 1 Запрос: Добавление нового пользователя.
- Юзкейс: Вход Количество запросов: 1 Запрос: Поиск пользователя по email и паролю (при условии, что хранится хэш пароля).
- Юзкейс: Карта с Точками Количество запросов: 1 Запрос: Получение всех точек на карте.
- Юзкейс: Импорт/экспорт Количество запросов: 2: Импорт точек из документа (может потребоваться несколько запросов в зависимости от количества точек). Экспорт точек доступности.
- Юзкейс: Карта Количество запросов: 1 Запрос: Получение точки по ее координатам или описанию.
- Юзкейс: Поддержка Количество запросов: 1 Запрос: Создание нового обращения в поддержку.
- Юзкейс: Поиск по атрибутам Количество запросов: 1 Запрос: Поиск точек по заданным атрибутам.
- Юзкейс: Настройки Количество запросов: 2: олучение текущих настроек пользователя. Обновление настроек (например, языка или пароля).
- Юзкейс: Смена пароля Количество запросов: 1 Запрос: Обновление пароля пользователя. RETURN u
- Юзкейс: Статистика Количество запросов: 1 Запрос: Получение статистики обращений в поддержку.
Суммарно количество задействованных коллекций (узлов) будет зависеть от конкретных действий пользователя, в среднем:
- User: 1
- Point: X (количество точек, которые находятся в базе данных)
- Route: Y (количество маршрутов)
- SupportRequest: 1
- Lakes: Z ( количество всех озёр)
- Назначение: Хранение информации о пользователях, которые могут создавать маршруты и оставлять обращения в поддержку.
-
Поля:
-
id:
SERIAL
— Уникальный идентификатор пользователя, занимает 4 байта. -
email:
VARCHAR(50)
— Электронная почта пользователя, занимает до 50 байт. -
created_at:
TIMESTAMP
— Дата регистрации пользователя, занимает 8 байт. -
updated_at:
TIMESTAMP
— Дата последнего обновления профиля, занимает 8 байт. -
avatar:
VARCHAR(100)
— Ссылка на аватар пользователя, занимает до 100 байт.
-
id:
- Назначение: Хранение информации о точках, которые могут быть добавлены в маршруты.
-
Поля:
-
id:
SERIAL
— Уникальный идентификатор точки, занимает 4 байта. -
coordinates:
VARCHAR(20)
— Координаты точки на карте, занимает до 20 байт. -
description:
VARCHAR(100)
— Описание точки, занимает до 100 байт. -
availability:
TEXT[]
— Список атрибутов доступности, массив строк (занимает переменное количество байт).
-
id:
- Назначение: Хранение информации о маршрутах, созданных пользователями, включая точки, которые входят в маршрут.
-
Поля:
-
id:
SERIAL
— Уникальный идентификатор маршрута, занимает 4 байта. -
user_id:
INTEGER
— Идентификатор пользователя, создающего маршрут (ссылка на таблицу User), занимает 4 байта. -
name:
VARCHAR(100)
— Название маршрута, занимает до 100 байт. -
points:
INTEGER[]
— Список идентификаторов точек (ссылки на таблицу Point), массив целых чисел (занимает переменное количество байт).
-
id:
- Назначение: Хранение информации о запросах пользователей в службу поддержки.
-
Поля:
-
id:
SERIAL
— Уникальный идентификатор обращения, занимает 4 байта. -
user_id:
INTEGER
— Идентификатор пользователя, отправляющего обращение (ссылка на таблицу User), занимает 4 байта. -
subject:
VARCHAR(50)
— Тема обращения, занимает до 50 байт. -
reviews:
JSONB
— Отзывы по обращению, хранящиеся в формате JSON (занимает переменное количество байт).
-
id:
- Назначение: Хранение информации об озёрах, которые могут быть связаны с маршрутами.
-
Поля:
-
id:
SERIAL
— Уникальный идентификатор озера, занимает 4 байта. -
name:
VARCHAR(100)
— Название озера, занимает до 100 байт. -
coordinates_boundary:
VARCHAR(50)
— Координаты границ озера, занимает до 50 байт. -
description:
VARCHAR(200)
— Описание озера, занимает до 200 байт. -
availability_score:
INTEGER
— Оценка доступности озера, занимает 4 байта. -
max_depth:
INTEGER
— Максимальная глубина озера, занимает 4 байта. -
inflow_rivers:
TEXT
— Список впадающих рек, текстовый формат (размер зависит от содержимого). -
outflow_rivers:
TEXT
— Список вытекающих рек, текстовый формат (размер зависит от содержимого). -
salinity:
INTEGER
— Соленость озера, занимает 4 байта.
-
id:
- Назначение: Хранение информации о связи между маршрутами и озёрами.
-
Поля:
-
route_id:
INTEGER
— Идентификатор маршрута (ссылка на таблицу Route). -
lake_id:
INTEGER
— Идентификатор озера (ссылка на таблицу Lake). - PRIMARY KEY (route_id, lake_id) — Составной первичный ключ.
-
route_id:
Вычислим средний размер объектов каждой сущности:
-
User:( 4 + 50 + 8 + 8 + 100 = 170 ) байт
-
Point:( 4 + 20 + 100 + 30 = 154 ) байта
-
Route:( 4 + 4 + 100 + 96 = 204 ) байта
-
SupportTicket:( 4 + 4 + 50 + 2796202 = 2796260 ) байт
-
Lake: (4 + 100 + 20 + 100 + 4 + 4 + 100 + 100 + 4 = 436 ) байта
-
RouteLake:( 4 + 4 = 8 ) байт
Пусть в среднем на один маршрут приходится 10 точек, а также сообщения в поддержку пишет каждый 10 человек, ну и примерно каждый пользователь совершает 10 маршрутов и всего 100 озер.
При количестве пользователей, равном N, получаем: Итог = 326720*N байт
В реляционной таблице избыточным можно считать внешний ключ т.е. все id - 4 байта. И при расчете избыточности она будет не значительной.
Модель будет линейно расти при создании объектов Point, Route и User, но при огромном наплыве SupportRequest могут появится проблемы.
INSERT INTO Users (email, registration_date) VALUES ('[email protected]', NOW());
INSERT INTO Points (coordinates, description, availability) VALUES ('59.9343,30.3351', 'Площадь Восстания', '["доступен для пешеходов", "доступен для велосипедистов"]');
INSERT INTO Routes (points) VALUES (ARRAY[1, 2]);
INSERT INTO SupportRequests (support_request_id, text, date, photo) VALUES (1, 'Маршрут не отображается', NOW(), 'base64image...');
SELECT * FROM Users;
SELECT * FROM Points WHERE availability @> '["доступен для пешеходов"]';
SELECT r.id AS route_id, p.id AS point_id, p.description FROM Routes r JOIN unnest(r.points) AS point_id ON TRUE JOIN Points p ON p.id = point_id;
UPDATE Users SET email = '[email protected]' WHERE id = 1;
UPDATE Points SET description = 'Новая площадь Восстания' WHERE id = 1;
DELETE FROM Points WHERE id = 1;
DELETE FROM SupportTickets WHERE id = 1;
Регистрация нового пользователя 1 запрос для вставки данных о пользователе Добавление новой точки 1 запрос для вставки данных о точке.
Создание маршрута 1 запрос для вставки данных о маршруте (включает список точек).
Отправка обращения в поддержку 1 запрос для вставки данных о запросе в поддержку.
Добавление отзыва на обращение 1 запрос для вставки данных о отзыве.
Получение всех пользователей 1 запрос для выборки всех пользователей.
Получение всех точек доступности 1 запрос для выборки всех точек.
Получение маршрута с точками 1 запрос для выборки маршрута с точками.
Получение обращений пользователя 1 запрос для выборки всех обращений пользователя.
Обновление информации о пользователе 1 запрос для обновления данных о пользователе.
Обновление информации о точке 1 запрос для обновления данных о точке.
Обновление обращения в поддержку 1 запрос для обновления данных о запросе.
Удаление пользователя 1 запрос для удаления пользователя.
Удаление точки 1 запрос для удаления точки.
Удаление обращения в поддержку 1 запрос для удаления обращения.
-
User Хранит информацию о пользователях системы.
-
Point Хранит информацию о точках на карте доступности.
-
Route Хранит информацию о маршрутах, включающих точки.
-
SupportRequest Хранит информацию о запросах в поддержку, включая отзывы. Итого: 4
Сравнение показывает, что реляционная модель может иметь фиксированный объем информации, в то время как в нереляционной модели объем данных может варьироваться в зависимости от структуры документа.
Количество запросов для каждой модели одинаково и равно 1, кроме пункта с созданием маршрута, там зависит от количества точек.
Обе модели используют одну коллекцию при любом сценарии использования.
В заключение, выбор между реляционной и нереляционной моделями данных зависит от требований к производительности, гибкости, объему информации . Реляционная модель гарантирует целостность данных а Нереляционная модель предлагает гибкость и производительность при больших объемах данных, что идеально для современных высоконагруженных приложений. Это облегчит управление данными без необходимости жесткой схемы и снизит сложность поддержки приложения в долгосрочной перспективе.