首页 > 解决方案 > 使用 Prefetch 提高具有数百万行的子查询中的计数性能

问题描述

我有一个监控来自网站的事件的应用程序,部分用户界面显示给定时间段内每个网站的事件计数。以下是模型的外观:

class Website(models.Model):
    name = models.CharField(max_length=64)
    url = models.TextField()

class Event(models.Model):
    website = models.ForeignKey(Website, related_name="events")
    created_at = models.DateTimeField(default=timezone.now)
    ip_address = models.CharField(max_length=64)
    status = models.CharField(max_length=16)
    message = models.CharField(max_length=128)

这些网站每天产生数千个事件,因此Event与其他表相比,该表相当大。这是我试图生成的查询的样子:

eargs = {
    "website": OuterRef("pk"),
    "created_at__gte": some_start_time,
    "created_at__lt": some_end_time
}
events = Event.objects.filter(**eargs).values("website")
events_count = events.annotate(c=Count("*").values("c")[:1]

websites = Website.objects.annotate(events=Coalesce(Subquery(events_count,
                                                    output_field=IntegerField()), 0)

就像我之前提到的,该Events表中有数百万行。对于少数网站,此查询不会花费太长时间。但是当有100个或更多的网站时,需要相当长的时间。我做了一些分析,数据库(内部)正在查询每个网站的计数。因此,如果我有 100 个网站,数据库将进行 100 个查询来生成计数(仍然有一个查询来自 Django,但 Postgres 在内部进行了这 100 个子查询)。

我想做的是预取这些计数,因为当我运行以下原始 SQL 时,它实际上非常快:

SELECT
    website_id,
    COUNT(*)
FROM
    myapp_events
WHERE
    created_at >= some_start_time AND created_at < some_end_time
GROUP BY
    website_id;

是否有任何可能的方法来预取该查询并仍然使用我的 QuerySet 中的结果?还是我把这一切都搞错了?这似乎是一件很常见的事情,我很难思考如何加快速度。

标签: djangopostgresql

解决方案


推荐阅读