Django:select_related()和执行时间性能的用法

时间:2015-01-03 18:07:08

标签: django sqlite django-queryset django-orm

我是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]

检索所有数据 ~233ms 。这就是我所期待的!

在阅读了更多文档之后,我在Django中发现了select_related()功能。 所以,我试过了:

cmt_list = Comment.objects.all().select_related('short__short').order_by('title')

检索所有数据需要 ~1.3s 。比原始SQL好得多,但与原始SQL查询相比仍然相当慢。

问题

我使用select_related()/ Django ORM的方式我做错了什么?我知道ORM会增加一些开销,但1.3s和233ms似乎过多。或者,这是预期的,我只需要克服它吗?

如何使用ORM来构建一个与我所做的原始SQL查询等效的查询?根据我对select_related()的理解,我的原始SQL查询和我的Django应该大致相同。 (Django查询将获取更多内容,但对于我的测试数据,将不会有太多额外的检索。)

2 个答案:

答案 0 :(得分:1)

实际上,ORM会导致相当多的开销。以下语句生成相同的查询(您可以通过比较str(cmt_list.query))来检查它:

cmt_list1 = Comment.objects.all()
cmt_list2 = Comment.objects.values('id', 'title', 'content', 'short_id')

但是,使用timeit.timeit(在我的本地项目中使用不同模型)的简单测试显示第二种方法的速度比第一种方法快两倍。

那,并且不要忘记与TextFieldint列相比,varchar(255)是一个庞大的数据量。我敢肯定,如果你在原始SQL查询中获取content列,那么数字会更接近。

答案 1 :(得分:0)

根据答案中的建议,我使用Django ORM更新了我的查询。我必须添加一个小列表理解来为我的模板提供相同的输入。结果如下:

    cmt_list = [ {"title":entry["title"], "short":entry["short__short"]} 
                 for entry in 
                 Comment.objects.order_by('title').values("title", 'short__short')
               ]

执行查询的时间是 ~280ms ,这与使用原始SQL可以获得的内容更为一致。

为了确定Django ORM幕后的原始SQL是什么,我使用了以下打印语句......

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()