Вступ до баз даних¶
База даних — організоване сховище для зберігання, керування та отримання даних. Замість тисяч файлів — структурована система з швидким пошуком та надійністю.
Навіщо потрібна база даних?¶
┌─────────────────────────────────────────────────────────────────┐
│ БЕЗ БАЗИ ДАНИХ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ users.json │
│ ├── Один файл = один user │
│ ├── Пошук = прочитати всі файли │
│ ├── Конкурентний доступ = колізії │
│ └── Зв'язки між даними = складно │
│ │
│ Проблеми: │
│ ✗ Повільний пошук (O(n)) │
│ ✗ Немає транзакцій │
│ ✗ Легко втратити дані │
│ ✗ Важко масштабувати │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ З БАЗОЮ ДАНИХ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PostgreSQL │
│ ├── Структуровані таблиці │
│ ├── Індекси для швидкого пошуку │
│ ├── Транзакції — атомарність операцій │
│ └── Зв'язки через JOIN │
│ │
│ Переваги: │
│ ✓ Швидкий пошук (O(log n) з індексами) │
│ ✓ ACID гарантії │
│ ✓ Резервне копіювання │
│ ✓ Масштабування │
│ │
└─────────────────────────────────────────────────────────────────┘
SQL vs NoSQL¶
SQL (реляційні бази даних)¶
Дані організовані в таблиці з рядками та колонками. Є чітка схема (структура даних).
┌────────────────────────────────────────────────────────────────┐
│ ТАБЛИЦЯ users │
├──────┬──────────────┬─────────────────────┬────────────────────┤
│ id │ name │ email │ created_at │
├──────┼──────────────┼─────────────────────┼────────────────────┤
│ 1 │ Олександр │ alex@example.com │ 2026-01-15 10:00 │
│ 2 │ Марія │ maria@example.com │ 2026-01-16 14:30 │
│ 3 │ Петро │ petro@example.com │ 2026-01-17 09:15 │
└──────┴──────────────┴─────────────────────┴────────────────────┘
│
│ JOIN
▼
┌────────────────────────────────────────────────────────────────┐
│ ТАБЛИЦЯ orders │
├──────┬───────────┬─────────────┬───────────┬──────────────────┤
│ id │ user_id │ product │ price │ order_date │
├──────┼───────────┼─────────────┼───────────┼──────────────────┤
│ 1 │ 1 │ Laptop │ 25000 │ 2026-01-16 │
│ 2 │ 1 │ Mouse │ 500 │ 2026-01-16 │
│ 3 │ 2 │ Keyboard │ 1200 │ 2026-01-17 │
└──────┴───────────┴─────────────┴───────────┴──────────────────┘
Приклади SQL баз:
- PostgreSQL — найпотужніша, рекомендована
- MySQL/MariaDB — популярна для веб
- SQLite — вбудована, для простих застосунків
NoSQL (нереляційні бази даних)¶
Гнучка структура, без жорсткої схеми.
┌────────────────────────────────────────────────────────────────┐
│ ДОКУМЕНТ (MongoDB) │
├────────────────────────────────────────────────────────────────┤
│ { │
│ "_id": "user_001", │
│ "name": "Олександр", │
│ "email": "alex@example.com", │
│ "orders": [ │
│ { "product": "Laptop", "price": 25000 }, │
│ { "product": "Mouse", "price": 500 } │
│ ], │
│ "preferences": { │
│ "theme": "dark", │
│ "language": "uk" │
│ } │
│ } │
└────────────────────────────────────────────────────────────────┘
Типи NoSQL:
| Тип | Опис | Приклад |
|---|---|---|
| Document | JSON-подібні документи | MongoDB |
| Key-Value | Простий ключ → значення | Redis |
| Column | Колонкове сховище | Cassandra |
| Graph | Зв'язки між даними | Neo4j |
Коли що використовувати?¶
┌────────────────────────────────────────────────────────────────┐
│ ВИБІР БАЗИ ДАНИХ │
├────────────────────────────────────────────────────────────────┤
│ │
│ SQL (PostgreSQL) — коли: │
│ ✓ Структуровані дані │
│ ✓ Складні запити та JOIN │
│ ✓ Потрібні транзакції (гроші, замовлення) │
│ ✓ Цілісність даних критична │
│ │
│ Приклади: користувачі, замовлення, фінанси, інвентар │
│ │
│ ─────────────────────────────────────────────────────────────│
│ │
│ NoSQL (MongoDB) — коли: │
│ ✓ Гнучка/змінна схема │
│ ✓ Ієрархічні дані (вкладені документи) │
│ ✓ Швидке прототипування │
│ ✓ Горизонтальне масштабування │
│ │
│ Приклади: логи, контент CMS, каталоги товарів, IoT дані │
│ │
│ ─────────────────────────────────────────────────────────────│
│ │
│ Redis (Key-Value) — коли: │
│ ✓ Кешування │
│ ✓ Сесії користувачів │
│ ✓ Черги повідомлень │
│ ✓ Real-time лічильники │
│ │
│ Приклади: кеш, rate limiting, pub/sub │
│ │
└────────────────────────────────────────────────────────────────┘
Порівняльна таблиця¶
| Критерій | PostgreSQL | MongoDB | Redis |
|---|---|---|---|
| Тип | SQL | Document | Key-Value |
| Схема | Строга | Гнучка | Немає |
| Транзакції | ACID | Базові | Обмежені |
| Масштабування | Вертикальне | Горизонтальне | Горизонтальне |
| Швидкість запису | Середня | Висока | Дуже висока |
| Складні запити | Відмінно | Добре | Немає |
| Зберігання | На диску | На диску | В RAM |
ACID принципи¶
ACID — гарантії для транзакцій в SQL базах даних:
┌────────────────────────────────────────────────────────────────┐
│ ACID │
├────────────────────────────────────────────────────────────────┤
│ │
│ A — Atomicity (Атомарність) │
│ Транзакція або виконується повністю, або не виконується │
│ зовсім. Немає "наполовину виконаних" операцій. │
│ │
│ Переказ грошей: │
│ 1. Зняти з рахунку А │
│ 2. Додати на рахунок Б │
│ → Або обидві операції, або жодна! │
│ │
│ ─────────────────────────────────────────────────────────────│
│ │
│ C — Consistency (Узгодженість) │
│ База завжди в коректному стані. │
│ Всі обмеження (constraints) дотримуються. │
│ │
│ email має бути унікальним → база не дозволить │
│ два однакових email │
│ │
│ ─────────────────────────────────────────────────────────────│
│ │
│ I — Isolation (Ізоляція) │
│ Паралельні транзакції не заважають одна одній. │
│ │
│ Два користувачі редагують різні рядки → │
│ кожен бачить консистентні дані │
│ │
│ ─────────────────────────────────────────────────────────────│
│ │
│ D — Durability (Довговічність) │
│ Збережені дані не втрачаються навіть при збоях. │
│ │
│ COMMIT пройшов → дані записані на диск, │
│ навіть якщо сервер впаде │
│ │
└────────────────────────────────────────────────────────────────┘
Приклад транзакції¶
-- Переказ грошей: атомарна операція
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE user_id = 2;
-- Якщо все OK
COMMIT;
-- Якщо щось пішло не так
-- ROLLBACK;
ORM — що це?¶
ORM (Object-Relational Mapping) — бібліотека, що дозволяє працювати з базою через об'єкти замість SQL.
# Без ORM (чистий SQL)
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
row = cursor.fetchone()
user = {"id": row[0], "name": row[1], "email": row[2]}
# З ORM (SQLAlchemy)
user = session.query(User).filter(User.id == user_id).first()
print(user.name) # Працюємо як з об'єктом
Популярні ORM¶
| Мова | ORM |
|---|---|
| Python | SQLAlchemy, Django ORM |
| JavaScript | Prisma, Sequelize, TypeORM |
| Go | GORM, sqlx |
| Ruby | ActiveRecord |
| PHP | Eloquent (Laravel) |
Плюси і мінуси ORM¶
✓ Простіший код
✓ Захист від SQL injection
✓ Переносимість між БД
✓ Міграції схеми
✗ Складні запити важче оптимізувати
✗ N+1 проблема
✗ Додатковий шар абстракції
Індекси¶
Індекс — структура даних для швидкого пошуку. Як індекс в книзі — замість гортати всі сторінки, дивишся на потрібну літеру.
┌────────────────────────────────────────────────────────────────┐
│ БЕЗ ІНДЕКСУ │
├────────────────────────────────────────────────────────────────┤
│ │
│ SELECT * FROM users WHERE email = 'alex@example.com' │
│ │
│ Сканування всієї таблиці (Full Table Scan): │
│ [row 1] → [row 2] → [row 3] → ... → [row 1000000] │
│ │
│ Час: O(n) — лінійно залежить від кількості рядків │
│ │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ З ІНДЕКСОМ │
├────────────────────────────────────────────────────────────────┤
│ │
│ CREATE INDEX idx_users_email ON users(email); │
│ │
│ Пошук по B-tree індексу: │
│ [M] │
│ / \ │
│ [D] [S] │
│ / \ / \ │
│ [A] [G] [N] [Z] │
│ │ │
│ alex@... ← Знайшли за 3 кроки! │
│ │
│ Час: O(log n) — логарифмічно │
│ │
└────────────────────────────────────────────────────────────────┘
Коли створювати індекси?¶
✓ Колонки в WHERE
✓ Колонки в JOIN
✓ Колонки в ORDER BY
✓ Унікальні значення (email, username)
✗ Не створювати на все поспіль
✗ Індекси займають місце
✗ Індекси уповільнюють INSERT/UPDATE
Практичний приклад: вибір БД¶
Веб-застосунок (типовий)¶
PostgreSQL — основна база
├── users (id, email, password_hash, created_at)
├── posts (id, user_id, content, created_at)
├── comments (id, post_id, user_id, content)
└── ...
Redis — кешування та сесії
├── session:abc123 → {user_id: 1, ...}
├── cache:posts:recent → [post1, post2, ...]
└── rate_limit:user:1 → 45
Docker Compose¶
services:
app:
image: myapp
depends_on:
- postgres
- redis
postgres:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Типові помилки¶
N+1 проблема¶
# Погано: N+1 запитів
users = User.query.all() # 1 запит
for user in users:
print(user.orders) # N запитів (по одному на user)
# Добре: 1-2 запити
users = User.query.options(
joinedload(User.orders)
).all()
Відсутність індексів¶
-- Повільно (Full Table Scan)
SELECT * FROM logs WHERE created_at > '2026-01-01';
-- Швидко (Index Scan)
CREATE INDEX idx_logs_created ON logs(created_at);
SELECT * FROM logs WHERE created_at > '2026-01-01';
SELECT *¶
-- Погано: тягнемо всі колонки
SELECT * FROM users;
-- Добре: тільки потрібні
SELECT id, name, email FROM users;
Див. також¶
- PostgreSQL основи — робота з PostgreSQL
- Бази даних (практика) — наша інфраструктура
- Docker Compose — запуск баз в контейнерах
Шлях: services/databases/intro.md