Почему запросы в базе данных могут быть плохими
Что проверяют
Понимаете ли вы, из чего складывается стоимость запроса, и умеете ли искать причину, а не угадывать. Для Java это ещё и вопрос про ORM: заметная часть плохих запросов не написана руками, их сгенерировал Hibernate.
Как отвечать
Определите «плохой» как «читает или блокирует больше, чем нужно для результата», и разложите на причины:
- нет подходящего индекса: условие отбирает мало строк, но планировщик вынужден сканировать таблицу целиком;
- индекс есть, но не применяется: функция или приведение типа поверх колонки (
where lower(email) = ?, строка против числа) делают его бесполезным, помогает индекс по выражению; - выбирается лишнее:
select *тянет большие колонки и ломает покрывающий индекс, отсутствиеlimitзаставляет материализовать всю выборку; - много обращений вместо одного: классическая N+1 от ленивых связей;
- постраничность через большой
offset: база отбрасывает все пропущенные строки, дешевле идти курсором по ключу; - долгая транзакция: она держит снимок и мешает чистке старых версий строк.
Дальше скажите, как проверяете: explain analyze вместо догадок, сравнение оценки строк с фактом (расхождение означает устаревшую статистику), лог медленных запросов, а со стороны приложения перехват через p6spy или datasource-proxy, чтобы увидеть, что реально ушло в базу из JPA.
Где спотыкаются
Предлагают «разбить сложный запрос на несколько простых». Один join почти всегда дешевле пяти обходов по сети, и именно такое дробление порождает N+1.
Второе: считают, что чтение блокирует запись. В PostgreSQL и в InnoDB работает многоверсионность, обычный select писателей не ждёт и сам их не блокирует, зато ждут select for update и операции изменения схемы.
Третье: советуют навесить индексы на все колонки. Каждый индекс оплачивается на записи, а десяток пересекающихся индексов ещё и путает планировщик; правильнее один составной под конкретный фильтр и сортировку.