性能与优化

本文档概述了可以帮助您的Django代码更高效、更快地运行和使用更少的系统资源的技术和工具。

介绍

一般来说,一个人的首要问题是编写 作品 其逻辑功能按要求产生预期的输出。但是,有时这还不足以使代码工作 有效地 如你所愿。

在这种情况下,需要的是一些东西——在实践中,通常是一些东西的集合——以提高代码的性能,而不影响或只影响其行为的最小程度。

一般方法

你在优化什么 for 是吗?

明确你所说的“表现”是什么意思很重要。它不只是一个度量标准。

提高速度可能是程序最明显的目标,但有时可能会寻求其他性能改进,例如降低内存消耗或减少对数据库或网络的需求。

一个领域的改进通常会在另一个领域带来改进的性能,但并非总是如此;有时甚至可以牺牲另一个领域。例如,程序速度的提高可能会导致它使用更多的内存。更糟糕的是,它可能会自我挫败——如果速度提高太过消耗内存,以至于系统开始耗尽内存,那么您所做的弊大于利。

还有其他的权衡需要考虑。你自己的时间是一种宝贵的资源,比CPU时间更宝贵。有些改进可能太难实现,或者可能影响代码的可移植性或可维护性。并非所有的性能改进都值得付出努力。

因此,你需要知道你的目标是什么样的绩效改进,你也需要知道你有一个很好的理由朝着这个方向努力——为此你需要:

绩效基准

仅仅猜测或假设代码中的低效率是不好的。

Django 工具

django-debug-toolbar 这是一个非常方便的工具,它可以提供对代码正在做什么以及花费了多少时间的洞察。特别是,它可以显示页面正在生成的所有SQL查询,以及每个查询所用的时间。

第三方面板也可用于工具栏,它可以(例如)报告缓存性能和模板呈现时间。

第三方服务

有许多免费服务可以从远程HTTP客户机的角度分析和报告站点页面的性能,实际上是模拟实际用户的体验。

这些不能报告代码的内部结构,但可以提供对站点整体性能的有用洞察,包括在Django环境中无法充分测量的方面。示例包括:

也有一些付费服务可以执行类似的分析,包括一些Django感知的服务,可以与代码库集成以更全面地分析其性能。

从一开始就把事情做好

优化中的一些工作涉及到解决性能缺陷,但有些工作可以内置在您将要做的事情中,作为甚至在您开始考虑提高性能之前就应该采用的良好实践的一部分。

在这方面,Python是一种很好的语言,因为看起来优雅、感觉良好的解决方案通常是性能最好的解决方案。与大多数技能一样,学习“看起来正确”需要练习,但最有用的指导原则之一是:

在适当的水平上工作

Django提供了许多不同的接近事物的方法,但仅仅因为以某种方式做某件事情是可能的,并不意味着它是最合适的方法。例如,您可能会发现,可以在 QuerySet 在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 }}

一般来说,最适合工作的级别是最适合编码的最低级别。

备注

上面的例子仅仅是说明性的。

首先,在现实生活中,你需要考虑在你数数之前和之后发生的事情,以找出最佳的方法。 在那个特定的背景下 . 数据库优化文档描述 a case where counting in the template would be better .

第二,还有其他的选择要考虑:在现实生活中, {{{{ my_bicycles.count }}}} ,它调用 QuerySet count() 方法直接来自模板,可能是最合适的选择。

高速缓存

计算一个值通常是昂贵的(即,资源匮乏且速度慢),因此将该值保存到一个快速可访问的缓存中会有巨大的好处,为下一次需要它时做好准备。

Django包含了一个全面的缓存框架,以及其他较小的缓存功能,这是一种非常重要和强大的技术。

The caching framework

Django caching framework 通过保存动态内容,不需要为每个请求计算,为性能提升提供了非常重要的机会。

为了方便起见,Django提供了不同级别的缓存粒度:您可以缓存特定视图的输出,或者只缓存难以生成的部分,甚至整个站点。

不应将实现缓存视为改进性能较差的代码的替代方案,因为代码编写得很差。这是生成性能良好的代码的最后步骤之一,而不是捷径。

cached_property

通常需要多次调用类实例的方法。如果这个功能很昂贵,那么这样做可能是浪费。

使用 cached_property decorator保存属性返回的值;下次对该实例调用函数时,它将返回保存的值,而不是重新计算该值。注意,这只适用于 self 作为它们唯一的参数,它将方法更改为属性。

某些Django组件也有自己的缓存功能;下面将在与这些组件相关的部分中讨论这些功能。

理解懒惰

懒惰 是对缓存的补充策略。缓存通过保存结果来避免重新计算;延迟会延迟计算,直到实际需要为止。

懒惰使我们能够在事物被实例化之前,甚至在它被实例化之前引用它们。这有很多用途。

例如, lazy translation 可以在目标语言还未被识别之前使用,因为在实际需要翻译后的字符串(例如在呈现的模板中)之前,它不会发生。

懒惰也是一种通过一开始就避免工作来省力的方法。也就是说,懒惰的一个方面就是在必须做之前什么都不做,因为它可能根本就不必要。因此,懒惰可能会影响到性能,而相关工作的成本越高,通过懒惰获得的收益就越多。

python提供了许多用于惰性评估的工具,特别是通过 generatorgenerator expression 构造。值得一读Python中的惰性,以发现在代码中使用惰性模式的机会。

Django 的懒惰

Django 本身很懒。一个很好的例子可以在 QuerySets . QuerySets are lazy . 因此A QuerySet 可以创建、传递和与其他 QuerySets ,而不会实际导致任何访问数据库以获取其描述的项。被传递的是 QuerySet 对象,而不是数据库中最终需要的项集合。

