Ответ
Оптимизация запросов к БД — это основа производительности Node.js-приложения. Вот мои основные практики:
1. Использование пула соединений. Это предотвращает накладные расходы на установление нового соединения для каждого запроса. Например, с pg (PostgreSQL):
const { Pool } = require('pg');
const pool = new Pool({
max: 20, // Максимальное число клиентов в пуле
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Использование
const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
2. Эффективное кеширование. Для часто запрашиваемых и редко меняющихся данных я использую Redis.
const getCachedUser = async (userId) => {
const cacheKey = `user:${userId}`;
let user = await redisClient.get(cacheKey);
if (!user) {
// Кеш-промах: идем в БД
const { rows } = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
user = rows[0];
// Сохраняем в Redis с TTL (например, 5 минут)
await redisClient.setEx(cacheKey, 300, JSON.stringify(user));
} else {
user = JSON.parse(user);
}
return user;
};
3. Оптимизация самих SQL-запросов:
- Индексы: Анализирую медленные запросы с помощью
EXPLAIN ANALYZEи добавляю индексы на поля в условияхWHERE,JOINиORDER BY. - Селективность: Выбираю только нужные колонки (
SELECT id, nameвместоSELECT *). - Пакетные операции (Batching): Объединяю несколько мелких операций в одну.
// Вместо N запросов в цикле — один запрос с IN const userIds = [1, 2, 3, 4]; const { rows } = await pool.query( 'SELECT * FROM users WHERE id = ANY($1::int[])', [userIds] ); - Prepared Statements: Использую параметризованные запросы (как в примерах выше) для безопасности от SQL-инъекций и возможности повторного использования плана выполнения СУБД.
4. Пагинация. Для больших наборов данных всегда реализую пагинацию на уровне БД с помощью LIMIT и OFFSET (или лучше WHERE id > lastId для keyset-пагинации).
5. Мониторинг. Подключаю логирование медленных запросов и использую инструменты вроде pg_stat_statements для постоянного анализа и выявления узких мест.