Завдання №2

Конструювання програмного забезпечення

Мета

Спроєктувати зберігання даних для системи із Завдання №1: обрати бази даних під конкретні дані й запити, розрахувати, скільки машин вони потребують, і спланувати реплікацію, балансування навантаження та кешування.

Беріть числа із Завдання №1: RPS на читання і запис, обсяг даних за 5 років, співвідношення читання до запису. Якщо за цей час вони змінилися, оновіть їх і поясніть чому.

Орієнтири для розрахунків

Доповнюють орієнтири із Завдання №1:

Величина Орієнтир
Одна машина БД 10k RPS читань або 1k RPS записів, 50 ТБ SSD, 1 ТБ RAM
Один вузол кешу в RAM (Redis) порядку 100 тис. операцій на секунду
Звернення до пам’яті 100 нс
Запит туди й назад у межах дата-центру 0.5 мс
Позиціювання головки HDD 10 мс
Навантаження на БД з кешем читання × (1 − частка влучань у кеш)
Пікове навантаження у 2–3 рази більше за середнє
Реплікація кожен запис зберігається у 3 копіях

Розминка: знайдіть помилки 🕵️

Студент проєктує зберігання для хостингу текстів із лекції (на кшталт Pastebin):

  • 1 млн нових текстів на день по 10 кБ;
  • 12 RPS на запис і 120 RPS на читання;
  • за 5 років — 1.8 млрд текстів, або 18 ТБ;
  • нефункціональна вимога: завантажені тексти не повинні зникати.

Його рішення:

  1. Масштабування. 18 ТБ — це багато, тому розбиваємо тексти на 18 шардів по 1 ТБ, кожен на окремому сервері.
  2. Тип БД. Текст завжди читають за коротким посиланням, тобто за ключем, тож метадані можна тримати в key-value сховищі або в реляційній БД з індексом за ключем.
  3. Кеш. Щоб читання були швидкими, кешуємо в Redis усі тексти.
  4. Балансування. Балансувальник рівномірно розподіляє всі запити, зокрема записи, між трьома репліками БД.
  5. Надійність. Щоб записи були швидкими, пишемо текст спершу в Redis, а в БД переносимо раз на годину.

В одному рядку все правильно, у решті — помилки. Знайдіть їх, перш ніж відкривати відповідь.

  1. ❌ Шардування не потрібне. 120 RPS читань і 12 RPS записів — це ≈ 1% можливостей однієї машини (10k і 1k), а 18 ТБ вміщаються на її 50 ТБ SSD. Потрібні не шарди, а репліки — для доступності й збереження даних. А ще краще класти самі тексти в об’єктне сховище (на кшталт S3), а в БД тримати лише метадані: ключ, автора, дату, термін дії.
  2. ✅ Правильно: вибір типу БД випливає із запиту, який вона обслуговує.
  3. ❌ Кешувати все — дорого й марно. 18 ТБ RAM коштують ≈ $180 тис. і займуть 18 машин. Здебільшого читають свіжі тексти: за день їх з’являється 10 ГБ, і якщо кешувати найпопулярніші 20%, вистачить ≈ 2 ГБ.
  4. ❌ Записи йдуть лише на лідера. У реплікації «лідер — послідовники» репліки приймають тільки читання. Балансувати можна читання, а записи — ні.
  5. ❌ Кеш не є джерелом правди. Якщо вузол Redis впаде між перенесеннями, тексти за годину можуть загубитися, а це прямо порушує вимогу «завантажені тексти не повинні зникати». Спершу запис у БД, потім — у кеш.

Етапи

1. Дані та запити

Базу даних обирають під запити, тому почніть із них.

  • Випишіть 3–6 основних сутностей і заповніть таблицю:

    Сутність Розмір запису Записів за 5 років Обсяг Читань/с Записів/с
    … … … … … …
  • Випишіть основні запити до даних і вимоги до них:

    Запит Приклад RPS (середнє / пік) Допустима затримка
    за ключем отримати замовлення за номером
    список останні 20 замовлень користувача, новіші спершу
    … … … …
  • Окремо позначте великі файли (фото, відео, документи): їхнє місце — в об’єктному сховищі, а не в БД.

2. Типи баз даних

Для кожної сутності оберіть тип сховища і поясніть вибір через запити з етапу 1, а не через популярність.

