Skip to content

Модель данных

Kovalev Pavel edited this page Nov 27, 2024 · 12 revisions

Нереляционная модель

Neo4j

Графическое представление модели в виде json-схемы

nosql1.png

Описание коллекций

User - Хранение информации о пользователях.

  • id (string): Уникальный идентификатор пользователя.
  • email (string): Электронная почта пользователя.
  • created_at (timestamp): Дата регистрации пользователя.
  • updated_at (timestamp): Дата и время последнего обновления профиля.
  • avatar_url (string): Ссылка на аватар пользователя.

Point - Хранение данных о точках маршрутов.

  • id (string): Уникальный идентификатор точки.
  • coordinates (GeoJSON): Координаты точки на карте.
  • description (string): Описание точки.
  • availability (number): Уровень доступности.

Route - Маршруты, составленные пользователями.

  • id (string): Уникальный идентификатор маршрута.
  • points (array of references): Массив ссылок на точки маршрута (Point).
  • author (reference to User): Идентификатор автора маршрута.
  • popularity_score (number): Рейтинг маршрута на основе обращений в поддержку и популярности.

Lake - Озера, которые являются точками интереса для маршрутов.

  • 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): Уровень солености озера.

SupportTicket - Запросы в поддержку от пользователей.

  • 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.

Пользователь (User)

  • id (строка, 8 байт)
  • email (строка, 50 байт)
  • дата регистрации (дата, 8 байт)
  • дата обновления профиля (timestamp, 8 байт)
  • аватар (строка, 100 байт)

Итого:

  • ( 8 + 50 + 8 + 8 + 100 = 174 ) байта на пользователя

Точка (Point)

  • id (строка, 8 байт)
  • координаты (GeoJSON, 16 байт)
  • описание (строка, 100 байт)
  • доступность (целое число, 4 байта)

Итого:

  • ( 8 + 16 + 100 + 4 = 128 ) байт на точку

Маршрут (Route)

  • id (строка, 8 байт)
  • список точек (массив ссылок на узлы точек, в среднем 10 ссылок, по 8 байт каждая): ( 10 \times 8 = 80 ) байт
  • автор (ссылка на User, 8 байт)
  • рейтинг (число, 4 байта)

Итого:

  • ( 8 + 80 + 8 + 4 = 100 ) байт на маршрут

Озеро (Lake)

  • 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 { байт}

Обращение в поддержку (SupportRequest)

  • id (строка, 8 байт)
  • автор (ссылка на User, 8 байт)
  • связь с маршрутом (ссылка на Route, 8 байт)
  • связь с озером (ссылка на Lake, 8 байт)
  • тема (строка, 50 байт)
  • отзывы (массив объектов):
    • текст (строка, 200 байт)
    • дата (timestamp, 8 байт)
    • фото (base64, 2 796 202 байта)

Оценка объема памяти для связей

  1. User-Creates-Route
  2. Route-Belongs-To-User
  3. Route-Includes-Point
  4. Point-Belongs-To-Route
  5. User-Creates-SupportRequest
  6. SupportRequest-Belongs-To-User
  7. Lake-Connected-To-SupportRequest
  8. Route-Connected-To-SupportRequest
  9. User-Creates-Point
  10. Point-Belongs-To-Lake
  11. Point-Includes-Lake

Общий объем памяти для связей

Теперь давайте подытожим объем памяти для всех указанных связей: 37+37+37+37+37+37+39+39+37+37+37= 398 байт

Итого для расчета на пользователя

  1. Пользователь (User): 174 байта
  2. Точки (Point): 12800 байт (10 точек по 128 байт в 10 разных маршрутах)
  3. Маршруты (Route): 1000 байт (10 маршрутов по 100 байт)
  4. Обращения в поддержку (SupportRequest): 2,796,492 байт делим на 10
  5. **Связи: 398 байт
  6. Озера: 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) возрастает объем хранимых данных, таких как учетные записи пользователей и обращения в поддержку. Процент обращений в поддержку:
  • При росте числа пользователей также возрастает количество обращений в службу поддержки. Увеличение количества отзывов очень сильно влияет на количество данных.

