← Назад к вопросам

Для чего используют сразу несколько баз данных?

2.0 Middle🔥 191 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Использование нескольких БД в приложении

Да, я встречал проекты с несколькими БД. Это практика для оптимизации производительности, надёжности и масштабируемости. Важно понять зачем это делают.

1. Разные типы БД для разных задач

PostgreSQL + Redis:

┌─────────────────────────────────┐
│   Frontend Application           │
└──────┬──────────────────────────┘
       │
       ├──> PostgreSQL (основная БД)
       │    - Пользователи
       │    - Посты
       │    - Комментарии
       │    - ACID транзакции
       │
       └──> Redis (кэш)
            - Sessions
            - Rate limiting
            - Real-time notifications
            - Быстрый доступ (in-memory)

Практический пример:

# Backend (FastAPI)
from fastapi import FastAPI
import redis
import asyncpg

app = FastAPI()

# PostgreSQL для постоянного хранения
db_pool: asyncpg.Pool

# Redis для кэша и сессий
redis_client: redis.Redis

@app.get('/api/users/{user_id}')
async def get_user(user_id: int):
    # 1. Проверяем Redis (кэш)
    cached_user = await redis_client.get(f'user:{user_id}')
    if cached_user:
        return json.loads(cached_user)
    
    # 2. Если нет в кэше — берём из PostgreSQL
    user = await db_pool.fetchrow(
        'SELECT * FROM users WHERE id = $1',
        user_id
    )
    
    # 3. Сохраняем в Redis на 1 час
    await redis_client.setex(
        f'user:{user_id}',
        3600,
        json.dumps(dict(user))
    )
    
    return dict(user)

Frontend не видит различия:

const fetchUser = async (userId) => {
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
};

// Для frontend это просто API, БД скрыта за backend

2. Sharding — распределение данных по БД

Когда одна БД не вмещает данные:

Пользователи распределены по ID:
┌─────────────────┬─────────────────┬─────────────────┐
│   DB Shard 1    │   DB Shard 2    │   DB Shard 3    │
├─────────────────┼─────────────────┼─────────────────┤
│ user_id: 1-1000 │ user_id: 1001-  │ user_id: 2001-  │
│ (PostgreSQL)    │ 2000 (PG)       │ 3000 (PG)       │
└─────────────────┴─────────────────┴─────────────────┘
         │               │               │
         └───────────────┼───────────────┘
                         │
                    Backend (Router)
                    Определяет какой shard

Backend логика:

def get_shard_for_user(user_id: int) -> str:
    # Используем хеширование
    shard_num = hash(user_id) % 3
    return f'db_shard_{shard_num}'

async def get_user(user_id: int):
    shard = get_shard_for_user(user_id)
    db = get_db_connection(shard)
    
    user = await db.fetchrow(
        'SELECT * FROM users WHERE id = $1',
        user_id
    )
    return user

3. Read Replicas — оптимизация читается

         Backend
           │
    ┌──────┴──────┐
    │             │
    ▼             ▼
  Write       Read Replicas
PostgreSQL
(Primary)    ┌─────────────┐
  (5432)     │  Replica 1  │
             │  (5433)     │
             ├─────────────┤
             │  Replica 2  │
             │  (5434)     │
             └─────────────┘

Использование:

# Пишем в основную БД
async def create_post(title: str, content: str):
    post_id = await primary_db.execute(
        'INSERT INTO posts (title, content) VALUES ($1, $2)',
        title, content
    )
    return post_id

# Читаем с реплик (load balancing)
async def get_feed():
    replica = select_random_replica()  # Random read replica
    posts = await replica.fetch(
        'SELECT * FROM posts ORDER BY created_at DESC LIMIT 20'
    )
    return posts

4. Polyglot Persistence — разные БД для разных данных

┌──────────────────────────────────┐
│   Backend Application            │
└──┬──┬──┬──┬────────────┬─────────┘
   │  │  │  │            │
   │  │  │  │            ▼
   │  │  │  │        MongoDB
   │  │  │  │        (гибкая
   │  │  │  │         схема)
   │  │  │  │
   │  │  │  └──> Elasticsearch
   │  │  │       (полнотекстовый
   │  │  │        поиск)
   │  │  │
   │  │  └──> Redis
   │  │       (кэш/очереди)
   │  │
   │  └──> PostgreSQL
   │       (основные данные)
   │
   └──> Neo4j
        (графовая БД
         для связей)

