Якщо сторінка відкривається за
50 ms на локалці, але падає в продакшені — дуже часто це N+1.Rails робить асоціації зручними.
І саме ця зручність іноді дорого коштує.
Ми всі через це проходили 🙂
Класичний приклад
users = User.all
users.each do |user|
puts user.posts.count
end
Здається невинно.
Але під капотом:
1 запит за всіма usersN запитів за posts для кожного userЯкщо користувачів
200 — це вже 201 SQL-запит.Чому це небезпечно
🔹 Проблема не помітна на маленьких даних
🔹 Логи виглядають “нормально”
🔹 Ламається тільки під навантаженням
🔹 Важко відловити без моніторингу
N+1 — це не баг. Це “тиха деградація”.Як це виправити?
1️⃣ includes
users = User.includes(:posts)
Rails зробить
2 запити замість N+1.2️⃣ preload vs eager_load
includes — універсальний варіантpreload — окремі запитиeager_load — JOINІноді
JOIN краще, іноді — ні. Треба дивитись у SQL.3️⃣ counter_cache
Якщо ви лише рахуєте:
add_column :users, :posts_count, :integer, default: 0
І:
belongs_to :user, counter_cache: true
І більше ніяких
.count у циклі.4️⃣ Дивитись у EXPLAIN
Якщо не дивишся в
SQL — ти не контролюєш продуктивність.Маленьке правило
Якщо в коді є
.each + асоціація — перевір, скільки запитів реально виконується.💬 Хто ловив N+1 уже після релізу? І через що помітили —
Sentry, NewRelic чи “клієнт написав”? 😄TikTok | Instagram | Telegram