性能和优化 — Django 文档
性能和优化
本文档提供了技术和工具的概述,可以帮助您更有效地运行 Django 代码 - 更快,并使用更少的系统资源。
简介
通常,人们首先关心的是编写 工作 的代码,其逻辑功能按要求运行以产生预期的输出。 然而,有时这并不足以使代码像人们希望的那样 有效地 工作。
在这种情况下,需要的是一些东西——在实践中,通常是一些东西的集合——来提高代码的性能,而不会或只是最低限度地影响其行为。
一般方法
你在为 优化 什么?
清楚地了解“性能”是什么意思很重要。 它不仅仅是一个指标。
提高速度可能是程序最明显的目标,但有时可能会寻求其他性能改进,例如降低内存消耗或减少对数据库或网络的需求。
一个领域的改进通常会带来另一个领域的绩效提升,但并非总是如此; 有时一个人甚至可以以牺牲另一个人为代价。 例如,程序速度的提高可能会导致它使用更多的内存。 更糟糕的是,它可能会弄巧成拙——如果速度提升如此之大,以至于系统开始耗尽内存,那将弊大于利。
还有其他一些权衡需要牢记。 您自己的时间是宝贵的资源,比 CPU 时间更宝贵。 某些改进可能太难而不值得实施,或者可能会影响代码的可移植性或可维护性。 并非所有的性能改进都值得付出努力。
因此,您需要知道您的目标是哪些性能改进,并且您还需要知道您有充分的理由朝着那个方向瞄准 - 为此您需要:
性能基准测试
仅仅猜测或假设代码中的低效率是没有用的。
Django 工具
django-debug-toolbar 是一个非常方便的工具,它可以让您深入了解您的代码正在做什么以及它花费了多少时间。 特别是它可以显示页面生成的所有 SQL 查询,以及每个查询所用的时间。
第三方面板也可用于工具栏,可以(例如)报告缓存性能和模板渲染时间。
从一开始就把事情做好
优化中的一些工作涉及解决性能缺陷,但有些工作可以简单地内置到您无论如何要做的事情中,作为在您开始考虑提高性能之前就应该采用的良好实践的一部分。
在这方面,Python 是一种优秀的语言,因为看起来优雅且感觉正确的解决方案通常是性能最好的解决方案。 与大多数技能一样,学习“看起来正确”需要练习,但最有用的指导方针之一是:
在适当的级别工作
Django 提供了许多不同的处理方式,但是仅仅因为可以以某种方式做某事并不意味着它是最合适的方式。 例如,您可能会发现可以在 QuerySet
、Python 或模板中计算同样的事情 - 集合中的项目数。
但是,在较低级别而不是较高级别完成这项工作几乎总是更快。 在更高级别,系统必须通过多个抽象级别和机器层来处理对象。
也就是说,数据库通常可以比 Python 更快地完成任务,Python 可以比模板语言更快地完成任务:
# QuerySet operation on the database
# fast, because that's what databases are good at
my_bicycles.count()
# counting Python objects
# slower, because it requires a database query anyway, and processing
# of the Python objects
len(my_bicycles)
# Django template filter
# slower still, because it will have to count them in Python anyway,
# and because of template language overheads
{{ my_bicycles|length }}
一般来说,最适合这项工作的级别是最适合编码的最低级别。
笔记
上面的例子只是说明性的。
首先,在现实生活中,您需要考虑计数前后发生的情况,以找出在特定上下文 中进行此操作的最佳方法 。 数据库优化文档描述了在模板中计数会更好的情况。
其次,还有其他选项需要考虑:在实际案例中,直接从模板调用 QuerySet
count()
方法的 模板:My bicycles.count
可能是最合适的选择.
缓存
计算一个值通常很昂贵(即,资源匮乏且速度缓慢),因此将值保存到可快速访问的缓存中会带来巨大的好处,以备下次需要时使用。
这是一个足够重要和强大的技术,Django 包含一个全面的缓存框架,以及其他较小的缓存功能。
缓存框架
Django 的 缓存框架 为性能提升提供了非常重要的机会,通过保存动态内容,无需为每个请求计算。
为方便起见,Django 提供了不同级别的缓存粒度:您可以缓存特定视图的输出,或者仅缓存难以生成的部分,甚至整个站点。
实现缓存不应被视为改进性能不佳的代码的替代方法,因为它写得不好。 这是生成性能良好代码的最后步骤之一,而不是捷径。
cached_property
必须多次调用类实例的方法是很常见的。 如果该功能很昂贵,那么这样做可能会很浪费。
使用 cached_property 装饰器保存属性返回的值; 下次在该实例上调用该函数时,它将返回保存的值而不是重新计算它。 请注意,这仅适用于将 self
作为其唯一参数的方法,并将方法更改为属性。
某些 Django 组件也有自己的缓存功能; 这些将在与这些组件相关的部分中讨论。
理解懒惰
Laziness 是缓存的补充策略。 缓存通过保存结果避免重新计算; 懒惰会延迟计算,直到真正需要它。
懒惰允许我们在实例化之前引用事物,甚至在可以实例化它们之前引用它们。 这有很多用途。
例如, 延迟翻译 甚至可以在知道目标语言之前使用,因为它不会在实际需要翻译的字符串之前发生,例如在渲染模板中。
懒惰也是一种通过尝试避免工作来节省精力的方法。 也就是说,懒惰的一方面是在必须做任何事情之前不做任何事情,因为它可能最终证明是不必要的。 因此,懒惰会对性能产生影响,并且相关工作越昂贵,通过懒惰获得的收益就越大。
Python 提供了许多用于惰性求值的工具,特别是通过 generator 和 generator expression 构造。 值得一读 Python 中的惰性,以发现在代码中使用惰性模式的机会。
Django 中的懒惰
Django 本身就很懒惰。 一个很好的例子可以在 QuerySets
的评估中找到。 QuerySets 是惰性的 。 因此,一个 QuerySet
可以被创建、传递并与其他 QuerySets
组合,而无需实际访问数据库以获取它所描述的项目。 传递的是 QuerySet
对象,而不是最终需要从数据库中获取的项目集合。
另一方面, 某些操作将强制评估 QuerySet。 避免过早评估 QuerySet
可以节省昂贵且不必要的数据库之旅。
Django 还提供了一个 keep_lazy() 装饰器。 这允许使用惰性参数调用的函数自身惰性地运行,仅在需要时才对其进行评估。 因此,在严格要求之前,不会调用惰性参数(可能是昂贵的参数)进行评估。
数据库
HTTP 性能
中间件
Django 附带了一些有用的 中间件 ,可以帮助优化您网站的性能。 它们包括:
ConditionalGetMiddleware
添加对现代浏览器的支持,以根据 ETag
和 Last-Modified
标头有条件地获取响应。 如果需要,它还计算并设置一个 ETag。
GZipMiddleware
压缩所有现代浏览器的响应,节省带宽和传输时间。 请注意,GZipMiddleware 目前被认为是一种安全风险,并且容易受到使 TLS/SSL 提供的保护无效的攻击。 有关更多信息,请参阅 GZipMiddleware 中的警告。
会话
静态文件
静态文件,根据定义不是动态的,是优化收益的绝佳目标。
ManifestStaticFilesStorage
通过利用 Web 浏览器的缓存功能,您可以在初始下载后完全消除给定文件的网络命中。
ManifestStaticFilesStorage 在 静态文件 的文件名中附加一个内容相关的标签,使浏览器可以安全地长期缓存它们而不会错过未来的更改 - 当文件更改时,标签,因此浏览器将自动重新加载资产。
“缩小”
一些第三方 Django 工具和包提供了“缩小”HTML、CSS 和 JavaScript 的能力。 它们删除了不必要的空格、换行符和注释,并缩短了变量名称,从而减小了站点发布的文档的大小。
模板性能
请注意:
- 使用
{% block %}
比使用{% include %}
更快 - 大量碎片化的模板,由许多小块组装而成,会影响性能
使用不同版本的可用软件
有时值得检查您正在使用的软件的不同和性能更好的版本是否可用。
这些技术针对的是更高级的用户,他们想要突破已经优化好的 Django 站点的性能界限。
然而,它们并不是性能问题的神奇解决方案,而且它们不太可能给那些还没有以正确的方式做更基本的事情的站点带来比边际收益更好的收益。
笔记
值得重申的是: 寻找您已经在使用的软件的替代品从来都不是解决性能问题的第一个答案。 当您达到这种优化级别时,您需要一个正式的基准测试解决方案。
更新通常——但并非总是——更好
维护良好的软件的新版本效率较低的情况相当罕见,但维护人员无法预测所有可能的用例 - 因此,尽管意识到新版本可能性能更好,但不要简单地假设它们永远都会。
Django 本身也是如此。 后续版本对整个系统进行了许多改进,但您仍应检查应用程序的实际性能,因为在某些情况下,您可能会发现更改意味着它的性能更差而不是更好。
较新版本的 Python 以及 Python 包通常也会表现得更好——但要进行衡量,而不是假设。
笔记
除非您在特定版本中遇到不寻常的性能问题,否则您通常会在新版本中发现更好的功能、可靠性和安全性,并且这些好处远比您可能赢得或失去的任何性能更重要。
Django 模板语言的替代品
对于几乎所有情况,Django 的内置模板语言完全足够。 但是,如果您的 Django 项目的瓶颈似乎在于模板系统,并且您已经用尽了其他机会来解决这个问题,那么第三方替代方案可能就是答案。
Jinja2 可以提供性能改进,尤其是在速度方面。
替代模板系统在共享 Django 模板语言的程度上有所不同。
笔记
如果您在模板中遇到性能问题,首先要做的是确切了解原因。 使用替代模板系统可能会更快,但也可能获得相同的收益而不会遇到麻烦 - 例如,可以在视图中更有效地完成模板中昂贵的处理和逻辑。
替代软件实现
可能值得检查您正在使用的 Python 软件是否已在不同的实现中提供,以更快地执行相同的代码。
然而:编写良好的 Django 站点中的大多数性能问题都不是在 Python 执行级别,而是低效的数据库查询、缓存和模板。 如果您依赖于写得不好的 Python 代码,那么您的性能问题不太可能通过让它执行得更快来解决。
使用替代实现可能会引入兼容性、部署、可移植性或维护问题。 毋庸置疑,在采用非标准实现之前,您应该确保它为您的应用程序提供足够的性能增益以超过潜在风险。
考虑到这些警告,您应该注意:
pypy
PyPy 是 Python 本身的 Python 实现(“标准”Python 实现是用 C 语言实现的)。 PyPy 可以提供显着的性能提升,通常用于重量级应用程序。
PyPy 项目的一个主要目标是 与现有 Python API 和库的兼容性 。 Django 是兼容的,但您需要检查您依赖的其他库的兼容性。
Python 库的 C 实现
一些 Python 库也是用 C 实现的,而且速度会快得多。 他们旨在提供相同的 API。 请注意,兼容性问题和行为差异并非未知(并且并不总是很明显)。