Django, DRF: Когда существует много сквозных таблиц m2m, annotate() и count() работают медленно

Я использую postgres и имею около 300,000 строк в Video, 2,000 в Tag, и эти through tables имеют около 1.4 миллиона строк.

Возможно, это связано с большим количеством сквозных таблиц, но при расчете или фильтрации моделей со ссылочными отношениями выдача запроса занимает очень много времени.

Если вы сделаете .annotate(count=Count('video')), а затем сделаете .order_by('-count') с таким значением, как показано ниже, это займет около 750 мс.

Кроме того, поскольку мы используем пагинацию DRF, .count() выполняется за кулисами, и скорость .count() составляет около 700 мс.

Я потратил на это довольно много времени и до сих пор не нашел решения. Как я могу его улучшить? Если вам нужна какая-то другая информация, пожалуйста, дайте мне знать.

# models.py
class Tag(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(unique=True, max_length=100)
    is_actress = models.BooleanField(default=False, db_index=True)
    created_at = models.DateTimeField(default=timezone.now)

    class Meta:
        ordering = ["-is_actress"]

    def __str__(self):
        return self.name

class Video(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=300)
    thumbnail_url = models.URLField(max_length=1000)
    preview_url = models.URLField(max_length=1000, blank=True, null=True)
    embed_url = models.URLField(max_length=1000)
    embed_source = models.ForeignKey(EmbedSource, on_delete=models.CASCADE)
    duration = models.CharField(max_length=8)
    tags = models.ManyToManyField(Tag, blank=True, db_index=True)
    views = models.PositiveIntegerField(default=0, db_index=True)
    is_public = models.BooleanField(default=True)
    published_at = models.DateTimeField(default=timezone.now, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-published_at"]

    def __str__(self):
        return self.title
# serializers.py
class TagSerializer(serializers.ModelSerializer):
    count = serializers.IntegerField()

    class Meta:
        model = Tag
        fields = ("pk", "name", "is_actress", "count")
# views.py
class TagListPagination(PageNumberPagination):
    page_size = 100


class TagListView(generics.ListAPIView):
    serializer_class = TagSerializer
    pagination_class = TagListPagination

    def get_queryset(self):
        queryset = Tag.objects.annotate(count=Count("video")).order_by("-count")
        is_actress = self.request.query_params.get("is_actress")
        if is_actress:
            return queryset.filter(is_actress=True)
        return queryset.filter(is_actress=False)

enter image description here

enter image description here

Вернуться на верх