MidТеория13 min

Фреймворк выбора БД

Критерии выбора базы данных, decision tree и сравнительная таблица для System Design интервью

Почему выбор БД важен

Смена базы данных в работающей системе -- одна из самых дорогих операций. Правильный выбор на старте экономит месяцы работы. На System Design интервью умение обосновать выбор БД -- ключевой навык.

Критерии выбора

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

Вопрос Если "да" Рекомендация
Данные строго структурированы? Таблицы со связями PostgreSQL/MySQL
Данные имеют переменную структуру? JSON-документы MongoDB/PostgreSQL JSONB
Данные -- пары ключ-значение? Простой lookup Redis/DynamoDB
Много связей между сущностями? Графовые запросы Neo4j/ArangoDB
Данные с временными метками? Временные ряды TimescaleDB/InfluxDB

2. Паттерны доступа (Access Patterns)

<?php

declare(strict_types=1);

/**
 * Different access patterns require different databases
 */
final class AccessPatternAnalysis
{
    /**
     * Pattern 1: Point reads by primary key (any DB works well)
     * Best: Redis > DynamoDB > PostgreSQL
     */
    public function getUserById(\PDO $db, string $userId): ?array
    {
        $stmt = $db->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $userId]);
        return $stmt->fetch(\PDO::FETCH_ASSOC) ?: null;
    }

    /**
     * Pattern 2: Complex queries with JOINs (relational DB)
     * Best: PostgreSQL > MySQL > CockroachDB
     */
    public function getOrdersWithDetails(\PDO $db, string $userId): array
    {
        $stmt = $db->prepare(<<<SQL
            SELECT o.*, p.name, oi.quantity
            FROM orders o
            JOIN order_items oi ON o.id = oi.order_id
            JOIN products p ON oi.product_id = p.id
            WHERE o.user_id = :user_id
            ORDER BY o.created_at DESC
            LIMIT 50
        SQL);
        $stmt->execute(['user_id' => $userId]);
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
    }

    /**
     * Pattern 3: Full-text search
     * Best: Elasticsearch > Meilisearch > PostgreSQL (tsvector)
     */
    public function searchProducts(\PDO $db, string $query): array
    {
        $stmt = $db->prepare(<<<SQL
            SELECT id, name, description,
                   ts_rank(search_vector, plainto_tsquery(:query)) as rank
            FROM products
            WHERE search_vector @@ plainto_tsquery(:query)
            ORDER BY rank DESC
            LIMIT 20
        SQL);
        $stmt->execute(['query' => $query]);
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
    }

    /**
     * Pattern 4: Write-heavy with time-series
     * Best: TimescaleDB > InfluxDB > Cassandra
     */
    public function insertMetric(\PDO $db, array $metric): void
    {
        $stmt = $db->prepare(<<<SQL
            INSERT INTO metrics (time, host, name, value)
            VALUES (:time, :host, :name, :value)
        SQL);
        $stmt->execute($metric);
    }

    /**
     * Pattern 5: Aggregations on large datasets (OLAP)
     * Best: ClickHouse > BigQuery > PostgreSQL
     */
    public function getRevenueByCategory(\PDO $db, int $year): array
    {
        $stmt = $db->prepare(<<<SQL
            SELECT category, SUM(amount) as revenue, COUNT(*) as orders
            FROM sales
            WHERE EXTRACT(YEAR FROM created_at) = :year
            GROUP BY category
            ORDER BY revenue DESC
        SQL);
        $stmt->execute(['year' => $year]);
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
    }
}
### 3. Нефункциональные требования
Требование Метрика Влияние на выбор
Задержка (Latency) p99 < 10ms Redis, in-memory
Пропускная способность (Throughput) 100K writes/sec Cassandra, Kafka
Объём данных > 1TB Sharding / distributed
Доступность (Availability) 99.99% Multi-region, AP systems
Консистентность Strong PostgreSQL, CockroachDB
Durability Не потерять ни одного запроса ACID, WAL, replication

4. Операционные критерии

Критерий Вопрос
Экспертиза команды Кто будет поддерживать?
Managed vs Self-hosted Есть ли DBA?
Стоимость Лицензия, облако, персонал
Экосистема Инструменты, мониторинг, backup
Миграция данных Как мигрировать с текущей БД?

Decision Tree

