Django:使用 select_related() 和执行时间性能
Django: Usage Of select_related() And Execution Time Performance
我是 Django 和数据库的新手,所以我想对性能有一些看法。具体来说,我想了解 select_related()
是否按照我认为的方式工作。
这是我的模型的简化版本:
class User(models.Model):
short = models.CharField(max_length=255)
name = models.CharField(max_length=255)
class Comment(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
short = models.ForeignKey(User)
在我的模板中,我需要在评论标题旁边显示用户的简称。我的测试数据库大小有 1000 个用户和 19000 条评论。
起初,我是这样检索列表的:
cmt_list = Comment.objects.all().order_by('title')
我的模板正在访问 short
外键关系,这导致对数据库的额外命中。检索所有数据花费了 ~30s。这太可怕了。
我知道原始的速度要快得多 SQL,但我不知道如何通过 Django ORM 做到这一点。所以,我使用了底层接口:
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT app_comment.title,app_user.short \
FROM app_comment,app_user \
WHERE app_comment.short_id=app_user.id \
ORDER BY app_comment.title"
)
raw_list = cursor.fetchall()
cmt_list = [ {"title":entry[0], "short":entry[1]} for entry in raw_list]
检索所有数据花费了 ~233 毫秒。这就是我所期待的!
阅读更多文档后,我发现了 Django 中的 select_related()
功能。
所以,我试过了:
cmt_list = Comment.objects.all().select_related('short__short').order_by('title')
检索所有数据花费了 ~1.3s。比原来的好多了,但与原始 SQL 查询相比仍然很慢。
问题
我在使用 select_related()/Django ORM 的方式上做错了什么?我知道 ORM 会增加一些开销,但 1.3 秒与 233 毫秒相比似乎过高。或者,这是预料之中的,我只需要克服它吗?
我如何使用 ORM 设计一个与我所做的原始 SQL 查询等效的查询?鉴于我对 select_related() 的理解,我的原始 SQL 查询和我的 Django 查询应该大致相同。 (Django 查询将抓取更多内容,但对于我的测试数据,不会有太多额外检索。)
实际上,ORM 会导致相当多的开销。以下语句产生相同的查询(您可以通过比较 str(cmt_list.query)
来检查它):
cmt_list1 = Comment.objects.all()
cmt_list2 = Comment.objects.values('id', 'title', 'content', 'short_id')
然而,使用 timeit.timeit
的简单测试(在我的本地项目中使用不同的模型)显示第二种方法比第一种方法快两倍多。
那个,并且不要忘记 TextField
与 int
或 varchar(255)
列相比是一个巨大的数据量。我敢肯定,如果您在原始 sql 查询中获取 content
列,则数字会更接近。
按照答案中的建议,我使用 Django ORM 更新了我的查询。我必须添加一点列表理解才能为我的模板提供相同的输入。这是结果:
cmt_list = [ {"title":entry["title"], "short":entry["short__short"]}
for entry in
Comment.objects.order_by('title').values("title", 'short__short')
]
执行查询的时间是 ~280 毫秒,这与我使用原始 SQL.[=14= 可以获得的结果更加一致]
为了确定原始 SQL 在 Django ORM 的幕后是什么,我使用了以下打印语句...
print(Comment.objects.order_by('title').values("title", 'short__short').query)
结果如下:
SELECT "app_comment"."title", "app_user"."short"
FROM "app_comment" INNER JOIN "app_user"
ON ( "app_comment"."short_id" = "app_user"."id" )
ORDER BY "app_comment"."title" ASC
这和我做的一样。
此解决方案根本不需要 select_related()
。
我是 Django 和数据库的新手,所以我想对性能有一些看法。具体来说,我想了解 select_related()
是否按照我认为的方式工作。
这是我的模型的简化版本:
class User(models.Model):
short = models.CharField(max_length=255)
name = models.CharField(max_length=255)
class Comment(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
short = models.ForeignKey(User)
在我的模板中,我需要在评论标题旁边显示用户的简称。我的测试数据库大小有 1000 个用户和 19000 条评论。
起初,我是这样检索列表的:
cmt_list = Comment.objects.all().order_by('title')
我的模板正在访问 short
外键关系,这导致对数据库的额外命中。检索所有数据花费了 ~30s。这太可怕了。
我知道原始的速度要快得多 SQL,但我不知道如何通过 Django ORM 做到这一点。所以,我使用了底层接口:
from django.db import connection
cursor = connection.cursor()
cursor.execute("SELECT app_comment.title,app_user.short \
FROM app_comment,app_user \
WHERE app_comment.short_id=app_user.id \
ORDER BY app_comment.title"
)
raw_list = cursor.fetchall()
cmt_list = [ {"title":entry[0], "short":entry[1]} for entry in raw_list]
检索所有数据花费了 ~233 毫秒。这就是我所期待的!
阅读更多文档后,我发现了 Django 中的 select_related()
功能。
所以,我试过了:
cmt_list = Comment.objects.all().select_related('short__short').order_by('title')
检索所有数据花费了 ~1.3s。比原来的好多了,但与原始 SQL 查询相比仍然很慢。
问题
我在使用 select_related()/Django ORM 的方式上做错了什么?我知道 ORM 会增加一些开销,但 1.3 秒与 233 毫秒相比似乎过高。或者,这是预料之中的,我只需要克服它吗?
我如何使用 ORM 设计一个与我所做的原始 SQL 查询等效的查询?鉴于我对 select_related() 的理解,我的原始 SQL 查询和我的 Django 查询应该大致相同。 (Django 查询将抓取更多内容,但对于我的测试数据,不会有太多额外检索。)
实际上,ORM 会导致相当多的开销。以下语句产生相同的查询(您可以通过比较 str(cmt_list.query)
来检查它):
cmt_list1 = Comment.objects.all()
cmt_list2 = Comment.objects.values('id', 'title', 'content', 'short_id')
然而,使用 timeit.timeit
的简单测试(在我的本地项目中使用不同的模型)显示第二种方法比第一种方法快两倍多。
那个,并且不要忘记 TextField
与 int
或 varchar(255)
列相比是一个巨大的数据量。我敢肯定,如果您在原始 sql 查询中获取 content
列,则数字会更接近。
按照答案中的建议,我使用 Django ORM 更新了我的查询。我必须添加一点列表理解才能为我的模板提供相同的输入。这是结果:
cmt_list = [ {"title":entry["title"], "short":entry["short__short"]}
for entry in
Comment.objects.order_by('title').values("title", 'short__short')
]
执行查询的时间是 ~280 毫秒,这与我使用原始 SQL.[=14= 可以获得的结果更加一致]
为了确定原始 SQL 在 Django ORM 的幕后是什么,我使用了以下打印语句...
print(Comment.objects.order_by('title').values("title", 'short__short').query)
结果如下:
SELECT "app_comment"."title", "app_user"."short"
FROM "app_comment" INNER JOIN "app_user"
ON ( "app_comment"."short_id" = "app_user"."id" )
ORDER BY "app_comment"."title" ASC
这和我做的一样。
此解决方案根本不需要 select_related()
。