Ruby, PostgreSQL и Rails — мощный стек для разработки, но даже в нём есть подводные камни. Разберёмся, как эффективно работать с базами данных, избегать узких мест в производительности и выстраивать надёжные DevOps-процессы.
Ты живёшь с JOIN в проде, всё быстро и стабильно. Но однажды вторая таблица переваливает за 1000 записей — и внезапно запрос начинает тормозить. Почему?
🔥 Что произошло?
До порога в ~1К строк Postgres мог:
- выбирать оптимальный план (
Nested Loop,Index Scan) - моментально находить записи по индексу
- делать всё в памяти (
shared_buffers)
Но как только таблица выросла — Postgres пересчитал план.
📉 Как это выглядит в EXPLAIN?
Nested Loop
-> Seq Scan on main_table
-> Index Scan on second_table
Index Cond: ...
Проблема: внешняя таблица большая, а Index Scan выполняется для каждой строки.
🚨 Постепенный рост — внезапная боль
Postgres выбирает план, исходя из статистики (ANALYZE), и думает:
«Да тут всего 999 строк, нормально!»
Но на 1001-й — Nested Loop превращается в ад: сотни тысяч обращений к индексу. Прод “висит”, CPU забито, Postgres не успевает.
💡 Как это исправить?
- Проверь план через
EXPLAIN (ANALYZE, BUFFERS) - Сравни
actual rowsиestimated rows— если они сильно различаются, это сигнал - Запусти
ANALYZEилиVACUUM ANALYZEвручную - Укажи
JOINявно (вместоWHERE ... IN) - Попробуй форсировать план:
SET enable_nestloop = off;
(не в проде! Только чтобы проверить, поможет ли Hash Join)
🛡️ Профилактика
- Регулярный
ANALYZE - Мониторинг прироста строк в таблицах
- Логгирование медленных запросов (
log_min_duration_statement) - Тестирование SQL-запросов при объёмах, в 10 раз превышающих текущие
📌 Вывод: не доверяй малым таблицам. Они вырастают — и в самый плохой момент рушат тебе прод. Следи за ростом и будь готов поменять стратегию.