아무래도 Rails 백엔드 개발은 ActiveRecord의 Subclass에서 정의된 모델을 이용해 DB에 직접 Query를 날려서 CRUD 작업을 하는데, 이 때 알아야 할 이슈가 N+1 Query Problem 입니다.
N+1 Query Problem 의 영문 정의는 his problem occurs when the code needs to load the children of a parent-child relationship (the “many” in the “one-to-many” 이다. 즉, 부모 자식 관계에 있으며 자식과 부모의 db 관계가 자식:부모 => many to one 관계로 있는 두 db 테이블에서 자식 테이블을 쿼리를 통해 갖고 올 때 일어나는 문제입니다.
예를 들어봅시다. 어떤 서비스의 DB 구조에서 유저 정보가 잇는 1. User Table, 유저의 인증 정보를 저장하는 2. User_authentication Table이 있다고 합시다. User 테이블은 부모 테이블이고 user_authentication 테이블은 User 테이블의 primary key 값을 외래키(foreign key)로 갖는, 부모 자식 관계라고 합시다. 이때 유저 인증 table을 특정 유저로 물어서 갖고 올 때 유저 table을 갖고 오면서 발생하는 쿼리 +1 에 그 자식에 해당하는 유저 인증 table에서 각 row를 갖고올때마다 쿼리가 N 번 발생하여 총 N+1 번 발생하게 되는데, 이런 상황에서 발생하는 문제가 N+1 쿼리 problem이다. 꼭 N+1 이라는 숫자를 정확히 명시하는건 아니고.. 각 쿼리가 오버헤드면 통칭 N+1 query problem이라고 합니다. 여기서 오버헤드는 가성비가 좋지 않은 쿼리(?) 정도로 생각합시다.
RoR에서 N+1 Query Problem 을 해결하기 위한 방법은, Eager Loading 이며, ActiveRecord 의 includes Method 를 사용하여, 문제를 해결할 수 있습니다.(# of Query 감소 효과)
Eager Loading 의미는 아래와 같은데, Associated 된 Records들을 불러올 때 있어서 부모의 Model.find에 의해 반환되는 obj의 records를 불러옵니다. 이때 Model.find로 한번에 조건을 만족하는 obj들을 불러오기 때문에 쿼리 횟수를 최소화 할 수 있습니다.
예로 들면, User 테이블에 pk 값이 1인 user가 user_authentication row를 5개 갖고 있다고 해봅시다. 그럼 User_authentication테이블에서 user_id가 1인 조건을 만족하는 데이터들을 한번의 쿼리로 불러오기 때문에 시간을 단축하는 것입니다. 즉 5번이 아니고, user_id만 맞으면 다 불러오기 때문에 시간을 절약합니다.
Eager loading is the mechanism for loading the associated records of the objects returned by Model.find using as few queries as possible.
아래와 같이 includes를 사용한다면, article 과 authors가 1대 다 관계일 때, 해당 Article에 해당하는 Author가 'WHERE 'authors'.'id' IN (1,2,3,4,5)' 구문으로 한 번의 쿼리로 authors를 갖고 옵니다.
Article Load (0.4ms) SELECT 'articles'.* FROM 'articles'
Author Load (0.4ms) SELECT 'authors'.* FROM 'authors' WHERE 'authors'.'id' IN (1,2,3,4,5)
'Back_End(Ruby on Rails)' 카테고리의 다른 글
Rails Application Request/Response Cycle(레일즈 애플리케이션은 서버에서 어떻게 작동할까?) (2) | 2020.02.10 |
---|---|
RubyOnRails/Model Association (0) | 2020.02.10 |
Rails Transaction Isolation Level (트랜잭션 격리 레벨) (0) | 2020.02.10 |
Rails/디자인패턴(Design Pattern)/Decorator/Draper Gem을 이용 (0) | 2019.11.23 |
루비온레일즈 테스트 코드 작성하기(How to Write a Test Code for RoR) (0) | 2019.11.02 |