> Какие есть способы оптимизации тяжелых запросов к базе данных (PHP)

Уровень: middle · Роль: backend · Язык: PHP · Категория: Технические вопросы

Компании: витринатв

Стек: PHP

> Пример ответа

Оптимизация тяжелых запросов в PHP-приложениях обычно включает несколько этапов. Первым делом стоит проанализировать запрос через EXPLAIN - это покажет, используются ли индексы, где происходит полное сканирование таблиц и какие объемы данных обрабатываются. Затем применяются следующие техники:

  1. Индексация: создание составных индексов для полей, участвующих в WHERE, JOIN, ORDER BY. Например, для запроса с фильтрацией по user_id и сортировкой по created_at нужен индекс (user_id, created_at).

  2. Оптимизация JOIN: избегать вложенных циклов, использовать STRAIGHT_JOIN для принудительного порядка соединения, если оптимизатор ошибается.

  3. Пагинация на основе курсора: вместо OFFSET с большим смещением использовать WHERE id > last_seen_id LIMIT 20, что позволяет избежать сканирования пропущенных строк.

  4. Агрегации и подзапросы: заменить коррелированные подзапросы на JOIN с группировкой, а тяжелые COUNT(DISTINCT) - на приблизительные оценки через APPROX_COUNT_DISTINCT (если СУБД поддерживает) или отдельные таблицы-счетчики.

  5. Кэширование результатов: для редко меняющихся данных использовать Redis или Memcached, инвалидируя кеш при записи. Например, сложный отчет можно кешировать на 5 минут.

  6. Денормализация: добавление вычисляемых полей (например, total_comments в таблицу постов) для избежания частых COUNT с JOIN.

  7. Партиционирование: для таблиц с миллионами записей разбить данные по дате или хешу, чтобы запросы сканировали только нужный раздел.

Пример на PHP: если запрос к таблице orders с фильтром по status и сортировкой по created_at выполняется медленно, проверяем индексы, добавляем композитный индекс (status, created_at), а для пагинации используем WHERE status = 'active' AND created_at < :cursor ORDER BY created_at DESC LIMIT 20. В коде это может выглядеть так:

PHP
$stmt = $pdo->prepare('SELECT * FROM orders WHERE status = :status AND created_at < :cursor ORDER BY created_at DESC LIMIT 20');
$stmt->execute(['status' => 'active', 'cursor' => $lastCreatedAt]);

Такой подход снижает нагрузку на БД и ускоряет ответ.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью