django很慢

osc*_*car 6 python django django-templates

一些分析显示模板渲染是罪魁祸首.(我正在尝试一个只有缓存查询的页面.)但是,模板非常简单.最复杂的部分是一个运行10次的嵌套循环,但是如果一切顺利,嵌套循环就不会运行,因为它被缓存了.(比如在我的测试中)

那是

{% for p in posts %}
 --{{p.by.username}}
 --{{p.text}}
 {% cache 600 p p.timestamp %}
    {% for img in p.images.all %}
      --{{img.path}}
    {% endfor %}
 {% endcache %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

我在dev上得到了~80 req/s.此页面的服务器.(我发现我可以在生产部署中将该数字乘以3)为了进行比较,我得到1000req/s的简单模板只包含一个短的静态字符串.

这是一个已知问题吗?我该如何纠正/避免它?

小智 5

在开发模式下,django 做了很多事情来简化开发(例如:代码重新加载;如果使用模板,则为每个请求进行模板渲染;...)。

在生产模式下,优先部署 WSGI 服务器,然后再部署 django。这样的 wsgi 可能是gunicornuWSGI,...

典型的生产 Web 服务器的布局可能是:nginx -> Gunicorn -> django

一个简单的对比(django官方教程代码和一个非常简单的“Hello World!@time”模板):

{% block content %}
    <p> Hello World! @ {{ now }}</p>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

发展模式

直接用django运行

python 管理.py runserver

运行 apache-ab 进行测试

ab -n1000 -c10 http://127.0.0.1:8000/polls/helloworld

Server Software:        WSGIServer/0.2 # django
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /polls/helloworld
Document Length:        59 bytes

Concurrency Level:      10
Time taken for tests:   3.438 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      242000 bytes
HTML transferred:       59000 bytes
Requests per second:    290.87 [#/sec] (mean)
Time per request:       34.380 [ms] (mean)
Time per request:       3.438 [ms] (mean, across all concurrent requests)
Transfer rate:          68.74 [Kbytes/sec] received
Run Code Online (Sandbox Code Playgroud)

生产模式

与gunicorn一起运行

../bin/gunicorn -w4 mysite.wsgi # 有 4 个工人

运行 apache-ab 进行测试

ab -n1000 -c10 http://127.0.0.1:8000/polls/helloworld

Server Software:        gunicorn/19.7.1  # gunicorn
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /polls/helloworld
Document Length:        59 bytes

Concurrency Level:      10
Time taken for tests:   0.618 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      248000 bytes
HTML transferred:       59000 bytes
Requests per second:    1619.10 [#/sec] (mean)
Time per request:       6.176 [ms] (mean)
Time per request:       0.618 [ms] (mean, across all concurrent requests)
Transfer rate:          392.13 [Kbytes/sec] received
Run Code Online (Sandbox Code Playgroud)


ete*_*ode 3

(显然我还没有足够的“业力”来发表评论,或者我会将其作为评论而不是答案发布)

您能详细说明一下“仅缓存查询”的含义吗?

除此之外,在我看来,您的问题可能是您在模板渲染期间频繁访问数据库。

{% for p in posts %}
 --{{p.by.username}} {# 1 #}
 --{{p.text}}
 {% cache 600 p p.timestamp %}
    {% for img in p.images.all %} {# 2 #}
      --{{img.path}}
    {% endfor %}
 {% endcache %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

您向模板提供“帖子”;这是一个查询,您说过它有 100 个结果。

然后,对于帖子的每次迭代,您都将访问数据库以{# 1 #}获取 p.by,我认为它是 auth.User 的外键。

除此之外,由于缓存无效(第一次运行),您将再次访问数据库{# 2 #}以获取该帖子的图像列表。

因此,对于 100 个项目,初始运行时每个请求会访问数据库 201 次,图像循环的缓存已满,则访问数据库 101 次。

如果可能的话,尝试将select_lated与您的 posts 查询结合使用,将这些额外结果提取到一个查询中。类似的东西posts = Post.objects.select_related('by', 'images').filter(...)可能会起作用,尽管我知道select_related在反转外键和 m2m 字段方面存在限制(它可能不适用于图像,具体取决于您的模型结构)。