Какие проблемы могут быть в SQL-запросах с использованием select
GolangSQLбазы данныхошибки запросовпроизводительность
Что проверяют
Два слоя сразу: что делает медленным или неверным сам запрос и как Go-код вокруг него ломается под нагрузкой. Следом обычно просят разобрать план через EXPLAIN ANALYZE или найти ошибку в цикле с rows.Next().
Как отвечать
На стороне SQL:
- *`SELECT
**: тянет лишние колонки, включая тяжёлыеtextиjsonb`, мешает index-only scan и ломает код при добавлении колонки, если сканирование идёт по позиции; - индекс не работает: функция над колонкой (
lower(email) = $1), неявное приведение типов,LIKE '%x'с ведущим процентом, низкая селективность; NOT INиNULL: если подзапрос вернул хоть одинNULL, результат пустой, надёжнееNOT EXISTS;- N+1: запрос в цикле по результатам другого. Заменяется
JOINили однимWHERE id = ANY($1); - нет
LIMIT: весь результат уходит в память сервиса.
На стороне Go (database/sql, pgx):
- незакрытые
rows: безdefer rows.Close()соединение не возвращается в пул, и со временем все запросы ждут свободного; - непроверенный
rows.Err():rows.Next()мог прерваться из-за сетевой ошибки, а код решит, что данные кончились; NULLвstring:Scanвернёт ошибку, нуженsql.NullString, указатель илиCOALESCE;sql.ErrNoRows:QueryRow().Scanвозвращает её, когда строки нет, и отличать её от настоящей ошибки нужно черезerrors.Is;- инъекции:
fmt.Sprintfсо значениями от пользователя. Только плейсхолдеры$1или?, а имена колонок для сортировки через белый список; - нет контекста: без
QueryContextс таймаутом зависший запрос держит горутину и соединение.
Где спотыкаются
Ограничиваются советом «добавить индекс». Без EXPLAIN ANALYZE это гадание, индекс может не подойти по порядку колонок.
Забывают про rows.Close() и пул. Утечка соединений проявляется не сразу, а под нагрузкой, и это частая причина инцидентов в Go-сервисах.
Думают, что плейсхолдер защищает везде. Имя таблицы или направление сортировки параметром не передать, и склейка строк появляется именно там.