数据库管理是后端开发最重要的方面之一。适当优化的数据库可以帮助减少响应时间,从而带来更好的用户体验。 在本文中,我们将讨论在 Django 应用程序中优化数据库以提高速度的方法。虽然,我们不会单独深入研究每个概念,因此,请参阅 以获取完整详细信息。 官方 Django 文档 理解 Django 中的查询 了解 Django 中的查询集是优化的关键,因此,请记住以下几点: 查询集是惰性的,这意味着在您对查询集执行某些操作(例如对其进行迭代)之前,不会发出相应的数据库请求。 始终通过指定要返回的值的数量来限制数据库查询的结果。 在 Django 中,可以通过迭代、切片、缓存和 python 方法(例如 、 等)来评估查询集。确保充分利用它们。 len() count() Django 查询集被缓存,因此如果您重复使用相同的查询集,将不会发出多个数据库请求,从而最大限度地减少数据库访问。 一次检索您需要的所有内容,但请确保您只检索您需要的内容。 Django中的查询优化 数据库索引 数据库索引是一种在从数据库中检索记录时加快查询速度的技术。随着应用程序大小的增加,它可能会变慢,并且用户会注意到,因为获取所需数据需要更长的时间。因此,在处理生成大量数据的大型数据库时,索引是一项不可协商的操作。 索引是一种基于各个字段对大量数据进行排序的方法。当您在数据库中的字段上创建索引时,您将创建另一个数据结构,其中包含字段值以及指向与其相关的记录的指针。然后对该索引结构进行排序,使二进制搜索成为可能。 例如,这是一个名为 Sale 的 Django 模型: # models.py from django.db import models class Sale(models.Model): sold_at = models.DateTimeField( auto_now_add=True, ) charged_amount = models.PositiveIntegerField() 在定义 Django 模型时,可以将数据库索引添加到特定字段,如下所示: # models.py from django.db import models class Sale(models.Model): sold_at = models.DateTimeField( auto_now_add=True, db_index=True, #DB Indexing ) charged_amount = models.PositiveIntegerField() 如果您为此模型运行迁移,Django 将在表 Sales 上创建一个数据库索引,并且它将被锁定直到索引完成。在本地开发设置中,数据量很少,连接很少,这种迁移可能感觉是瞬间的,但是当我们谈论生产环境时,有很多并发连接的大型数据集可能会导致停机,如获取锁和创建数据库索引可能需要很长时间。 您还可以为两个字段创建单个索引,如下所示: # models.py from django.db import models class 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"]] 数据库缓存 数据库缓存是从数据库获得快速响应的最佳方法之一。它确保对数据库的调用更少,从而防止过载。标准缓存操作遵循以下结构: Django 提供了一种缓存机制,可以使用不同的缓存后端,如 Memcached 和 Redis,让您避免多次运行相同的查询。 Memcached 是一个开源的内存系统,可保证在不到一毫秒的时间内提供缓存结果。它易于设置和扩展。另一方面,Redis 是一种开源缓存解决方案,具有与 Memcached 相似的特性。大多数离线应用程序使用以前缓存的数据,这意味着大多数查询永远不会到达数据库。 用户会话应该保存在 Django 应用程序的缓存中,并且因为 Redis 在磁盘上维护数据,所以登录用户的所有会话都来自缓存而不是数据库。 要在 Django 中使用 Memcache,我们需要定义以下内容: 定义要使用的缓存后端。 BACKEND: values 其中 是 Memcached 守护进程的 IP 地址, 是运行 Memcached 的端口,或者是指向您的 Redis 实例的 URL,使用适当的方案。 LOCATION: ip:port ip port 要使用 Memcached 启用数据库缓存,请使用以下命令使用 pip 安装 : pymemcache pip install pymemcache 然后,您可以在 中配置缓存设置,如下所示: settings.py CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', 'LOCATION': '127.0.0.1:11211', } } 在上面的示例中,Memcached 使用 绑定在 localhost (127.0.0.1) 端口 11211 上运行: pymemcache 同样,要使用 Redis 启用数据库缓存,请使用以下命令使用 pip 安装 Redis: pip install redis 然后通过添加以下代码在 中配置缓存设置: settings.py CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379', } } Memcached 和 Redis 也可用于存储用户身份验证令牌。因为每个登录的人都必须提供一个令牌,所以所有这些过程都会导致大量的数据库开销。使用缓存的令牌将大大加快数据库访问速度。 尽可能使用迭代器 Django 中的查询集通常会在评估发生时缓存其结果,对于该查询集的任何进一步操作,它首先检查是否有缓存的结果。但是,当您使用 时,它不会检查缓存并直接从数据库中读取结果,也不会将结果保存到查询集。 iterator() 现在,您一定想知道这有什么帮助。考虑一个查询集,它返回大量具有大量内存的对象进行缓存,但只能使用一次,在这种情况下,您应该使用 。 iterator() 例如,在下面的代码中,所有记录将从数据库中获取,然后加载到内存中,然后我们将遍历每条记录: queryset = Product.objects.all() for each in queryset: do_something(each) 而如果我们使用 ,Django 将保持 SQL 连接打开并读取每条记录,并在读取下一条记录之前调用 : iterator() do_something() queryset = Product.objects.all().iterator() for each in queryset: do_something(each) 使用持久性数据库连接 Django 为每个请求创建一个新的数据库连接,并在请求完成后关闭它。这种行为是由 引起的,它的默认值为 0。但是应该设置多长时间呢?这取决于您网站上的流量;音量越高,维持连接所需的秒数就越多。通常建议从较低的数字开始,例如 60。 CONN_MAX_AGE 您需要将额外选项包装在 中,详见 : 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', } } } 使用查询表达式 查询表达式定义了可以在更新、创建、过滤、排序、注释或聚合操作中使用的值或计算。 Django 中常用的内置查询表达式是 F 表达式。让我们看看它是如何工作的并且很有用。 这些表达式在 和 中定义,但为了方便起见,它们是可用的并且通常从 导入。 注意: django.db.models.expressions django.db.models.aggregates django.db.models F 表达式 在 Django Queryset API 中, 表达式用于直接引用模型字段值。它允许您引用模型字段值并对它们执行数据库操作,而无需从数据库中获取它们并进入 Python 内存。相反,Django 使用 对象来生成定义所需数据库活动的 SQL 短语。 F() F() 例如,假设我们想将所有产品的价格提高 20%,那么代码将如下所示: products = Product.objects.all() for product in products: product.price *= 1.2 product.save() 但是,如果我们使用 ,我们可以在单个查询中执行此操作,如下所示: F() from django.db.models import F Product.objects.update(price=F('price') * 1.2) 使用 和 select_related() prefetch_related() Django 提供了 和 参数,通过最小化数据库请求的数量来优化你的查询集。 select_related() prefetch_related() 根据官方 Django 文档: “遵循”外键关系,在执行查询时选择其他相关对象数据。 select_related() 对每个关系进行单独的查找,并在 Python 中进行“加入”。 prefetch_related() select_related() 当要选择的项目是单个对象时,我们使用 ,这意味着前向 , 和后向 字段。 select_related() ForeignKey OneToOne OneToOne 您可以使用 创建单个查询,该查询返回单个实例的所有相关对象,用于一对多和一对一连接。执行查询时, 从外键关系中检索任何额外的相关对象数据。 select_related() select_related() 通过生成 SQL 连接来工作,并在 表达式中包含相关对象的列。因此, 返回同一数据库查询中的相关项目。 select_related() SELECT select_related() 尽管 会产生更复杂的查询,但获取的数据会被缓存,因此处理获取的数据不需要任何额外的数据库请求。 select_related() 语法看起来像这样: queryset = Tweet.objects.select_related('owner').all() prefetch_related() 相反, 用于多对多和多对一连接。它生成一个查询,其中包括查询中给出的所有模型和过滤器。 prefetch_related() 语法看起来像这样: Book.objects.prefetch_related('author').get(id=1).author.first_name 注意:不应使用 SQL 处理多对多关系,因为在处理大型表时可能会出现许多性能问题。这就是为什么 prefetch_related 方法在 Python 中连接表,避免进行大型 SQL 连接。 在 详细了解 和 之间的区别。 此处 select_related() prefetch_related() 使用 和 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.title 现在,假设我们要向这个模型添加多条数据记录,那么我们可以像这样使用 : bulk_create() #articles articles = [Post(title="Hello python"), Post(title="Hello django"), Post(title="Hello bulk")] #insert data Post.objects.bulk_create(articles) 输出如下所示: >>> Post.objects.all() <QuerySet [<Post: Hello python>, <Post: Hello django>, <Post: Hello bulk>]> 如果我们想更新数据,那么我们可以像这样使用 : bulk_update() update_queries = [] a = Post.objects.get(id=14) b = Post.objects.get(id=15) c = Post.objects.get(id=16) #set update value a.title="Hello python updated" b.title="Hello django updated" c.title="Hello bulk updated" #append update_queries.extend((a, b, c)) Post.objects.bulk_update(update_queries, ['title']) 输出如下所示: >>> Post.objects.all() <QuerySet [<Post: Hello python updated>, <Post: Hello django updated>, <Post: Hello bulk updated>]> 结论 在本文中,我们介绍了在 Django 应用程序中优化数据库性能、减少瓶颈和节省资源的技巧。 我希望你觉得它有帮助。继续阅读!