Django很慢
一些性能分析显示,模板渲染是问题的根源。(我在一个只有缓存查询的页面上进行测试。)
不过,这个模板其实很简单。
最复杂的部分是一个嵌套循环,最多运行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)
作为对比,我在一个只包含短静态字符串的简单模板上可以达到每秒1000个请求。
这是一个已知的问题吗?我该如何去修复或避免它呢?
2 个回答
(显然我还不够“有 karma”来发评论,要不然我就把这个当评论发了,而不是作为回答)
你能详细解释一下“仅缓存查询”是什么意思吗?
除此之外,我觉得你的问题可能是因为在模板渲染时,你频繁地访问数据库。
{% 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,我猜这是指向一个用户的外键。
除此之外,在缓存无效的情况下(第一次运行),你在{# 2 #}
又要访问数据库来获取这个帖子的图片列表。
所以对于100个项目来说,第一次请求时你要访问数据库201次,而如果缓存已经填充好,获取图片时就只需要访问101次。
试着在你的帖子查询中使用 select_related,这样可以在一次查询中拉取这些额外的结果。如果可能的话,可以试试像 posts = Post.objects.select_related('by', 'images').filter(...)
这样的写法,虽然我知道 select_related
在处理反向外键和多对多字段时有一些限制(根据你的模型结构,可能对图片不适用)。
在开发模式下,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