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

С какой базой данных работаешь

1.0 Junior🔥 281 комментариев
#Базы данных и SQL

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

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

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

Опыт работы с различными базами данных

Я работал с различными типами баз данных, и каждая имеет свои сильные стороны. Расскажу о моём опыте и подходе к выбору БД для разных задач.

PostgreSQL — основная БД для большинства проектов

Это мой выбор по умолчанию для 80% приложений. Почему:

  • ACID гарантии — надёжные транзакции
  • JSON/JSONB — гибкость без потери типизации
  • Full-text search — встроенный поиск
  • Отличные индексы — B-tree, GiST, GIN
  • Зрелость — 25+ лет разработки
// Пример работы с PostgreSQL в Node.js
const { Pool } = require('pg');

const pool = new Pool({
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  host: process.env.DB_HOST,
  port: 5432,
  database: process.env.DB_NAME
});

// Параметризованные запросы (защита от SQL injection)
const getUserById = async (userId) => {
  const result = await pool.query(
    'SELECT id, email, role FROM users WHERE id = $1',
    [userId]
  );
  return result.rows[0];
};

// Транзакции для atomicity
const transferMoney = async (fromUserId, toUserId, amount) => {
  const client = await pool.connect();
  
  try {
    await client.query('BEGIN');
    
    // Блокируем строки для безопасности
    await client.query(
      'UPDATE accounts SET balance = balance - $1 WHERE user_id = $2 FOR UPDATE',
      [amount, fromUserId]
    );
    
    await client.query(
      'UPDATE accounts SET balance = balance + $1 WHERE user_id = $2',
      [amount, toUserId]
    );
    
    await client.query('COMMIT');
  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release();
  }
};

// JSONB для гибкости
const storeMetadata = async (userId, metadata) => {
  await pool.query(
    'UPDATE users SET metadata = $1 WHERE id = $2',
    [JSON.stringify(metadata), userId]
  );
};

// Full-text search
const searchUsers = async (query) => {
  const result = await pool.query(`
    SELECT id, email, name
    FROM users
    WHERE to_tsvector('english', name || ' ' || email) @@ plainto_tsquery('english', $1)
    LIMIT 10
  `, [query]);
  return result.rows;
};

// Миграции с Goose (raw SQL)
// 0001_create_users.sql
/*
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  password VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
*/

MongoDB — когда структура данных не определена

Использую для:

  • Быстрого прототипирования — schema может меняться
  • Документоориентированных данных — logs, events, analytics
  • Высокого масштабирования — горизонтальное шардирование
const { MongoClient } = require('mongodb');

const client = new MongoClient(process.env.MONGO_URI);
const db = client.db('myapp');

// Гибкая схема
const storeEvent = async (event) => {
  const result = await db.collection('events').insertOne({
    type: event.type,
    userId: event.userId,
    data: event.data, // Может содержать что угодно
    timestamp: new Date(),
    metadata: {
      ip: event.ip,
      userAgent: event.userAgent
    }
  });
  return result.insertedId;
};

// Aggregation pipeline для аналитики
const getUserAnalytics = async (userId) => {
  const pipeline = [
    { $match: { userId: new ObjectId(userId) } },
    { $group: {
      _id: '$type',
      count: { $sum: 1 },
      lastEvent: { $max: '$timestamp' }
    } },
    { $sort: { count: -1 } }
  ];
  
  return await db.collection('events').aggregate(pipeline).toArray();
};

// Transaction (MongoDB 4.0+)
const session = client.startSession();
await session.withTransaction(async () => {
  await db.collection('orders').insertOne({ ...order }, { session });
  await db.collection('inventory').updateOne({}, { ...update }, { session });
});

Redis — кеширование и real-time данные

Использую для:

  • Сессии — быстрый доступ
  • Кеш — уменьшение нагрузки на БД
  • Real-time features — WebSocket, pub/sub
  • Rate limiting — счётчик запросов
  • Background jobs — через Bull
const redis = require('redis');
const client = redis.createClient();

// Сессии
const storeSession = async (sessionId, data) => {
  await client.setEx(
    `session:${sessionId}`,
    3600, // 1 час TTL
    JSON.stringify(data)
  );
};