Начало: Какие данные?
│
├── Структурированные с транзакциями?
│   ├── Объём < 1 TB?
│   │   └── PostgreSQL (универсальный выбор)
│   ├── Объём > 1 TB, нужен SQL?
│   │   └── CockroachDB / TiDB (NewSQL)
│   └── Только MySQL экосистема?
│       └── MySQL 8+ / Vitess (для шардинга)
│
├── Кэш / сессии / счётчики?
│   └── Redis (в памяти, быстро)
│
├── Документы с переменной схемой?
│   ├── Транзакции нужны?
│   │   └── PostgreSQL JSONB / MongoDB 4+
│   └── Горизонтальное масштабирование?
│       └── MongoDB (sharding)
│
├── Аналитика / OLAP?
│   ├── Петабайтный масштаб, cloud?
│   │   └── BigQuery / Snowflake
│   └── Self-hosted?
│       └── ClickHouse
│
├── Полнотекстовый поиск?
│   ├── Простой поиск?
│   │   └── PostgreSQL (tsvector)
│   └── Сложный / фасетный поиск?
│       └── Elasticsearch / Meilisearch
│
├── Метрики / IoT / временные ряды?
│   ├── Уже есть PostgreSQL?
│   │   └── TimescaleDB (расширение PG)
│   └── Специализированное решение?
│       └── InfluxDB / QuestDB
│
├── Графовые данные (рекомендации, социальные сети)?
│   └── Neo4j / ArangoDB
│
└── AI / семантический поиск?
    ├── Уже есть PostgreSQL?
    │   └── pgvector
    └── Специализированное?
        └── Pinecone / Qdrant

Сравнительная таблица

Топ-10 БД для PHP-разработчика

БД Тип Сильные стороны Слабые стороны PHP-драйвер
PostgreSQL RDBMS Универсальность, JSONB, расширения Масштабирование writes PDO pgsql
MySQL RDBMS Простота, экосистема Меньше возможностей чем PG PDO mysql
Redis Key-Value Скорость, структуры данных Ограничен RAM phpredis
MongoDB Document Гибкая схема, sharding Нет JOIN, eventual consistency mongodb ext
Elasticsearch Search Полнотекстовый поиск Сложность, ресурсоёмкость elasticsearch-php
ClickHouse Column OLAP, скорость аналитики Нет UPDATE/DELETE HTTP API
Cassandra Wide Column Write throughput, geo Сложная модель данных php-cassandra
TimescaleDB Time-Series PG-совместимость, time-series Только временные данные PDO pgsql
CockroachDB NewSQL Distributed SQL, ACID Сложность, стоимость PDO pgsql
Neo4j Graph Графовые запросы Узкая специализация neo4j-php

Антипаттерны выбора

1. Resume-Driven Development

# Плохо: выбираем MongoDB потому что "модно"
"Давайте используем MongoDB для нашего интернет-магазина!"
# Факт: интернет-магазин = заказы + платежи = транзакции = PostgreSQL

2. One Size Fits All

# Плохо: PostgreSQL для всего
# Факт: для поиска по каталогу лучше Elasticsearch,
# для кэша -- Redis, для метрик -- TimescaleDB

3. Premature Optimization

# Плохо: сразу ставим Cassandra для "масштабирования"
# Факт: PostgreSQL на одном сервере обслужит миллионы пользователей.
# Cassandra нужна при > 100K writes/sec или multi-region

Практический шаблон для интервью

<?php

declare(strict_types=1);

/**
 * Template: justify database choice on System Design interview
 */
final class DatabaseSelectionTemplate
{
    /**
     * Step 1: Identify data characteristics
     */
    public function analyzeData(): array
    {
        return [
            'structure' => 'structured',      // structured / semi-structured / unstructured
            'relationships' => 'many JOINs',   // none / few / many JOINs
            'schema_stability' => 'stable',    // stable / evolving / unknown
            'size' => '100 GB',                // estimate
            'growth_rate' => '1 GB/day',       // estimate
        ];
    }

    /**
     * Step 2: Identify access patterns
     */
    public function analyzeAccessPatterns(): array
    {
        return [
            'read_write_ratio' => '80:20',     // read-heavy / write-heavy / balanced
            'query_complexity' => 'complex',   // point lookup / range / complex / aggregation
            'latency_requirement' => 'p99 < 50ms',
            'throughput' => '10K requests/sec',
            'consistency' => 'strong',          // strong / eventual / causal
        ];
    }

    /**
     * Step 3: Consider non-functional requirements
     */
    public function analyzeNFR(): array
    {
        return [
            'availability' => '99.9%',
            'durability' => 'critical',
            'geographic' => 'single region',
            'compliance' => 'GDPR',
            'budget' => 'moderate',
        ];
    }

    /**
     * Step 4: Make decision with justification
     *
     * "For this system I would choose PostgreSQL because:
     *  1. Our data is highly structured with complex relationships
     *  2. We need strong consistency for financial transactions
     *  3. Read-heavy workload works well with read replicas
     *  4. JSONB covers our semi-structured data needs
     *  5. Team has PostgreSQL expertise
     *  6. 100GB fits well in a single instance
     *
     *  I would add Redis for session cache and rate limiting."
     */
    public function makeDecision(): string
    {
        return 'PostgreSQL + Redis';
    }
}
> **На интервью:** всегда обосновывайте выбор конкретными требованиями системы. Покажите, что знаете альтернативы и объясните, почему они не подходят.

Итоги

  • Начинайте выбор с анализа данных и паттернов доступа
  • PostgreSQL -- лучший выбор по умолчанию для большинства случаев
  • Добавляйте специализированные БД только для конкретных проблем
  • Операционная стоимость каждой дополнительной БД значительна
  • На интервью: структурированный подход важнее правильного ответа