【问题标题】:django is very slowdjango 很慢
【发布时间】:2023-03-22 11:52:01
【问题描述】:

一些分析显示模板渲染是罪魁祸首。 (我正在尝试仅缓存查询的页面。) 但是,模板仍然非常简单。 最复杂的部分是运行 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 %}

我在开发中获得约 80 个请求/秒。此页面的服务器。 (我发现我可以在生产部署中将该数字乘以 3) 作为比较,我得到了 1000req/s 的一个简单的模板,它只包含一个短的静态字符串。

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

【问题讨论】:

  • “慢”到底是什么?
  • 80 req/s 很慢。因为如果没有几个内存缓存,我什么都不做。
  • 不是一个答案,而是一个建议。您是否尝试过像这里这样的缓存:djangosnippets.org/snippets/507
  • 是的,但并没有太大的区别。那是因为它节省了模板解析的时间,而我的模板渲染速度很慢
  • 但究竟什么是“慢”?哪条线?

标签: python django django-templates


【解决方案1】:

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

在生产模式下,在首选 django 之前部署 WSGI 服务器。这样的wsgi可能是gunicorn, uWSGI, ...

一个典型的生产网络服务器的布局可能是:nginx -> gunicorn -> django

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

{% block content %}
    <p> Hello World! @ {{ now }}</p>
{% endblock %}

开发模式

直接用django运行

python manage.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

生产模式

用 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

【讨论】:

    【解决方案2】:

    (显然我的“业力”还不足以发布 cmets,否则我会将其发布为评论而不是答案)

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

    除此之外,在我看来,您的问题可能是您在模板呈现期间大量访问了数据库。

    {% 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 %}
    

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

    然后,对于帖子的每次迭代,您都在{# 1 #} 访问数据库以获取 p.by,我假设它是 auth.User 的 ForeignKey。

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

    因此,对于 100 个项目,您在每次请求中访问数据库 201 次以进行初始运行,并且为图像循环访问 101 次缓存。

    如果可能,请尝试将select_related 与您的帖子查询一起使用,以便在一个查询中提取这些额外的结果。 posts = Post.objects.select_related('by', 'images').filter(...) 之类的东西可能会起作用,但我知道 select_related 在反向外键和 m2m 字段方面有限制(它可能不适用于图像,具体取决于您的模型结构)。

    【讨论】:

    • 当查询没有被缓存时,我已经使用了 select_related,但就像我说的,在这种情况下,我只是从 memcached 中提取结果。 -- 是的,调试工具栏显示执行了 0 个查询。
    • 另外,我说它有 10 个结果,而不是 100 个。
    • 您在评论中说“100 个循环”;我将您在问题中提到的 10 表示为每个帖子的内部图像循环运行十次。对不起,如果我误解了。
    • 不抱歉,这是 ipython shell 的 timeit 命令。我用它来测试模板的渲染速度。 (它在循环中重复表达式并选择最佳时间)
    猜你喜欢
    • 1970-01-01
    • 2012-03-04
    • 2012-10-25
    • 2013-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    • 2010-09-26
    相关资源
    最近更新 更多