Django N+1 Problem

ORM

홍찬기
3 min readFeb 27, 2020

ORM이란 Object Relation Mapper의 줄임말이다. 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑 해주는 것을 말한다. 객체를 통해 간접적으로 데이터베이스 데이터를 다룬다.

ORM의 장점

  • 객체 지향적인 코드로 인해 더 직관적이고 비즈니스 로직에 더 집중할 수 있게 도와준다.
  • 재사용 및 유지보수에 용이하다.
  • DBMS에 대한 종속성이 줄어든다.

ORM의 단점

  • ORM으로만 완벽한 서비스를 구현할 수 없다.
  • 프로시저가 많은 시스템에선 ORM의 객체 지향적인 장점을 활용하기 어렵다.
  • 프로젝트의 복잡성이 크면 구현하는 난이도가 상승한다.

N+1 Problem

django ORM은 Lazy-Loading 방식이다. Lazy-Loading 방식을 사용하면 ORM에서 명령을 실행할 때마다 데이터베이스에서 데이터를 가져오는 것이 아니라 모든 명령 처리가 끝나고 실제로 데이터를 불러와야 할 시점이 왔을 때 데이터베이스에 쿼리를 실행하는 방식이다.

user = User.objects.all()
hong = user.filter(first_name='hong') # 아직 DB에서 데이터를 가져오지않음
order_hong = hong.order_by('id') # 아직 DB에서 데이터를 가져오지않음
gildong = user.get(id=1)
parent = gildong.parent.name # parent 를 가져오는 쿼리를 더 보냄

N+1 Problem은 쿼리 1번으로 N건의 데이터를 가져왔는데 원하는 데이터를 얻기 위해 이 N건의 데이터를 데이터 수 만큼 반복해서 2차적으로 쿼리를 수행하는 문제다. 이 문제의 해결방식으로 Eager-Loading방식이 있다.

Eager-Loading방식은 사전에 쓸 데이터를 포함하여 쿼리를 날리기 때문에 비효율적으로 늘어나는 쿼리 요청을 방지할 수 있다.

user = User.objects.all()
parent = user.prefetch_related('parent') # parent를 가져오는 쿼리를 사전에 날림

위와같이 prefetch_related 메서드를 통해 Eager-Loading 방식으로 데이터를 불러올 수 있다.

Django ORM에서 이처럼 Eager-Loading을 하는 방법으로는 두가지가있다.

  • select_related : foreign-key , one-to-one 처럼 single-valued relationships에서만 사용이 가능하다. SQL의 JOIN을 사용하는 방법이다.
  • prefetch_related : foreign-key , one-to-one 뿐만 아니라 many-to-many , many-to-one 등 모든 relationships에서 사용 가능하다. SQL의 WHERE … IN 구문을 사용하는 방법이다.

--

--