另一方面, certain operations will force the evaluation of a QuerySet . 避免对 QuerySet 可以节省对数据库进行昂贵和不必要的访问。

Django还提供 keep_lazy() 装饰符。这允许使用lazy参数调用的函数本身的行为很懒惰,只在需要时才进行计算。因此,在严格要求之前,懒惰的参数——可能是一个昂贵的参数——不会被要求进行评估。

数据库

数据库优化

Django的数据库层提供了各种方法来帮助开发人员从数据库中获得最佳性能。这个 database optimization documentation 收集到相关文档的链接,并添加各种提示,概述在尝试优化数据库使用时要采取的步骤。

HTTP性能

中间件

Django提供了一些有用的 middleware 这有助于优化网站的性能。它们包括:

ConditionalGetMiddleware

添加对现代浏览器的支持,以便根据 ETagLast-Modified 标题。如果需要,它还计算并设置ETag。

GZipMiddleware

压缩所有现代浏览器的响应,节省带宽和传输时间。请注意,gzip中间件目前被视为安全风险,并且容易受到攻击,这些攻击会使TLS/SSL提供的保护失效。请参阅中的警告 GZipMiddleware 更多信息。

会议

使用缓存会话

Using cached sessions 这可能是一种提高性能的方法,因为无需从较慢的存储源(如数据库)加载会话数据,而是将频繁使用的会话数据存储在内存中。

静态文件

静态文件在定义上不是动态的,它是优化增益的一个很好的目标。

ManifestStaticFilesStorage

通过利用Web浏览器的缓存功能,可以在初始下载后完全消除给定文件的网络命中。

ManifestStaticFilesStorage 将内容相关标记附加到的文件名 static files 为了使浏览器能够安全地长期缓存它们,而不会丢失将来的更改—当文件发生更改时,标记也会发生更改,因此浏览器将自动重新加载资产。

“缩小”

一些第三方django工具和包提供了“缩小”HTML、CSS和JavaScript的功能。它们删除不必要的空白、换行符和注释,并缩短变量名,从而减少网站发布的文档的大小。

模板性能

注意:

  • 使用 {{% block %}} 比使用更快 {{% include %}}

  • 大量碎片化的模板(由许多小块组装而成)会影响性能

缓存模板加载程序

启用 cached template loader 通常会显著提高性能,因为它避免了每次需要呈现模板时都进行编译。

使用不同版本的可用软件

有时值得检查您所使用的软件的不同版本和性能更好的版本是否可用。

这些技术针对的是更高级的用户,他们希望突破已经优化的django站点的性能界限。

然而,它们并不是解决性能问题的神奇解决方案,而且它们不可能为那些还没有以正确的方式完成更基本的工作的站点带来比边际收益更好的收益。

备注

值得重复的是: 寻求已经在使用的软件的替代方案从来不是性能问题的第一个答案。 . 当您达到这个优化级别时,您需要一个正式的基准测试解决方案。

更新往往更好,但并非总是更好。

一个维护良好的软件的新版本的效率降低是相当罕见的,但是维护人员不能预测每一个可能的用例——因此,尽管意识到新版本可能会表现更好,但不要认为它们总是会这样。

这就是 Django 本身。连续的版本在整个系统中提供了许多改进,但是您仍然应该检查应用程序的实际性能,因为在某些情况下,您可能会发现更改意味着它的性能更差而不是更好。

更新版本的python,以及python包,通常也会表现得更好——但是要测量,而不是假设。

备注

除非在特定版本中遇到异常的性能问题,否则在新版本中通常会发现更好的特性、可靠性和安全性,而且这些好处远比您可能赢得或失去的任何性能都重要。

Django模板语言的替代方案

对于几乎所有情况,Django的内置模板语言都是完全足够的。但是,如果您的Django项目中的瓶颈似乎位于模板系统中,并且您已经用尽了其他机会来解决这一问题,那么可以选择第三方解决方案。

Jinja2 可以提供性能改进,尤其是在速度方面。

备选模板系统在共享Django模板语言的程度上有所不同。

备注

If 您在模板中遇到了性能问题,首先要做的是准确理解原因。使用另一个模板系统可能会更快,但同样的收益也可以在不出现问题的情况下获得——例如,模板中昂贵的处理和逻辑可以在视图中更有效地完成。

替代软件实现

可能值得检查您使用的python软件是否在不同的实现中提供,该实现可以更快地执行相同的代码。

但是:编写良好的Django站点中的大多数性能问题不在Python执行级别,而是在效率低下的数据库查询、缓存和模板中。如果您依赖编写得不好的Python代码,那么您的性能问题不太可能通过让它更快地执行来解决。

使用替代实现可能会带来兼容性、部署、可移植性或维护问题。不言而喻,在采用非标准实现之前,您应该确保它为您的应用程序提供足够的性能收益,以抵消潜在的风险。

考虑到这些警告,您应该意识到:

PyPy

PyPy 是Python本身的一个实现(“标准”的Python实现是用C语言实现的)。PyPy可以提供显著的性能提升,通常适用于重量级应用程序。

PyPy项目的一个主要目标是 compatibility 使用现有的PythonAPI和库。Django是兼容的,但您需要检查您所依赖的其他库的兼容性。

c Python库的实现

一些Python库也在C语言中实现,而且速度更快。他们的目标是提供相同的API。请注意,兼容性问题和行为差异并不是未知的(并不总是立即明显的)。