系统运维

Django 开发人员的 7 个数据库优化最佳实践

字号+作者:益华科技来源:人工智能2025-11-05 11:23:00我要评论(0)

数据库管理是后端开发最重要的方面之一。适当优化的数据库可以帮助减少响应时间,从而带来更好的用户体验。在本文中,我们将讨论在 Django 应用程序中优化数据库以提高速度的方法。了解 Django 中的

数据库管理是开发库优后端开发最重要的方面之一。适当优化的数据数据库可以帮助减少响应时间,从而带来更好的化最用户体验。在本文中,佳实践我们将讨论在 Django 应用程序中优化数据库以提高速度的开发库优方法。

了解 Django 中的数据查询集是优化的关键,因此,化最请记住以下几点:

查询集是佳实践惰性的,这意味着在你对查询集执行某些操作(例如对其进行迭代)之前,开发库优不会发出相应的数据数据库请求。始终通过指定要返回的化最值的数量来限制数据库查询的结果。在 Django 中,佳实践查询集可以通过迭代、开发库优切片、数据缓存和 python 方法(例如len()等)进行评估count()。化最确保充分利用它们。Django 查询集被缓存,因此如果你重复使用相同的查询集,将不会发出多个数据库请求,站群服务器从而最大限度地减少数据库访问。一次检索你需要的所有内容,但请确保你只检索你需要的内容。

Django中的查询优化

数据库索引

数据库索引是一种在从数据库中检索记录时加快查询速度的技术。随着应用程序大小的增加,它可能会变慢,并且用户会注意到,因为获取所需数据需要更长的时间。因此,在处理生成大量数据的大型数据库时,索引是一项不可协商的操作。

索引是一种基于各个字段对大量数据进行排序的方法。当你在数据库中的字段上创建索引时,你将创建另一个数据结构,其中包含字段值以及指向与其相关的记录的指针。然后对该索引结构进行排序,使二进制搜索成为可能。

例如,云服务器这是一个名为 Sale 的 Django 模型:

复制# models.pyfrom django.db import modelsclass Sale(models.Model

):

sold_at = models.DateTimeField

(

auto_now_add=True

,

)

charged_amount = models.PositiveIntegerField()1.2.3.4.5.6.7.8.9.

在定义 Django 模型时,可以将数据库索引添加到特定字段,如下所示:

复制# models.pyfrom django.db import modelsclass Sale(models.Model

):

sold_at = models.DateTimeField

(

auto_now_add=True

,

db_index=True, #DB Indexing

)

charged_amount = models.PositiveIntegerField()1.2.3.4.5.6.7.8.9.10.

如果你为此模型运行迁移,Django 将在表 Sales 上创建一个数据库索引,并且它将被锁定直到索引完成。在本地开发设置中,数据量很少,连接很少,这种迁移可能感觉是瞬间的,但是当我们谈论生产环境时,有很多并发连接的大型数据集可能会导致停机,如获取锁和创建数据库索引可能需要很长时间。

你还可以为两个字段创建单个索引,如下所示:

复制# models.pyfrom django.db import modelsclass Sale(models.Model

):

sold_at = models.DateTimeField

(

auto_now_add=True

,

db_index=True, #DB Indexing

)

charged_amount = models.PositiveIntegerField

()

class Meta

:

indexes =

[

["sold_at", "charged_amount"]]1.2.3.4.5.6.7.8.9.10.11.12.13.14.

数据库缓存

数据库缓存是从数据库获得快速响应的最佳方法之一。它确保对数据库的调用更少,从而防止过载。标准缓存操作遵循以下结构:

Django 提供了一种缓存机制,可以使用不同的缓存后端,如 Memcached 和 Redis,让你避免多次运行相同的查询。

Memcached 是一个开源的云南idc服务商内存系统,可保证在不到一毫秒的时间内提供缓存结果。它易于设置和扩展。另一方面,Redis 是一种开源缓存解决方案,具有与 Memcached 相似的特性。大多数离线应用程序使用以前缓存的数据,这意味着大多数查询永远不会到达数据库。

用户会话应该保存在 Django 应用程序的缓存中,并且因为 Redis 在磁盘上维护数据,所以登录用户的所有会话都来自缓存而不是数据库。

要在 Django 中使用 Memcache,我们需要定义以下内容:

