【Rails】N+1 문제에 대한 정리

N+1 문제란?



N + 1 문제는 데이터베이스에서 데이터를 검색 할 때 많은 양의 SQL이 발행되어 성능이 저하되는 문제입니다.

N+1 문제의 구체적인 예

rails는 all 및 find 메소드를 사용하여 데이터베이스에서 데이터를 검색합니다.
터미널의 로그를 보면 실제로는 아래와 같이 그때마다 SQL이 실행되고 있습니다.

SQL
Product Load (2.7ms)  SELECT `products`.* FROM `products`

users 테이블


id
이름


1
야마다

2
아라이

3
다나카

4
키타가와


products 테이블


id
제품
group_id


1
카레
2

2
물고기
1

3
불고기
3

4
사시미
1


"사용자 하나는 여러 제품을 가진 관계이므로 사용자 모델에 has_many 메소드를 정의하고 제품 모델에 belongs_to 메소드를 정의합니다.

사용자 및 제품 모델에 대한 연결 정의

UserモデルとProductモデルにアソシエーション定義
# User.rb
class User < ActiveRecord::Base
    has_many :Products
end

# Product.rb
class Product < ActiveRecord::Base
    belongs_to :User
end


모든 소유의 상품 일람」을 view로 표시하고 싶은 경우에, controller측에서 모든 user를 all 메소드로 취득해, view측에서 주인이 가지는 products를 어소시에이션에 의해 아래와 같이 기술할 수가 있습니다.

N+1 문제가 발생하는 코드 확인

# controller
@users = User.all

# view
@users.each do |user|
  user.products.each do |product|
    product.name
  end
end

「User.all」의 코드가 실행되면, 「users 테이블로부터 users 테이블의 모든 컬럼」이 취득.
이 SQL에 의해, users 테이블에 1회의 액세스.

다음에 veiw측. SQL을 보면, products 테이블에 대해, 4회의 액세스가 행해지고 있다.

N+1 문제가 발생하는 코드
@groups.each do |group|
  group.products.each do |product| 
    cat.name
  end
end

# このコードが4回のSQL文を発行
SELECT `products`.* FROM `products`  WHERE `products`.`group_id` = 1
SELECT `products`.* FROM `products`  WHERE `products`.`group_id` = 2
SELECT `products`.* FROM `products`  WHERE `products`.`group_id` = 3
SELECT `products`.* FROM `products`  WHERE `products`.`group_id` = 4

each 메소드에서 @groups이 가진 users 테이블에서 모든 레코드를 하나씩 그룹에 넣고 있습니다.

SQL이 "users 테이블에 대한 액세스가 한 번"에 대해 "products 테이블에 대한 액세스가 그룹 테이블의 레코드 수 (4 회)"발행

이와 같이 액세스 1회에 대하여, 관련 테이블이 N회 발행되고 있는 1+N의 상황을 「N+1 문제」라고 한다.

.

N+1 문제 해결



includes 메소드

includes 메소드
includesメソッドの使用例 -->
@users = User.includes(:user)
@users = User.allで取得していた箇所を@users = User.includes(:products)に変更します

includes 메소드
@users = User.includes(:products) # User.allから変更

 # 発行される2つのSQL
SELECT `users`.* FROM `users` 
SELECT `products`.* FROM `products`  WHERE `products`.`group_id` IN (1, 2, 3, 4)

두 개의 SQL이 발행되었습니다. 첫 번째 줄은 users 테이블의 모든 레코드를 검색하는 SQL 문입니다.
두 번째 행은 제품 테이블에서 WHERE 절에 지정된 조건과 일치하는 레코드를 검색합니다.

요약



includes 메소드를 사용하지 않는 경우는, 관련하는 user_id 컬럼의 값을 1개씩 지정해 취득하고 있었으므로 products 테이블에 4회의 액세스가 필요했습니다만, IN구로 컬럼의 값을 정리해 지정했다 일에 의해 1회로 취득할 수 있게 되었습니다. includes 메소드를 사용하여 레코드를 수집하여 필요 이상으로 SQL을 발행하지 않고 성능을 향상시킬 수 있습니다.

좋은 웹페이지 즐겨찾기