Практический пример:

# Пост
post = {
    'id': 1,
    'title': 'Hello',
    'content': 'World'
}

# Где хранить?
# - id, title → PostgreSQL (обязательно)
# - полный text → Elasticsearch (для поиска)
# - структурированные данные → MongoDB (гибко)
# - связи user->post→comment → Neo4j (граф)

@app.post('/api/posts')
async def create_post(post: PostCreate):
    # 1. PostgreSQL (основное хранилище)
    post_id = await pg_db.execute(
        'INSERT INTO posts (title, user_id) VALUES ($1, $2)',
        post.title, post.user_id
    )
    
    # 2. Elasticsearch (индексирование для поиска)
    await es.index(
        index='posts',
        id=post_id,
        body={'title': post.title, 'content': post.content}
    )
    
    # 3. Neo4j (граф связей)
    await neo4j.run(
        'CREATE (p:Post {id: $id, title: $title}) '
        'CREATE (u:User {id: $user_id}) '
        'CREATE (u)-[:WROTE]->(p)',
        id=post_id, title=post.title, user_id=post.user_id
    )
    
    # 4. Redis (кэш последних постов)
    await redis.lpush('recent_posts', post_id)
    await redis.ltrim('recent_posts', 0, 99)
    
    return {'id': post_id}

5. Event Sourcing — разные хранилища событий

┌──────────────────┐
│  User Action     │
│  (Button click)  │
└────────┬─────────┘
         │
         ▼
┌──────────────────────────────┐
│  Event Log (Append-only)     │
│  Kafka или Event Store       │
│  - user_created_event        │
│  - user_updated_event        │
│  - post_liked_event          │
└────────┬─────────────────────┘
         │
         ├──> PostgreSQL (Current state)
         │    для быстрых запросов
         │
         ├──> Elasticsearch (Projections)
         │    для аналитики
         │
         └──> Redis (Cache)
              для real-time

6. Выбор БД в frontend (индиректно)

Frontend видит это в API ответах:

// Быстро — данные из Redis
const cachedUser = await fetch('/api/users/1'); // <1ms

// Медленнее — данные из PostgreSQL
const user = await fetch('/api/users/complex-query'); // 50-100ms

// Поиск — из Elasticsearch
const searchResults = await fetch('/api/search?q=react'); // 10-50ms

Frontend может заметить разницу:

const loadPostsNormalWay = async () => {
  // Читает с реплик или кэша
  const response = await fetch('/api/posts'); // Быстро
  return response.json();
};

const loadPostsComplexQuery = async () => {
  // Сложный запрос к основной БД
  const response = await fetch('/api/posts/analytics'); // Медленнее
  return response.json();
};

Почему это важно для frontend разработчика

1. Понимание performance bottlenecks:

Если API медленный, возможно:
- Нет кэша (Redis)
- Запрос идёт на основную БД вместо реплики
- Нет индекса (Elasticsearch)

2. Правильное кэширование на клиенте:

// Часто меняется → кэшируем на 1 сек
const recentPosts = useQuery(['posts'], fetchPosts, {
  staleTime: 1000
});

// Редко меняется → кэшируем на 1 час
const user = useQuery(['user'], fetchUser, {
  staleTime: 3600000
});

3. Выбор инструментов для frontend:

// Если backend использует Redis для real-time:
const subscribe = () => {
  const ws = new WebSocket('wss://api.example.com/ws');
  ws.onmessage = handleUpdate;
};

// Если backend использует Event Sourcing:
const [optimisticUpdate, setOptimisticUpdate] = useState(null);
// Нужен optimistic update т.к. обработка может занять время

Вывод

Frontend разработчику нужно понимать, что:

  1. Несколько БД в backend = оптимизация, но не меняет API
  2. API скрывает сложность — frontend просто вызывает endpoints
  3. Понимание архитектуры помогает оптимизировать frontend — кэширование, debouncing, request batching
  4. Performance зависит от backend выбора БД — frontend не может влиять, но может компенсировать задержки

Важно знать почему backend выбирает несколько БД, но реально работать с ними не нужно.