Django 的性能与优化
简介
通常一个人首先关心的是编写代码的作品,它的逻辑功能根据需要产生预期的输出。但是,有时这还不足以使代码高效地工作。
在这种情况下,需要的是某种东西-实际上,通常是一系列东西-可以提高代码的性能,而又不会,或者仅以最小的方式影响代码的行为。
一般方法
什么是你最优化的?
清楚了解“性能”的含义很重要。不仅有一个指标。
提高速度可能是程序最明显的目标,但有时可能会寻求其他性能改进,例如降低内存消耗或减少对数据库或网络的需求。
一个方面的改进通常会带来另一方面的改进,但并非总是如此;有时一个人甚至可以牺牲另一个人。例如,程序速度的提高可能会导致它使用更多的内存。更糟糕的是,这可能是自欺欺人的-如果速度提升如此之耗内存,以至于系统开始耗尽内存,那么您的弊大于利。
还有其他需要权衡的方面。您自己的时间是宝贵的资源,比CPU时间更宝贵。有些改进可能太难了,不值得实施,或者可能影响代码的可移植性或可维护性。并非所有的性能改进都值得付出努力。
因此,您需要知道要实现哪些性能改进,并且还需要知道有充分的理由朝着这个方向瞄准-并且您需要:
性能基准测试
仅仅猜测或假设效率低下在代码中是没有用的。
Django工具
django-debug-toolbar是一个非常方便的工具,可以深入了解您的代码在做什么以及花了多少时间。特别是,它可以显示页面生成的所有SQL查询以及每个查询花费了多长时间。
工具栏也可以使用第三方面板,该面板可以(例如)报告缓存性能和模板渲染时间。
第三方服务
有许多免费服务可以从远程HTTP客户端的角度分析和报告站点页面的性能,实际上是在模拟实际用户的体验。
这些无法报告您的代码内部,但可以提供有用的洞察力来了解您网站的整体性能,包括无法在Django环境中充分衡量的方面。示例包括:
还有一些付费服务执行类似的分析,其中包括一些支持Django的服务,这些服务可以与您的代码库集成以更全面地分析其性能。
从一开始就做好事
优化方面的某些工作涉及解决性能缺陷,但是某些工作也可以内置于您要做的事情中,这是您甚至在开始考虑提高性能之前就应该采用的良好实践的一部分。
在这方面,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 }}
一般而言,最适合该工作的级别是适合编写代码的最低级别。
注意
上面的示例仅是说明性的。
首先,在现实生活中,您需要考虑计数前后发生的事情,以找出在特定情况下执行此操作的最佳方法。数据库优化文档描述了一种情况,在这种情况下,模板中的计数会更好。
其次,还有其他选择要考虑:在实际情况下,直接从模板调用方法可能是最合适的选择。{{ my_bicycles.count }}QuerySet count()
缓存
通常,计算值很昂贵(即耗费资源且速度很慢),因此将值保存到可快速访问的缓存中以备下次使用时会产生巨大的好处。
Django具有完善的缓存框架以及其他较小的缓存功能,这是一项足够重要且功能强大的技术。
该缓存框架
Django的缓存框架通过保存动态内容,因此无需为每个请求进行计算,就为提高性能提供了非常重要的机会。
为了方便起见,Django提供了不同级别的缓存粒度:您可以缓存特定视图的输出,或者仅缓存难以生成的片段,甚至整个站点。
实施缓存不应被视为改善性能不佳的代码的替代方法,因为它的编写质量很差。这是生成性能良好的代码的最后步骤之一,而不是捷径。
cached_property
通常必须多次调用一个类实例的方法。如果该功能很昂贵,那么这样做会很浪费。
使用cached_property装饰器可以保存属性返回的值。下次在该实例上调用该函数时,它将返回保存的值,而不是重新计算它。请注意,这仅适用于将self参数作为唯一参数的方法,并将方法更改为属性。
某些Django组件也具有自己的缓存功能;这些将在下面与那些组件相关的部分中讨论。
了解懒惰
懒惰是对缓存的一种补充策略。缓存通过保存结果来避免重新计算。懒惰会延迟计算,直到真正需要它为止。
惰性允许我们在实例化它们之前,甚至在可能实例化它们之前都引用它们。这有许多用途。
例如,可以在甚至不知道目标语言之前就使用惰性翻译,因为直到真正需要翻译后的字符串(例如在渲染的模板中)时,它才发生。
懒惰也是一种通过首先避免工作来节省精力的方法。就是说,懒惰的一个方面是什么也要做,直到必须要做,因为毕竟可能没有必要。因此,懒惰可能会影响性能,并且相关工作的成本越高,通过懒惰获得的收益就越大。
Python提供了许多用于懒惰求值的工具,尤其是通过 生成器和生成器表达式构造。值得阅读Python的惰性,以发现在代码中使用惰性模式的机会。
Django中的惰性
Django本身很懒。在的评估中可以找到一个很好的例子QuerySets。QuerySet是惰性的。因此,QuerySet可以创建,传递和与其他对象组合使用 QuerySets,而无需实际进行任何数据库访问以获取其描述的项目。传递的是QuerySet对象,而不是数据库最终需要的项目集合。
另一方面,某些操作将强制评估QuerySet。避免过早评估a QuerySet可以节省对数据库的昂贵而不必要的行程。
Django还提供了一个keep_lazy()装饰器。这允许使用惰性参数调用的函数本身表现为惰性,仅在需要时才进行评估。因此,在严格要求之前,不会调用惰性参数(可能是昂贵的参数)进行评估。
数据库
数据库优化
Django的数据库层提供了多种方法来帮助开发人员从其数据库中获得最佳性能。该数据库优化文档汇聚链接到相关文件,并增加了各种技巧,大纲的步骤尝试优化数据库使用情况时服用。
其他与数据库有关的技巧
启用持久连接可以在大部分请求处理时间中加快与数据库帐户的连接。
例如,这对网络性能有限的虚拟主机有很大帮助。
HTTP性能
中间件
Django随附了一些有用的中间件 ,可以帮助您优化网站的性能。它们包括:
ConditionalGetMiddleware
添加了对现代浏览器的支持,以基于ETag和Last-Modified标头有条件地获取响应 。如果需要,它还会计算并设置一个ETag。
GZipMiddleware
压缩所有现代浏览器的响应,节省带宽和传输时间。请注意,当前将GZipMiddleware视为安全风险,并且容易受到使TLS / SSL提供的保护无效的攻击的攻击。有关GZipMiddleware更多信息,请参阅警告。
会话
使用缓存的会话
使用缓存的会话可能是一种通过避免从较慢的存储源(如数据库)中加载会话数据,而将频繁使用的会话数据存储在内存中来提高性能的方法。
静态文件
静态文件(根据定义不是动态的)是实现优化收益的绝佳目标。
ManifestStaticFilesStorage
通过利用Web浏览器的缓存功能,您可以在初始下载后完全消除给定文件的网络命中。
ManifestStaticFilesStorage在静态文件的文件名后附加一个与内容相关的标记,以使浏览器可以安全地长期缓存它们,而不会丢失将来的更改-文件更改时,标记也将更改,因此浏览器将自动重新加载资产。
“缩小”
几个第三方Django工具和软件包提供了“最小化” HTML,CSS和JavaScript的功能。它们删除了不必要的空格,换行符和注释,并缩短了变量名,从而减小了站点发布的文档的大小。
模板性能
注意:
- 使用比使用更快{% block %}{% include %}
- 由许多小块组装而成的严重碎片化的模板会影响性能
缓存的模板加载器
启用通常可以大大提高性能,因为它避免了每次需要渲染每个模板时就对每个模板进行编译。cached template loader
使用不同版本的可用软件
有时值得检查您所使用软件的不同版本和性能更好的版本。
这些技术面向希望突破已经充分优化的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。请注意,兼容性问题和行为差异并不是未知的(并且并不总是立即可见)。
详情参考: https://docs.djangoproject.com/en/3.0/