Примеры данных

nosql2.png

Примеры запросов

  1. Получение всех пользователей
    MATCH (u:User)
    RETURN u.id AS userId, u.email AS email, u.registration_date AS registrationDate
  1. Поиск точки по идентификатору
    MATCH (p:Point {id: 'point001'})
    RETURN p.id AS pointId, p.coordinates AS coordinates, p.description AS description, p.availability AS availability
  1. Получение всех маршрутов с их точками
    MATCH (r:Route)-[:CONTAINS]->(p:Point)
    RETURN r.id AS routeId, collect(p.id) AS points
  1. Получение всех точек, доступных для маршрута
    MATCH (r:Route {id: 'route001'})-[:CONTAINS]->(p:Point)
    RETURN p.id AS pointId, p.description AS description
  1. Добавление нового пользователя
    CREATE (u:User {id: 'user003', email: '[email protected]', registration_date: '2024-02-03'})
    RETURN u
  1. Добавление новой точки
    CREATE (p:Point {id: 'point003', coordinates: '59.9342, 30.3351', description: 'Невский проспект', availability: ['Доступно']})
    RETURN p
  1. Создание маршрута с точками
    CREATE (r:Route {id: 'route003'})
    WITH r
    CREATE (p1:Point {id: 'point001'})-[:CONTAINS]->(r)
    CREATE (p2:Point {id: 'point002'})-[:CONTAINS]->(r)
    RETURN r
  1. Получение обращений в поддержку по пользователю
    MATCH (u:User)-[:MADE]->(sr:SupportTicket)
    WHERE u.id = 'user001'
    RETURN sr.id AS supportRequestId, sr.subject AS subject, sr.reviews AS reviews
  1. Получение статистики обращений в поддержку
    MATCH (sr:SupportTicket)
    RETURN COUNT(sr) AS totalSupportTickets
  1. Обновление темы обращения в поддержку
    MATCH (sr:SupportTicket {id: 'support001'})
    SET sr.subject = 'Обновленная тема обращения'
    RETURN sr
  1. Поиск самых популярных маршрутов и отзывов (по количеству обращений в поддержку)
*MATCH (route:Route)<-[:route_reference]-(request:SupportTicket)
*RETURN route.id AS RouteID, COUNT(request) AS SupportTicketsCount
*ORDER BY SupportTicketsCount DESC
*LIMIT 10;
  1. Самые длинные маршруты
MATCH (route:Route)
WITH route, SIZE(route.points) AS pointCount
RETURN route.id AS RouteID, pointCount
ORDER BY pointCount DESC
LIMIT 10; 
  1. Самые недоступные / доступные озера (с точки зрения близости к ним маршрутов) недоступные:
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;
  1. Подсчет доступности в виде числа и запись его в 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 Запрос: Добавление нового пользователя.
  2. Юзкейс: Вход Количество запросов: 1 Запрос: Поиск пользователя по email и паролю (при условии, что хранится хэш пароля).
  3. Юзкейс: Карта с Точками Количество запросов: 1 Запрос: Получение всех точек на карте.
  4. Юзкейс: Импорт/экспорт Количество запросов: 2: Импорт точек из документа (может потребоваться несколько запросов в зависимости от количества точек). Экспорт точек доступности.
  5. Юзкейс: Карта Количество запросов: 1 Запрос: Получение точки по ее координатам или описанию.
  6. Юзкейс: Поддержка Количество запросов: 1 Запрос: Создание нового обращения в поддержку.
  7. Юзкейс: Поиск по атрибутам Количество запросов: 1 Запрос: Поиск точек по заданным атрибутам.
  8. Юзкейс: Настройки Количество запросов: 2: олучение текущих настроек пользователя. Обновление настроек (например, языка или пароля).
  9. Юзкейс: Смена пароля Количество запросов: 1 Запрос: Обновление пароля пользователя. RETURN u
  10. Юзкейс: Статистика Количество запросов: 1 Запрос: Получение статистики обращений в поддержку.

Количество задействованных коллекций