// Кеширование результатов дорогих операций
const getCachedUser = async (userId) => {
  const cacheKey = `user:${userId}`;
  
  // Проверяем кеш
  let user = await client.get(cacheKey);
  if (user) {
    return JSON.parse(user);
  }
  
  // Если нет в кеше — достаём из БД
  user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
  
  // Сохраняем в кеш на 5 минут
  await client.setEx(cacheKey, 300, JSON.stringify(user));
  
  return user;
};

// Rate limiting
const checkRateLimit = async (userId, limit = 100, window = 3600) => {
  const key = `rate:${userId}`;
  const current = await client.incr(key);
  
  if (current === 1) {
    await client.expire(key, window);
  }
  
  if (current > limit) {
    throw new Error('Rate limit exceeded');
  }
};

// Pub/Sub для real-time notifications
const subscriber = redis.createClient();
await subscriber.subscribe('notifications', (message) => {
  console.log('Notification:', message);
});

// Publisher
const notifyUser = async (userId, message) => {
  await client.publish('notifications', JSON.stringify({
    userId,
    message,
    timestamp: new Date()
  }));
};

// Background jobs с Bull
const Queue = require('bull');
const emailQueue = new Queue('emails', process.env.REDIS_URL);

emailQueue.process(async (job) => {
  await sendEmail(job.data.email, job.data.subject);
});

const sendEmailAsync = async (email, subject) => {
  await emailQueue.add(
    { email, subject },
    { attempts: 3, backoff: { type: 'exponential', delay: 2000 } }
  );
};

Elasticsearch — полнотекстовый поиск и аналитика

Использую для:

  • Мощного поиска — лучше чем PostgreSQL full-text
  • Логирования — ELK стек
  • Аналитики — агрегация больших объёмов данных
const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });

// Индексирование
const indexProduct = async (product) => {
  await client.index({
    index: 'products',
    id: product.id,
    body: {
      name: product.name,
      description: product.description,
      price: product.price,
      category: product.category
    }
  });
};

// Сложный поиск
const searchProducts = async (query, filters) => {
  const result = await client.search({
    index: 'products',
    body: {
      query: {
        bool: {
          must: [
            { multi_match: {
              query,
              fields: ['name^2', 'description']
            } }
          ],
          filter: [
            { range: { price: { gte: filters.minPrice, lte: filters.maxPrice } } },
            { term: { category: filters.category } }
          ]
        }
      }
    }
  });
  return result.hits.hits;
};

Выбор БД для конкретного проекта

Задача                        | БД                  | Причина
------------------------------|--------------------|-----------
Ecommerce (заказы, товары)   | PostgreSQL          | ACID, надёжность
Реал-тайм чат, уведомления   | Redis + PostgreSQL  | Redis для скорости
Мобильное приложение          | MongoDB             | гибкость, offline-first
Аналитика и логирование       | Elasticsearch       | масштабирование
Кеширование                   | Redis               | скорость
Полнотекстовый поиск          | Elasticsearch       | мощный поиск
Микросервисная архитектура    | PostgreSQL + Redis  | независимость
IoT данные (временные ряды)   | TimescaleDB         | оптимизация

Лучшие практики

// 1. Connection pooling
const pool = new Pool({ max: 20 });

// 2. Prepared statements (защита от SQL injection)
const query = 'SELECT * FROM users WHERE id = $1'; // $1 вместо конкатенации

// 3. Индексы на часто используемые поля
// CREATE INDEX idx_users_email ON users(email);

// 4. Мониторинг медленных запросов
// Запросы > 1 сек логируем

// 5. Кешируем результаты в Redis
const getData = async (key) => {
  let data = await redis.get(key);
  if (data) return JSON.parse(data);
  
  data = await db.query(...);
  await redis.setEx(key, 3600, JSON.stringify(data));
  return data;
};

// 6. Транзакции для критичных операций
await db.query('BEGIN');
try {
  // несколько операций
  await db.query('COMMIT');
} catch (error) {
  await db.query('ROLLBACK');
}

Мой подход: PostgreSQL для основного приложения + Redis для кеша и real-time + специализированные БД где необходимо (Elasticsearch для поиска, MongoDB для специфичных случаев).

С какой базой данных работаешь | PrepBro