Как оптимизировать медленную загрузку главной страницы с постами
Что проверяют
Будете ли вы сначала искать узкое место или сразу начнёте перечислять всё, что знаете об оптимизации. Для Go-разработчика основная часть ответа про бэкенд: запросы к базе, кэш, конкурентность. Следом обычно спрашивают, что будет, когда кэш протух и сотни запросов одновременно пошли в базу.
Как отвечать
Начните с измерения. Разделите время на сервер и клиент: TTFB в DevTools показывает, сколько ждали ответа. Если долго отвечает сервер, смотрите трассировку, pprof и EXPLAIN ANALYZE на запросы.
Типичные причины на стороне Go:
- N+1 запросов: список постов, а потом отдельный запрос автора и лайков на каждый пост. Лечится
JOINили вторым запросом сWHERE id = ANY($1), счётчики лучше держать денормализованными; - пагинация:
OFFSET 10000заставляет базу прочитать и выбросить десять тысяч строк. Keyset-пагинация по(created_at, id)с индексом одинаково быстра на любой странице; - кэш: главная одинакова для большинства, её данные или готовый HTML кладут в Redis или в память процесса с коротким TTL. От лавины промахов защищает
singleflightизgolang.org/x/sync: одновременные запросы за одним ключом ждут один поход в базу; - параллельность: независимые блоки (посты, популярное, баннеры) собираются параллельно через
errgroupс общимcontextи таймаутом; - пул соединений:
SetMaxOpenConnsиSetMaxIdleConnsуdatabase/sqlили настройкиpgxpool. Слишком маленький пул выстраивает запросы в очередь; - ответ: сжатие gzip или brotli,
Cache-ControlиETag, шаблоныhtml/templateразбираются один раз при старте, а не на каждый запрос.
Если сервер отвечает быстро, а страница медленная, дело во фронте: тяжёлые картинки без loading="lazy" и форматов WebP или AVIF, блокирующие скрипты без defer.
Где спотыкаются
Предлагают сразу Redis, ничего не померив. Кэш прячет N+1 и медленный запрос до первого промаха.
Запускают горутину на каждый пост без ограничения и без context. Под нагрузкой это сотни одновременных запросов в базу и исчерпанный пул.
Советуют перенести посты в NoSQL. Проблема почти всегда в запросах и индексах, а не в типе хранилища.