Суммарно количество задействованных коллекций (узлов) будет зависеть от конкретных действий пользователя, в среднем:

  • User: 1
  • Point: X (количество точек, которые находятся в базе данных)
  • Route: Y (количество маршрутов)
  • SupportRequest: 1
  • Lakes: Z ( количество всех озёр)

Реляционная модель

OpenStreetMap

Графическое представление модели в виде ER-диаграммы

sql1.png !sql1.png

Описание коллекций

1. Пользователь (User)

  • Назначение: Хранение информации о пользователях, которые могут создавать маршруты и оставлять обращения в поддержку.
  • Поля:
    • id: SERIAL — Уникальный идентификатор пользователя, занимает 4 байта.
    • email: VARCHAR(50) — Электронная почта пользователя, занимает до 50 байт.
    • created_at: TIMESTAMP — Дата регистрации пользователя, занимает 8 байт.
    • updated_at: TIMESTAMP — Дата последнего обновления профиля, занимает 8 байт.
    • avatar: VARCHAR(100) — Ссылка на аватар пользователя, занимает до 100 байт.

2. Точка (Point)

  • Назначение: Хранение информации о точках, которые могут быть добавлены в маршруты.
  • Поля:
    • id: SERIAL — Уникальный идентификатор точки, занимает 4 байта.
    • coordinates: VARCHAR(20) — Координаты точки на карте, занимает до 20 байт.
    • description: VARCHAR(100) — Описание точки, занимает до 100 байт.
    • availability: TEXT[] — Список атрибутов доступности, массив строк (занимает переменное количество байт).

3. Маршрут (Route)

  • Назначение: Хранение информации о маршрутах, созданных пользователями, включая точки, которые входят в маршрут.
  • Поля:
    • id: SERIAL — Уникальный идентификатор маршрута, занимает 4 байта.
    • user_id: INTEGER — Идентификатор пользователя, создающего маршрут (ссылка на таблицу User), занимает 4 байта.
    • name: VARCHAR(100) — Название маршрута, занимает до 100 байт.
    • points: INTEGER[] — Список идентификаторов точек (ссылки на таблицу Point), массив целых чисел (занимает переменное количество байт).

4. Запрос в поддержку (SupportTicket)

  • Назначение: Хранение информации о запросах пользователей в службу поддержки.
  • Поля:
    • id: SERIAL — Уникальный идентификатор обращения, занимает 4 байта.
    • user_id: INTEGER — Идентификатор пользователя, отправляющего обращение (ссылка на таблицу User), занимает 4 байта.
    • subject: VARCHAR(50) — Тема обращения, занимает до 50 байт.
    • reviews: JSONB — Отзывы по обращению, хранящиеся в формате JSON (занимает переменное количество байт).

5. Озеро (Lake)

  • Назначение: Хранение информации об озёрах, которые могут быть связаны с маршрутами.
  • Поля:
    • 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 байта.

6. Связь между маршрутом и озером (RouteLake)

  • Назначение: Хранение информации о связи между маршрутами и озёрами.
  • Поля:
    • route_id: INTEGER — Идентификатор маршрута (ссылка на таблицу Route).
    • lake_id: INTEGER — Идентификатор озера (ссылка на таблицу Lake).
    • PRIMARY KEY (route_id, lake_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 могут появится проблемы.

Примеры данных

Таблица User

sql2

Таблица Point

sql3

Таблица Route

sql4

Таблица SupportTicket

sql5

Примеры запросов

Текст запросов:

Вставка данных

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, кроме пункта с созданием маршрута, там зависит от количества точек.

Количество задействованных коллекций

Обе модели используют одну коллекцию при любом сценарии использования.

Вывод

В заключение, выбор между реляционной и нереляционной моделями данных зависит от требований к производительности, гибкости, объему информации . Реляционная модель гарантирует целостность данных а Нереляционная модель предлагает гибкость и производительность при больших объемах данных, что идеально для современных высоконагруженных приложений. Это облегчит управление данными без необходимости жесткой схемы и снизит сложность поддержки приложения в долгосрочной перспективе.

Clone this wiki locally