본문으로 바로가기

django, ForeignKey N+1

category Frame Work/Django 2021. 8. 16. 16:44
728x90
반응형

 

 

Forign Key 참조를 사용해보다가 발견한 현상

아래 두 모델은 DRF를 익히기 위해 만든 모델이다

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True)
    password = models.CharField(max_length=128)

    objects = UserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []
class Feed(models.Model):
    id = models.BigAutoField(help_text='Feed ID', primary_key=True)
    user_id = models.ForeignKey("User", on_delete=models.CASCADE, db_column='user_id')
    title = models.TextField(help_text='Feed title', null=False)
    content = models.TextField(help_text='Feed Content', blank=True, null=True)

    create_at = models.DateTimeField(auto_now_add=True)
    update_at = models.DateTimeField(auto_now=True)

각 Field에 대한 설명은 생략하겠다. 이 글의 목적에 맞게 Feed라는 모델이 User모델의 기본키를 참조(model.ForignKey("User"...) 하고 있는 걸 볼 수 있다. 겉으론 봐선 이상이 없어 보인다 문제는 다음과 같은 code를 쓸 때 나타났다.

class FeedService(serializers.ModelSerializer):
    class Meta:
        model = Feed
        fields = ['title', 'content']

    @classmethod
    def read(cls):
        query_result = Feed.objects.all()

        data = [
            {
                'id': query.id,
                'user_pk': query.user_id.pk,
                'title': query.title,
                'content': query.content,
                'create_at': query.create_at.strftime("%Y-%d-%m, %H:%M:%S"),
                'update_at': query.update_at.strftime("%Y-%d-%m, %H:%M:%S")

            } for query in query_result
        ]
        return data

반환 데이터를 보면 단순히 Feed 라는 모델의 데이터를 뽑아서 Return 해주고 있는 모습을 볼 수 있다. 하지만 DJango의 SQL 로그를 찍어보면 다음과 같이 같은 쿼리가 여러 번 발생한다.

feed 테이블에는 query가 한 번만 발생하는 반면 참조하는 user 테이블에는 쿼리가 여러번 발생한다 정확히는 feed 테이블에 있는 데이터의 수만큼 발생하는 것이다.

 

정확한 해결법은 모르지만 아마 다음과 같은 해결법이 존재할 것이다.

 

1. Forign Key 대신 user_pk를 feed 테이블에도 선언한 뒤 외래키 참조가 아니라 연관성만을 갖도록 설정

2. Django나 DRF에서 제공해주는 직렬화 라이브러리를 찾아서 적용

 

위의 예제는 간단한 쿼리여서 눈에 띄는 현상이 발생하지만 않지만 같은 쿼리가 여러 번 발생한다는 것은 DB에 부하가 갈 수 있을 뿐만 아니라 비효율적으로 돌아간다는 대표적인 예이고 의도하지 않은 동작이므로 고치는 것이 좋지 않을까?

728x90
반응형