Столкнулись ли вы с проблемами нагруженности таблиц и как их решали
Что проверяют
Проверяют, работали ли вы с базой, которая уже не помещается в голову: умеете ли найти виновный запрос, а не угадать его, и понимаете ли, что нагрузка бывает разная. Медленное чтение, конкуренция за блокировки и распухание таблицы от обновлений лечатся по-разному, и ответ «добавил индексы» показывает, что кандидат видел только первый случай.
Как отвечать
Начните с того, как искали, а не что чинили. В Postgres это pg_stat_statements (суммарное время, а не одиночный медленный запрос), затем EXPLAIN (ANALYZE, BUFFERS) на конкретном запросе, pg_stat_activity с полем wait_event для блокировок и pg_stat_user_tables для живых и мёртвых строк.
Дальше по находке:
- нет индекса или он не подходит под порядок колонок: добавляем, на боевой таблице только
CREATE INDEX CONCURRENTLY; - частичный индекс
WHERE is_activeвместо полного, если выборка всегда идёт по подмножеству; - запрос читает лишнее: пагинация по ключу вместо
OFFSET, покрывающий индекс сINCLUDE; - распухание от частых обновлений: настройка автовакуума на конкретную таблицу, отказ от обновления индексируемых колонок ради обновления на месте;
- рост объёма: секционирование по дате, отдельная холодная таблица для архива.
Со стороны Go скажите про пул: SetMaxOpenConns ограничивает нагрузку на базу, SetConnMaxLifetime спасает при смене мастера. Для очереди в таблице назовите FOR UPDATE SKIP LOCKED.
Где спотыкаются
Вешают индекс на каждое поле из WHERE. Чтение почти не выигрывает, запись замедляется на каждой вставке, и вдобавок планировщик начинает выбирать не тот индекс. Индекс добавляется под конкретный план, подтверждённый через EXPLAIN.
Путают медленный запрос с ожиданием. Если сверху висит долгая транзакция или ALTER TABLE, время в логе большое, но сам запрос дешёвый, и оптимизировать там нечего.
Отвечают кэшем на любой вопрос. Кэш не убирает нагрузку, он переносит её на момент инвалидации, и без ответа на вопрос «что делаем при промахе и как чистим» это выглядит заплаткой.