数据库管理是后端开发最重要的方面之一。适当优化的数据库可以帮助减少响应时间,从而带来更好的用户体验。在本文中,我们将讨论优化数据库以提高Django应用程序速度的方法。了解Django中的查询集是优化的关键,因此请牢记以下几点:查询集是惰性的,这意味着直到您对查询集执行某些操作(例如迭代它)之前,没有相应的数据库请求。始终通过指定要返回的值的数量来限制数据库查询的结果。在Django中,查询集可以通过迭代、切片、缓存和len()和count()等python方法进行评估。确保充分利用它们。Django查询集被缓存,因此如果您重复使用相同的查询集,将不会发出多个数据库请求,从而最大限度地减少数据库访问。一次检索您需要的所有内容,但请确保只检索您需要的内容。Django数据库索引中的查询优化数据库索引是一种在从数据库中检索记录时加快查询速度的技术。随着应用程序大小的增加,它可能会变慢,用户会注意到,因为它需要更长的时间来获取所需的数据。因此,在处理产生大量数据的大型数据库时,索引是一个不可协商的操作。索引是一种根据各个字段对大量数据进行排序的方法。当您在数据库中的字段上创建索引时,您创建了另一个数据结构,其中包含字段值和指向它相关记录的指针。然后对该索引结构进行排序,使二进制搜索成为可能。例如,这里有一个名为Sale的Django模型:#models.pyfromdjango.dbimportmodelsclassSale(models.Model):sold_at=models.DateTimeField(auto_now_add=True,)charged_amount=models.PositiveIntegerField()在Django的定义中创建模型时,可以将数据库索引添加到特定字段,如下所示:DBIndexing)charged_amount=models.PositiveIntegerField()如果您为此模型运行迁移,Django将在表Sales上创建一个数据库索引,并且它将被锁定直到索引完成。在数据和连接很少的本地开发设置中,这种迁移可能感觉是瞬时的,但当我们谈论生产环境时,具有许多并发连接的大型数据集可能会导致停机,例如获取锁和创建数据库索引可能需要很长时间.你也可以为两个字段创建一个索引,像这样:=models.PositiveIntegerField()classMeta:indexes=[["sold_at","charged_amount"]]数据库缓存数据库缓存是从数据库获得快速响应的最佳方式之一。它确保对数据库的调用更少,从而防止过载。标准缓存操作遵循以下结构:Django提供了一种缓存机制,可以使用不同的缓存后端,例如Memcached和Redis,让您避免多次运行相同的查询。Memcached是一种开源内存系统,可保证在不到一毫秒的时间内缓存结果。它易于设置和扩展。另一方面,Redis是一种开源缓存解决方案,具有与Memcached类似的功能。大多数离线应用程序使用以前缓存的数据,这意味着大多数查询永远不会到达数据库。用户会话应该保存在Django应用程序的缓存中,并且由于Redis在磁盘上维护数据,因此登录用户的所有会话都来自缓存而不是数据库。要在Django中使用Memcache,我们需要定义以下内容:BACKEND:定义要使用的缓存后端。LOCATION:ip:portvalue其中ip是Memcached守护程序的IP地址,port是运行Memcached的端口,或者使用适当的方案指向您的Redis实例的URL。要使用Memcached启用数据库缓存,使用以下命令通过pip安装pymemcache:pipinstallpymemcache然后您可以在settings.py中配置缓存设置,如下所示:core.cache.backends.memcached.PyMemcacheCache','LOCATION':'127.0.0.1:11211',}}在上面的示例中,Memcached使用以下pymemcache绑定在本地主机(127.0.0.1)端口11211上运行:使用Redis启用数据库缓存,使用以下命令使用pip安装Redis:pipinstallredissettings.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()foreachinqueryset:do_something(each)andif我们使用iterator(),Django会保持SQL连接打开并读取每条记录,并在读取下一条记录之前调用do_something():queryset=Product.objects.all().iterator()foreachinqueryset:do_something(each)使用持久数据库连接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',}}}使用查询表达式查询表达式定义可用于更新、创建、过滤、排序、注释或聚合操作的值或计算。Django中常用的内置查询表达式是F表达式。让我们看看它是如何工作和有用的。在DjangoQuerysetAPI中,F()表达式用于直接引用模型字段值。它允许您引用模型字段值并对它们执行数据库操作,而无需将它们从数据库中获取并放入Python内存中。相反,Django使用F()对象来生成定义所需数据库活动的SQL短语。例如,假设我们想将所有产品的价格提高20%,那么代码将如下所示:products=Product.objects.all()forproductinproducts:product.price*=1.2product.save()但是,如果我们使用F(),我们可以在单个查询中执行此操作,如下所示:)Django提供prefetch_related()参数通过最小化数据库请求的数量来优化查询集。根据Django官方文档:select_related()“遵循”外键关系,在执行查询时选择其他相关对象数据。prefetch_related()对每个关系进行单独查找,并在Python中进行“连接”。select_related()当要选择的项目是单个对象时,我们使用select_related(),这意味着forwardForeignKey,OneToOne和backOneToOne字段。您可以使用select_related()创建单个查询,该查询返回单个实例的所有相关对象,用于一对多和一对一连接。执行查询时,select_related()从外键关系中检索任何其他相关对象数据。select_related()通过生成SQL连接并在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使用bulk_create()和bulk_update()bulk_create()是一个将提供对象列表的查询方法创建到数据库中。类似地,bulk_update()是一种使用一个查询更新提供的模型实例上的给定字段的方法。例如,如果我们有一个看起来像这样的Post模型:):returnself.title现在,假设我们要向这个模型添加多条数据记录,那么我们可以像这样使用bulk_create():#articlesarticles=[Post(title="Hellopython"),Post(title="Hellodjango"),Post(title="Hellobulk")]#insertdataPost.objects.bulk_create(articles)输出如下:>>>Post.objects.all()