BACKEND:定义要使用的缓存后端。LOCATION:ip:port 值 where ip 是 Memcached 守护程序的 IP 地址, port 是运行 Memcached 的端口,或者是指向你的 Redis 实例的 URL,使用适当的方案。

要使用 Memcached 启用数据库缓存,请pymemcache使用以下命令使用 pip 进行安装:

复制pip install pymemcache1.

然后,你可以settings.py按如下方式配置缓存设置:

复制CACHES =

{

default

: {

BACKEND: django.core.cache.backends.memcached.PyMemcacheCache

,

LOCATION: 127.0.0.1:11211

,

}

}1.2.3.4.5.6.

在上面的示例中,Memcached 使用以下 pymemcache 绑定在 localhost (127.0.0.1) 端口 11211 上运行:

同样,要使用 Redis 启用数据库缓存,请使用以下命令使用 pip 安装 Redis:

复制pip install redis1.

tings.py然后通过添加以下代码来配置你的缓存设置:

复制CACHES =

{

default

: {

BACKEND: django.core.cache.backends.redis.RedisCache

,

LOCATION: redis://127.0.0.1:6379

,

}

}1.2.3.4.5.6.

Memcached 和 Redis 也可用于存储用户身份验证令牌。因为每个登录的人都必须提供一个令牌,所以所有这些过程都会导致大量的数据库开销。使用缓存的令牌将大大加快数据库访问速度。

尽可能使用迭代器

Django 中的查询集通常会在评估发生时缓存其结果,对于该查询集的任何进一步操作,它首先检查是否有缓存的结果。但是,当你使用 时iterator(),它不会检查缓存并直接从数据库中读取结果,也不会将结果保存到查询集。

现在,你一定想知道这有什么帮助。考虑一个查询集,它返回大量具有大量内存的对象进行缓存,但只能使用一次,在这种情况下,你应该使用iterator()。

例如,在下面的代码中,所有记录将从数据库中获取,然后加载到内存中,然后我们将遍历每条记录:

复制queryset = Product.objects.all

()

for each in queryset

:

do_something(each)1.2.3.

而如果我们使用iterator(),Django 将保持 SQL 连接打开并读取每条记录,并 do_something() 在读取下一条记录之前调用:

复制queryset = Product.objects.all().iterator

()

for each in queryset

:

do_something(each)1.2.3.

使用持久性数据库连接

Django 为每个请求创建一个新的数据库连接,并在请求完成后关闭它。这种行为是由 引起的CONN_MAX_AGE,它的默认值为 0。但是应该设置多长时间呢?这取决于你网站上的流量;音量越高,维持连接所需的秒数就越多。通常建议从较低的数字开始,例如 60。

你需要将额外的选项包装在 中 OPTIONS,如留档中详细说明:

复制DATABASES =

{

default

: {

ENGINE: django.db.backends.mysql

,

NAME: dashboard

,

USER: root

,

PASSWORD: root

,

HOST: 127.0.0.1

,

PORT: 3306

,

OPTIONS

: {

CONN_MAX_AGE: 60

,

}

}

}1.2.3.4.5.6.7.8.9.10.11.12.13.

使用查询表达式

查询表达式定义了可以在更新、创建、过滤、排序、注释或聚合操作中使用的值或计算。Django 中常用的内置查询表达式是 F 表达式。让我们看看它是如何工作的并且很有用。

在 Django Queryset API 中,F()表达式用于直接引用模型字段值。它允许你引用模型字段值并对它们执行数据库操作,而无需从数据库中获取它们并进入 Python 内存。相反,Django 使用该F()对象来生成定义所需数据库活动的 SQL 短语。

例如,假设我们想将所有产品的价格提高 20%,那么代码将如下所示:

复制products = Product.objects.all

()

for product in products

:

product.price *= 1.2 product.save()1.2.3.4.

但是,如果我们使用F(),我们可以在单个查询中执行此操作,如下所示:

复制from django.db.models import FProduct.objects.update(price=F(price) * 1.2)1.2.3.

使用 select_related() 和 prefetch_related()

Django 通过最小化数据库请求的数量来提供优化查询集select_related()的prefetch_related()参数。

根据官方 Django 文档:

select_related() “遵循”外键关系,在执行查询时选择其他相关对象数据。

prefetch_related() 对每个关系进行单独的查找,并在 Python 中进行“加入”。

select_related()

