N+1 xảy ra khi lấy một danh sách rồi lặp gọi thêm một truy vấn con cho từng phần tử: 1 query lấy N bản ghi cha + N query lấy dữ liệu con → N+1 lần vào DB. Thường gặp với ORM khi truy cập quan hệ trong vòng lặp (lazy loading).
Ví dụ: lấy 100 bài viết (1 query) rồi với mỗi bài lại query tác giả (100 query) = 101 lần round-trip.
Khắc phục:
- JOIN: gộp cha + con trong một truy vấn.
- Eager loading: ORM nạp sẵn quan hệ (Prisma include, ActiveRecord includes, Hibernate JOIN FETCH).
- Batch / WHERE IN (...): gom N id con thành một truy vấn (DataLoader dùng cách này).
Cái giá không nằm ở tính toán mà ở số lần round-trip mạng tới DB — giảm số query là chính. Phát hiện bằng log query hoặc APM.
-- N+1: 1 + 100 round-trips
SELECT * FROM posts LIMIT 100;
SELECT * FROM users WHERE id = ?; -- repeated for each post
-- Fix: batch into one query
SELECT * FROM users WHERE id IN (1, 2, ...);