← Назад к вопросам
Что будешь делать с медленным SQL-запросом
2.0 Middle🔥 131 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Оптимизация медленного SQL-запроса: систематический подход
Это очень важный практический вопрос! Я имею структурированный методологический подход к диагностике и оптимизации медленных запросов.
Мой пошаговый подход
Шаг 1: Диагностика
Сначала измеряю время выполнения и использую инструменты:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public List<Order> getActiveOrders() {
long startTime = System.currentTimeMillis();
List<Order> orders = orderRepository.findActiveOrdersWithCustomer();
long duration = System.currentTimeMillis() - startTime;
logger.info("Query took: {} ms", duration);
return orders;
}
}
Инструменты для профилирования:
- EXPLAIN PLAN (PostgreSQL/MySQL)
- Hibernate query logging
- Slow query logs
- Spring Boot Actuator metrics
Шаг 2: Анализ плана запроса
Смотрю на:
- Sequential vs Index Scan
- Количество прочитанных строк
- Join методы
- Стоимость операции
Шаг 3: Типовые проблемы и решения
Проблема 1: N+1 queries (самая частая!)
// ПЛОХО: N+1
List<Order> orders = orderRepository.findAll();
orders.forEach(order -> {
order.getCustomer().getName(); // +N queries
});
// ХОРОШО: JOIN FETCH
@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findActiveOrdersWithCustomer();
Проблема 2: Отсутствие индексов
-- МЕДЛЕННО: full scan
SELECT * FROM orders WHERE status = 'ACTIVE' AND created_at > ?;
-- БЫСТРО: с индексом
CREATE INDEX idx_orders_status_created ON orders(status, created_at);
Проблема 3: Большие JOIN'ы
-- ПЛОХО: много дублирования
SELECT * FROM orders o JOIN customers c JOIN items i;
-- ХОРОШО: только нужные поля
SELECT o.id, o.amount, c.name, COUNT(i.id)
FROM orders o
JOIN customers c ON o.customer_id = c.id
LEFT JOIN items i ON o.id = i.order_id
GROUP BY o.id;
Шаг 4: Оптимизационные техники
Техника 1: Pagination
Page<Order> page = orderRepository.findAll(
PageRequest.of(0, 100, Sort.by("created_at"))
);
Техника 2: Caching
@Cacheable("orders-by-status")
public List<Order> getActiveOrders() {
return orderRepository.findActiveOrdersWithCustomer();
}
Техника 3: DTO Queries
@Query("SELECT new com.example.OrderDTO(o.id, o.amount, c.name) " +
"FROM Order o JOIN o.customer c")
List<OrderDTO> findOrderDTOs();
Техника 4: Индексы в БД
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_customer_date ON orders(customer_id, created_at DESC);
ANALYZE TABLE orders;
Реальный пример оптимизации
// БЫЛО: 5 секунд
List<Order> orders = orderRepository.findByCreatedDateBetween(
month.atStartOfDay(), month.plusMonths(1).atStartOfDay()
); // Нет индекса
orders.forEach(o -> o.getCustomer().getName()); // N+1
// СТАЛО: ~50ms
@Query("SELECT new com.example.OrderReportDTO(" +
"o.id, o.amount, c.name, COUNT(i.id)) " +
"FROM Order o " +
"JOIN o.customer c " +
"LEFT JOIN o.items i " +
"WHERE o.created_at >= :startDate " +
"GROUP BY o.id")
List<OrderReportDTO> findMonthlyReport(
@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate
);
Мой контрольный список
- EXPLAIN PLAN - смотрю на план запроса
- Индексы - правильные индексы это 90% оптимизации
- N+1 queries - JOIN FETCH или отдельные запросы
- Pagination - для больших результатов
- Caching - для часто используемых данных
- Monitoring - постоянно отслеживаю performance
- Load testing - тестирую под реальной нагрузкой
- Communication - обсуждаю с DBA
Итог
Мой подход систематический: измеряю → профилирую → анализирую → оптимизирую → мониторю. Каждый запрос требует анализа.