我们select_related()在要选择的项目是单个对象时使用,这意味着 forward ForeignKey、OneToOne和 backOneToOne字段。

你可以使用select_related()创建单个查询,该查询返回单个实例的所有相关对象,用于一对多和一对一连接。执行查询时,select_related()从外键关系中检索任何额外的相关对象数据。

select_related()通过生成 SQL 连接并在SELECT表达式中包含相关对象的列来工作。因此,select_related()在同一数据库查询中返回相关项目。

虽然select_related()会产生更复杂的查询,但获取的数据会被缓存,因此处理获取的数据不需要任何额外的数据库请求。

语法看起来像这样:

复制queryset = Tweet.objects.select_related(owner).all()1. prefetch_related()

相反,prefetch_related()用于多对多和多对一连接。它生成一个查询,其中包括查询中给出的所有模型和过滤器。

语法看起来像这样:

复制Book.objects.prefetch_related(author).get(id=1).author.first_name1.

使用bulk_create()和bulk_update()

bulk_create() 是一种通过一次查询将提供的对象列表创建到数据库中的方法。类似地,bulk_update() 是一种使用一个查询更新提供的模型实例上的给定字段的方法。

例如,如果我们有一个如下所示的帖子模型:

复制class Post(models.Model

):

title = models.CharField(max_length=300, unique=True

)

time = models.DateTimeField(auto_now_add=True

)

def __str__(self

):

return self.title1.2.3.4.5.

现在,假设我们要在这个模型中添加多条数据记录,那么我们可以bulk_create()这样使用:

复制#articlesarticles = [Post(title="Hello python"), Post(title="Hello django"), Post(title="Hello bulk"

)]

#insert dataPost.objects.bulk_create(articles)1.2.3.4.5.

输出如下所示:

复制>>> Post.objects.all

()

<QuerySet [<Post: Hello python>, <Post: Hello django>, <Post: Hello bulk>]>1.2.3.

如果我们想更新数据,那么我们可以bulk_update()这样使用:

复制update_queries =

[]

a = Post.objects.get(id=14

)

b = Post.objects.get(id=15

)

c = Post.objects.get(id=16

)

#set update valuea.title="Hello python updated"b.title="Hello django updated"c.title="Hello bulk updated"#appendupdate_queries.extend((a, b, c

))

Post.objects.bulk_update(update_queries, [title])1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

输出如下所示:

复制>>> Post.objects.all

()

<QuerySet [<Post: Hello python updated>, <Post: Hello django updated>, <Post: Hello bulk updated>]>1.2.3.4.

结语

在本文中,我们介绍了在 Django 应用程序中优化数据库性能、减少瓶颈和节省资源的技巧。

原文标题:​​7 Database Optimization Best Practices for Django Developers​​

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 年富供应链(以年富供应链为例,探索供应链管理的关键要素和创新点)

    年富供应链(以年富供应链为例,探索供应链管理的关键要素和创新点)

    2025-11-05 10:38

  • 如何使用谷粒金刚手柄玩电脑游戏(谷粒金刚手柄使用教程,让你畅享游戏快感)

    如何使用谷粒金刚手柄玩电脑游戏(谷粒金刚手柄使用教程,让你畅享游戏快感)

    2025-11-05 10:03

  • HP电脑驱动安装教程(一步步教你安装HP电脑驱动程序,轻松解决硬件问题)

    HP电脑驱动安装教程(一步步教你安装HP电脑驱动程序,轻松解决硬件问题)

    2025-11-05 09:29

  • 电脑显示wifi密码错误的原因分析(探究wifi密码错误的可能原因及解决办法)

    电脑显示wifi密码错误的原因分析(探究wifi密码错误的可能原因及解决办法)

    2025-11-05 09:16

