편리하면서도 은근히 사용하기 복잡한 Django의 ORM,
그 중 prefetch_related와 select_related에 대해 자세히 알아보자.
Object Relational Mapping, 객체-관계 매핑을 뜻하는 용어로, 객체와 관계형 데이터베이스의 데이터를 자동으로 연결해주는 것을 의미한다.
Foreign Key 속성이 있는 객체에서 해당 부모를 참조하거나 1:1 관계에서 참조하는 경우
Query문의 Join을 사용해 foreign-key(one to one, many to one)를 참조하며, 관련된 테이블 정보를 받아올 수 있다.
다음의 예시와 함께 확인해보자.
"나는 user id가 1인 사람의 도시 명을 알고 싶다."
(User: 자식 테이블, City: 자식이 참조하는 부모 테이블)
# 도시 정보
class City(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=20)
# 유저 정보
class User(models.Model):
id = models.BigAutoField(primary_key=True)
city = models.ForeignKey('City', on_delete=models.CASCADE)
name = models.CharField(max_length=20)
age = models.SmallIntegerField()
- select_related 사용 X
city_info = User.objects.get(id=1).city city_name = city_info.name
- select_related 사용 O
city_info = User.objects.select_related('city').get(id=1).city city_name = city_info.name
위의 1과 2 방법은 쿼리 상으로는 다를 게 없어 보인다. 오히려 2번이 더 복잡해 보이기도 한다. 그렇다면, DB 접근 방식을 비교해보자.
1번의 경우, DB에 2번의 접근을 하게 된다.
User 테이블에서 id가 1번인 유저의 city 값을 가져오기 위한 조회 -- (1),
가져온 1의 데이터를 바탕으로 City 테이블을 조회해 name 값을 가져오기 위한 조회 -- (2)
하지만, select_related를 사용한 2번의 경우 DB에 1번만 접근한다.
select_related에서 관련된 Object(여기서는 city)까지 한 번에 조회해 cache에 저장한다.
따라서, 이후에 city 값에 대한 조회를 할 때는 DB에 재접근 방식이 아닌 가져온 cache 값에서 꺼내서 사용하기만 하면 된다.
➰ select_related는 INNER JOIN을 통해 데이터를 가져온다.
➰ foreign-key, one-to-one과 같은 1:1 관계에서 사용하는 것이 효과적이다.
정참조를 포함하여, 역참조(다른 객체가 ForeignKey를 가지고 있거나 M:N 관계일 때, 해당 객체를 참조하고 있는 다른 객체를 참조)하는 경우 사용한다.
➰ prefetch_related는 foreign-key, one-to-one 뿐만 아니라 many-to-many, many-to-one 관계에서도 사용 가능하다.
➰ SQL에서 각 모델을 조회하고, Python에서 Join을 실행한다.
간단한 테이블 및 구조로 확인해 보았지만, 데이터가 많아지고 연관된 테이블이 많아질 경우 DB 접근 방식에 따른 쿼리 성능은 무시할 수 없다.
상황에 맞게 select_related와 prefetch_related를 사용해 효율적인 코딩을 해보자!😆