Python Django rest framework简单模型序列化程序列表视图显示10条记录需要花费很长时间

Python Django rest framework简单模型序列化程序列表视图显示10条记录需要花费很长时间,python,django,performance,django-rest-framework,django-debug-toolbar,Python,Django,Performance,Django Rest Framework,Django Debug Toolbar,我有这个模型- class News(BaseEntityBasicAbstract, HitCountMixin): """ News added from the dashboard with content """ NEWS_STATUS = ( ('draft', _('Draft')), ('pending', _('Pending')), ('review', _('Review')),

我有这个模型-

class News(BaseEntityBasicAbstract, HitCountMixin):
    """
    News added from the dashboard with content
    """
    NEWS_STATUS = (
        ('draft', _('Draft')),
        ('pending', _('Pending')),
        ('review', _('Review')),
        ('public', _('Public')),
        ('private', _('Private'))
    )
    backup = models.BooleanField(default=False)
    prev_id = models.BigIntegerField(null=True, blank=True)
    language = models.CharField(max_length=10, choices=LANGUAGES, default='bn', db_index=True)
    heading = models.CharField(max_length=255, null=True, blank=True,
                               verbose_name=_('News Heading'),
                               help_text=_('Provide a news heading/caption.'))
    sub_caption = models.TextField(max_length=255, null=True, blank=True,
                                   verbose_name=_('Summary'),
                                   help_text=_('Provide summary of the news.'))
    url = models.CharField(max_length=255, unique=True, verbose_name=_('URL/Slug/Link'),
                           help_text=_('Unique url for the news without whitspace.'))
    content = HTMLField(null=True, blank=True, verbose_name=_('Content'),
                        help_text=_('HTML content with texts, links & images.'))
    featured_image = models.FileField(upload_to=FilePrefix('news/'), null=True, blank=True,
                                      verbose_name=_('Featured Image'),
                                      help_text=_('Upload a featured image for news.'))
    image_caption = models.TextField(max_length=255, null=True, blank=True,
                                     verbose_name=_('Image Caption'),
                                     help_text=_('Provide a image caption.'))
    status = models.CharField(max_length=20, choices=NEWS_STATUS, default='pending',
                              verbose_name=_('News Status'), db_index=True,
                              help_text=_('Only public news can be seen on front end.'))
    source = models.ForeignKey(NewsSource, on_delete=models.SET_NULL, null=True, blank=True,
                               verbose_name=_('News Source'),
                               help_text=_('Select a news source.'))
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True,
                                 verbose_name=_('Category'),
                                 help_text=_('Select a news category.'))
    tags = tagulous.models.TagField(
        blank=True,
        to=Tags,
        verbose_name=_('News Tags'),
        help_text=_('Provide news tags separated with commas.')
    )
    published_at = models.DateTimeField(null=True, blank=True, db_index=True,
                                        verbose_name=_('Published At'))
    menu_items = GenericRelation(MenuItems, object_id_field='id',
                                 related_query_name='news_as_menu')
    hit_count_generic = GenericRelation(HitCount, object_id_field='object_pk',
                                        related_query_name='news_hit_count')
    created_by = models.ForeignKey(User, related_name='news_created_by',
                                   on_delete=models.SET_NULL, null=True, blank=True,
                                   verbose_name=_('Created By'))
    updated_by = models.ForeignKey(User, related_name='news_updated_by',
                                   on_delete=models.SET_NULL, null=True, blank=True,
                                   verbose_name=_('Last Updated By'))
    published_by = models.ForeignKey(User, related_name='news_published_by',
                                     on_delete=models.SET_NULL, null=True, blank=True,
                                     verbose_name=_('Published By'))
    deleted_by = models.ForeignKey(User, related_name='news_deleted_by',
                                   on_delete=models.SET_NULL, null=True, blank=True,
                                   verbose_name=_('Deleted By'))
下面是序列化程序-

class NewsSerializer(serializers.ModelSerializer):
    class Meta:
        model = News
        fields = ['id', 'heading', 'sub_caption', 'url', 'content', 'featured_image',
                  'image_caption', 'category', 'source', 'tags', 'published_at']
这是一种观点-

class NewsViewSets(viewsets.ModelViewSet):
    queryset = News.objects.filter(
        is_active=True,
        status='public'
    )
    serializer_class = NewsSerializer

    def get_queryset(self):
        queryset = self.queryset.filter(
            language=self.request.LANGUAGE_CODE
        ).order_by('-id')

        return queryset
在settings.py中,分页设置仅为10,当我点击新闻api url时,只需9/10秒即可加载10条记录。这是显示django调试工具栏报告的屏幕截图-


我在数据库表上有大约400k条记录,这可能是一个问题,但我认为这是太多的加载时间。请帮我找到这里的问题!提前感谢。

过滤通常会很慢。看起来您在相关字段上有数据库索引,但请注意,如果您有多个筛选器,则只会使用其中一个索引

我是根据你的专栏来猜测的,但似乎最常见的查询总是要查找is_active=1和status='public'。如果不是这样,你可能需要做一些调整

首先,去掉status、is_active和language字段上的db_index=True,否则数据库写入将不必要地减慢

然后,您可以制定如下索引:

class Meta:
    indexes = [
        models.Index(
            fields=["is_active", "status", "language"],
            name="idx_filtering",
        )
    ]
当您同时筛选所有三列时,这将有助于数据库。但是,如果只对其中一列进行筛选,则可能希望保持原始db_index=True

如果您使用的是PostgreSQL,您可以做得更好:

class Meta:
    indexes = [
        models.Index(
            fields=["is_active", "status", "language"],
            name="idx_filtering",
            condition=Q(is_active=True, status="public"),
        )
这将把索引的大小减少到只与Q匹配的大小,从而加快遍历速度


另一件需要注意的事情是,一旦达到更高的偏移量,使用偏移量分页的速度会非常慢。如果可能的话,你应该改用DRF的光标分页。

我刚刚注意到,如果我去掉status='public'过滤器,它将在200毫秒内加载。我很困惑,“status”选项字段为什么/如何使查询时间变得如此缓慢!!如果这主要是由于状态字段上的过滤引起的,那么应该尝试在数据库中添加索引,看看这是否有帮助。啊,我看到你已经有了索引,但是你可能需要一个复合索引,因为你要过滤多个字段。还要注意,对于更高的偏移量,限制/偏移量分页非常慢。如果可能的话,您不需要页码,我建议使用DRF的光标分页。状态字段设置为索引。我相信MySQL索引字段存在一些问题,因为现在我将AppDB切换到了postgres,在相同的查询和过滤条件下,一切都很好。关于MySQL的索引相关问题,这让我很困惑。如果你使用的是MySQL,你会受到一些限制。不过,我正在制定一个答案,这个答案应该会有所帮助。您的模型中似乎也缺少“is_活动”字段。非常感谢@Tom Carrick。给了我一些很棒的主意。我将尝试这种索引。我必须为管理员列表查询集保留单个索引。我被困在这些字段的索引中。@MahmudAbdurRahmanToki如果你觉得答案有用,请投票并接受答案——这就是网站的工作方式。