网友点评
精彩导读
作为一个经常喜欢敲命令的人,可能要同时做很多操作,Linux各个桌面的窗口切换有多那啥,这里就不吐槽了, 我总是在想要做另外一个操作,但又不想结束当前的工作,之前我习惯于再打开一个终端,但是后来终端越来越多导致我想找回原来的工作的时候就变的很费力,而且对桌面有洁癖的人不允许任务栏太杂 后来发现了一款终端软件terminator,它支持分割终端,并可以在终端中快速切换.还有一款下拉式的终端软件Guake可以随意呼出隐藏.下面就一一介绍一下.1 安装Terminator复制代码代码如下:2 使用复制代码代码如下:复制代码代码如下:3 Guake复制代码代码如下:在文本模式的环境中使用一个窗口管理器 – 这听起来有点不可思议, 是吧? 然而,你应该记得当 Web 浏览器第一次实现分页浏览的时候吧? 在当时, 这是在可用性上的一个重大进步,它减少了桌面任务栏的杂乱无章和繁多的窗口列表。 对于你的浏览器来说,你只需要一个按钮便可以在浏览器中切换到你打开的每个单独网站, 而不是针对每个网站都有一个任务栏或导航图标。 这个功能非常有意义。若有时你同时运行着几个虚拟终端,你便会遇到相似的情况; 在这些终端之间跳转,或每次在任务栏或窗口列表中找到你所需要的那一个终端,都可能会让你觉得麻烦。 拥有一个文本模式的窗口管理器不仅可以让你像在同一个终端窗口中运行多个 shell 会话,而且你甚至还可以将这些窗口排列在一起。另外,这样还有另一个好处:可以将这些窗口进行分离和重新连接。想要看看这是如何运行的最好方式是自己尝试一下。在一个终端窗口中,输入 screen (在大多数发行版本中,它已经默认安装了或者可以在软件包仓库中找到)。 某些欢迎的文字将会出现 – 只需敲击 Enter 键这些文字就会消失。 现在运行一个交互式的文本模式的程序,例如 nano, 并关闭这个终端窗口。在一个正常的 shell 对话中, 关闭窗口将会终止所有在该终端中运行的进程 – 所以刚才的 Nano 编辑对话也就被终止了, 但对于 screen 来说,并不是这样的。打开一个新的终端并输入如下命令:复制代码代码如下:复制代码代码如下:复制代码代码如下:上图中, tmux 开启了两个窗格: 左边是 Vim 正在编辑一个配置文件,而右边则展示着指导手册页。

作为一个经常喜欢敲命令的人,可能要同时做很多操作,Linux各个桌面的窗口切换有多那啥,这里就不吐槽了, 我总是在想要做另外一个操作,但又不想结束当前的工作,之前我习惯于再打开一个终端,但是后来终端越来越多导致我想找回原来的工作的时候就变的很费力,而且对桌面有洁癖的人不允许任务栏太杂 后来发现了一款终端软件terminator,它支持分割终端,并可以在终端中快速切换.还有一款下拉式的终端软件Guake可以随意呼出隐藏.下面就一一介绍一下.1 安装Terminator复制代码代码如下:2 使用复制代码代码如下:复制代码代码如下:3 Guake复制代码代码如下:在文本模式的环境中使用一个窗口管理器 – 这听起来有点不可思议, 是吧? 然而,你应该记得当 Web 浏览器第一次实现分页浏览的时候吧? 在当时, 这是在可用性上的一个重大进步,它减少了桌面任务栏的杂乱无章和繁多的窗口列表。 对于你的浏览器来说,你只需要一个按钮便可以在浏览器中切换到你打开的每个单独网站, 而不是针对每个网站都有一个任务栏或导航图标。 这个功能非常有意义。若有时你同时运行着几个虚拟终端,你便会遇到相似的情况; 在这些终端之间跳转,或每次在任务栏或窗口列表中找到你所需要的那一个终端,都可能会让你觉得麻烦。 拥有一个文本模式的窗口管理器不仅可以让你像在同一个终端窗口中运行多个 shell 会话,而且你甚至还可以将这些窗口排列在一起。另外,这样还有另一个好处:可以将这些窗口进行分离和重新连接。想要看看这是如何运行的最好方式是自己尝试一下。在一个终端窗口中,输入 screen (在大多数发行版本中,它已经默认安装了或者可以在软件包仓库中找到)。 某些欢迎的文字将会出现 – 只需敲击 Enter 键这些文字就会消失。 现在运行一个交互式的文本模式的程序,例如 nano, 并关闭这个终端窗口。在一个正常的 shell 对话中, 关闭窗口将会终止所有在该终端中运行的进程 – 所以刚才的 Nano 编辑对话也就被终止了, 但对于 screen 来说,并不是这样的。打开一个新的终端并输入如下命令:复制代码代码如下:复制代码代码如下:复制代码代码如下:上图中, tmux 开启了两个窗格: 左边是 Vim 正在编辑一个配置文件,而右边则展示着指导手册页。