Что лучше: выбрать из базы одним запросом тысячу id или сделать тысячу запросов по одному id
Что проверяют
Знаете ли вы, что в походе в базу дороже всего круговая задержка, а не сам поиск по индексу, и не свалитесь ли в противоположную крайность, отправив в базу список на сто тысяч значений.
Как отвечать
Один запрос. Тысяча запросов по одному это классический N+1. Аргументация в числах: каждый запрос это круговая задержка плюс разбор и планирование, и при задержке даже в полмиллисекунды тысяча обращений даёт полсекунды чистого ожидания, тогда как один запрос по индексу вернёт те же строки за единицы миллисекунд.
Как это пишется в Go:
where id = any($1)с массивом вместо собранного строкойin. Текст запроса один на любую длину списка, значит работают подготовленные запросы и кэш планов не засоряется;- список не бесконечен: в расширенном протоколе PostgreSQL число параметров ограничено значением 65535, да и план по огромному списку деградирует. Тысячу берём одним запросом, сто тысяч режем пачками;
- результат складываем в
map[int64]Rowи раскладываем в нужном порядке: безorder byпорядок строк не гарантирован, а отсутствующие идентификаторы просто не вернутся, и это надо отличать от ошибки; defer rows.Close()и проверкаrows.Err()после цикла, иначе потерянная ошибка выглядит как неполный ответ.
Где спотыкаются
Склеивают список для in конкатенацией строк. Это и путь к инъекции, и тысяча разных текстов запроса, забивающих кэш планов на стороне базы.
Второе: уходят в другую крайность и запускают тысячу запросов в горутинах. Они упираются в размер пула соединений, база получает всплеск параллельных сессий, и становится хуже, чем при последовательном варианте.
Третье: не смотрят на объём ответа. Если по каждому идентификатору тянется широкая строка с текстовыми полями, один ответ на тысячу записей съест память и сеть, поэтому берут только нужные колонки и режут выборку пачками.