Тип Коли пасує Приклади
Реляційна (SQL) транзакції, зв’язки між сутностями, складні запити, стабільна схема PostgreSQL, MySQL
Документна записи з різним набором атрибутів, читання документа цілком MongoDB
Key-value читання й запис за ключем з мінімальною затримкою Redis, DynamoDB
Широкостовпцева дуже багато записів, читання за ключем і діапазоном (наприклад, за часом) Cassandra, ScyllaDB
Пошукова повнотекстовий пошук, фільтри, ранжування Elasticsearch, OpenSearch
Графова запити зі зв’язків: друзі друзів, рекомендації Neo4j
Часових рядів метрики й події з часовою міткою, агрегація за періодами TimescaleDB, InfluxDB
Об’єктне сховище великі файли Amazon S3

Для кожної сутності також визначте:

  • Чи потрібні транзакції. Наприклад, списання грошей і резервування товару — так, лічильник переглядів — ні.
  • Що важливіше під час збою мережі — консистентність чи доступність. Вибір має збігатися з тим, що ви зробили в Завданні №1.

Приклади таких рішень: онлайн-магазин (MongoDB для каталогу, MySQL для замовлень, Cassandra для архіву, S3 для зображень) і платформа ШІ-оцінювання (одна таблиця DynamoDB з TTL і умовними записами, щоб оцінку не надіслали двічі).

3. Реплікація і масштабування

🔮 Спершу вгадайте: скільки машин БД знадобиться вашій системі? Запишіть число, а наприкінці порівняйте з розрахунком.

  1. Розрахуйте кількість машин для кожної БД окремо за трьома обмеженнями і візьміть найбільше значення:

    Обмеження Формула
    Обсяг обсяг за 5 років з реплікацією / 50 ТБ
    Записи пікові записи / 1k RPS
    Читання пікові читання після кешу / 10k RPS
  2. Опишіть реплікацію. Скільки реплік і навіщо: доступність, резервні копії, розподіл читань. Якщо читання йдуть із реплік, поясніть, що станеться, коли репліка відстає від лідера і показує застарілі дані.

  3. Якщо даних чи записів забагато для однієї машини, спроєктуйте шардування:

    • ключ шардування (ідентифікатор користувача, хеш ключа, діапазон дат) і чому саме він;
    • чи не з’являться «гарячі» шарди, куди потрапить непропорційно багато запитів;
    • які запити стануть дорогими: об’єднання даних з різних шардів, запити «по всіх користувачах».

    Якщо все вміщається на одну машину, шардування не робіть: зазначте, за якого зростання навантаження воно знадобиться.

4. Балансування навантаження

  • Покажіть, де стоять балансувальники: перед сервісами, між репліками читання.
  • Оберіть алгоритм (round robin, least connections, consistent hashing) і поясніть вибір. Consistent hashing, наприклад, пасує кешу й шардам: коли додається вузол, переїжджає лише невелика частина ключів.
  • Опишіть перевірки стану (health checks): як балансувальник дізнається, що вузол недоступний, і що відбувається із запитами до нього.
  • Записи — лише на лідера. Поясніть, що станеться, якщо лідер відмовить.

5. Кеш

  • Що кешуємо і де: CDN для статики й файлів, кеш застосунку (Redis) для гарячих даних.
  • Стратегія. Оберіть одну і поясніть:
    • cache-aside — застосунок спершу читає кеш, а при промаху читає БД і кладе результат у кеш;
    • write-through — запис іде одночасно в БД і в кеш.
  • Інвалідація. Скільки живе запис у кеші (TTL) і коли його скидають примусово. Скільки часу користувач може бачити застарілі дані і чи це прийнятно для цієї сутності. Приклад свідомого компромісу — кеш Moodle на хвилину в платформі ШІ-оцінювання.
  • Розрахунок:
    • обсяг кешу — яку частку даних кешуємо і чому (наприклад, 20% найпопулярніших);
    • очікувана частка влучань у кеш;
    • навантаження на БД: читання × (1 − частка влучань).
  • Що не кешуємо: дані, де застарілість неприпустима (залишки товару, баланс рахунку), і чому.

6. Схема та висновки

  • Намалюйте схему: сервіси → балансувальники → кеш → БД (лідер і репліки, шарди) → об’єктне сховище.
  • Що стане першим вузьким місцем, якщо навантаження зросте вдесятеро? Що ви зробите тоді?
  • Наскільки розрахунок кількості машин відрізнився від вашої оцінки з етапу 3?

Що здати

  1. Звіт з усіма етапами: таблиці даних і запитів, вибір БД з обґрунтуванням, розрахунок машин, реплікація й шардування, балансування, кеш, схема й висновки.
  2. Коротку презентацію для обговорення на занятті. Порівнюватимемо, які БД обрали різні команди для схожих даних і чому.

Перед здачею перевірте

Матеріали для підготовки