JBuilder N+1 query 해결
by Mingdo
댓글 리스트 조회 작업 중 N+1 쿼리 문제를 겪고 해결한 방법
1. N+1 쿼리 문제란 ?
- 쿼리 1번으로 N건을 가져왔는데, 관련 컬럼을 얻기 위해 쿼리를 N번 추가 수행하는 문제
문제 상황 예제
$books = query_rows("SELECT * FROM books");
foreach( $books as &$book ) {
$book['author_name'] = query_one("SELECT name FROM authors WHERE id=?", $book['author_id']);
}
-> book N건을 얻기 위한 쿼리 1회
-> author_name을 얻기 위한 쿼리 N회
-> 성능 측면에서 극히 불합리하지만, 로직을 이해하기 쉽다는 장점은 있음
해결 상황 예제
$books = query_rows("SELECT b.*, a.author_name
FROM books b LEFT JOIN authors a ON b.author_id=a.id");
2. 문제 해결
필요한 데이터
주문 댓글 리스트 -> 주문 -> 주문 상품 리스트
- 주문 댓글 (order_comment) : 댓글 정보
- 주문 (order) : 주문 날짜
- 주문 상품 리스트 (order_goods) : 주문 상태
N+1 쿼리 문제 상황
order_comments.each do |comment|
contents = comment.contents
order_date = comment.order.created_at
# order_goods.first 실행 시 N+1 쿼리 문제 발생
order_status = comment.order.order_goods.first.status
end
- has_many 관계에 있는 order_goods 의 첫 번째 데이터를 조회할 때마다 select 쿼리가 실행 돼 N+1 쿼리 문제가 발생
- find, find_by, first, last, pluck 등의 쿼리 메서드 사용 즉시 데이터베이스에 접근해 데이터를 조회
- first 를 사용하지 않고 주문의 첫 번째 상품을 조회 해야 함
N+1 쿼리 문제 해결
- rails 에선 model 간 관계를 지정할 수 있는데 order 와 order_goods 같은 경우는 아래와 같이 has_many 관계로 맺어짐
class Order has_many :order_goods
- 실제로 has_many 관계에 있다 해도 임의로 has_one 관계 설정을 통해 order 와 order_goods 가 1:1 의 관계로 사용 가능
class Order # 실제 relation has_many :order_goods # 커스텀 relation, order_goods_get_first 의 실제 모델인 OrderGoods 명시 필요 has_one :order_goods_get_one, class_name: 'OrderGoods'
- has_one 관계로 데이터 조회
order_comments.each do |comment| contents = comment.contents order_date = comment.order.created_at # N+1 쿼리 문제 해결 order_status = comment.order.order_goods_get_one.status end
마무리..
Subscribe via RSS