首页 > 其他分享 >django查询中如何进行查询优化

django查询中如何进行查询优化

时间:2023-11-22 18:23:24浏览次数:23  
标签:QuerySet 数据库 queryset django 使用 entry 查询 优化

一.性能方面

1.connection.queries

>>> from django.db import connection
>>> connection.queries
[{'sql': 'SELECT polls_polls.id, polls_polls.question, polls_polls.pub_date FROM polls_polls',
'time': '0.002'}]

  使用connection.queries可以来查看所有的sql查询语句及查询时间,仅仅当系统的 DEBUG 参数设为 True,上述命令才可生效,而且是按照查询的顺序排列的一个数组。因为这个记录是按照时间顺序排列的,所以 connection.queries[-1] 总能查询到最新的一条记录。

2.多数据库操作

如果系统用的是多个数据库,那么可以通过 connections['db_alias'].queries 来操作,比如我们使用的数据库的 alias 为 user:

>>> from django.db import connections
>>> connections['user'].queries

如果想清空之前的记录,可以调用 reset_queries() 函数:

from django.db import reset_queries
reset_queries() 

3. explain

我们也可以使用 explain() 函数来查看一条 QuerySet 的执行计划,包括索引以及联表查询的的一些信息,这个操作和mysql数据库中操作是一样的。

>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog  (cost=0.00..35.50 rows=10 width=12)
  Filter: (title = 'My Blog'::bpchar)

也可以加一些参数来查看更详细的信息:

>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True, analyze=True))
Seq Scan on public.blog  (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
  Output: id, title
  Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 mse

二、使用标准的数据库优化技术

数据库优化技术指的是在查询操作中 SQL 底层本身的优化,不涉及 Django 的查询操作

比如使用 索引 index,可以使用 Meta.indexes 或者字段里的 Field.db_index 来添加索引

如果频繁的使用到 filter()、exclude()、order_by() 等操作,建议为其中查询的字段添加索引,因为索引能帮助加快查询。

三、理解 QuerySet

1. 理解 QuerySet 获取数据的过程

1) QuerySet 的懒加载:

一个查询的创建并不会访问数据库,直到获取这条查询语句的具体数据的时候,系统才会去访问数据库:

>>> q = Entry.objects.filter(headline__startswith="What")  # 不访问数据库
>>> q = q.filter(pub_date__lte=datetime.date.today())  # 不访问数据库
>>> q = q.exclude(body_text__icontains="food")  # 不访问数据库
>>> print(q)  # 访问数据库

  上述中,只有最后一步才去访问了数据库。

2) 数据什么时候被加载:

迭代、使用步长分片、使用len()函数获取长度以及使用list()将QuerySet 转化成列表的时候数据才会被加载。

3) 数据是怎么被保存在内存中的:

当我们创建一个 QuerySet 的之后,并且数据第一次被加载,对数据库的查询操作就发生了。

然后 Django 会保存 QuerySet 查询的结果,并且在之后对这个 QuerySet 的操作中会重复使用,不会再去查询数据库。

当然,如果理解了这个原理之后,用得好就OK,否则会对数据库进行多次查询,造成性能的浪费,比如下面的操作:

>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

  上面的代码,同样一个查询操作,系统会查询两遍数据库,而且对于数据来说,两次的间隔期之间,Entry 表可能的某些数据库可能会增加或者被删除造成数据的不一致。

为了避免此类问题,我们可以这样复用这个 QuerySet :

>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # 查询数据库
>>> print([p.pub_date for p in queryset]) # 从缓存中直接使用,不会再次查询数据库

  上述中只查询了数据库1次。

使用数组的切片或者根据索引(即下标)不会缓存数据

举个例子,比如下面的操作,在缓存整个 QuerySet 数据前,查询一个 QuerySet 的部分数据时,系统会重复查询数据库:

>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # 查询数据库
>>> print(queryset[5]) # 再次查询数据库

  而在下面的操作中,整个 QuerySet 都被提前获取了,那么根据索引的下标获取数据,则能够从缓存中直接获取数据

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # 查询数据库
>>> print(queryset[5]) # 使用缓存
>>> print(queryset[5]) # 使用缓存

  上述中[entry for entry in queryset] 将queryset加载到了内存中。

如果一个 QuerySet 已经缓存到内存中,那么下面的操作将不会再次查询数据库:

>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset) 

2.理解 QuerySet 的缓存

除了 QuerySet 的缓存,单个 model 的 object 也有缓存的操作。

我们这里简单理解为外键和多对多的关系。

比如下面外键字段的获取,blog 是 Entry 的一个外键字段:

>>> entry = Entry.objects.get(id=1)
>>> entry.blog   # Blog 的实例被查询数据库获得
>>> entry.blog   # 第二次获取,使用缓存信息,不会查询数据库

  而多对多关系的获取每次都会被重新去数据库获取数据:

>>> entry = Entry.objects.get(id=1)
>>> entry.authors.all()   # 查询数据库
>>> entry.authors.all()   # 再次查询数据库

  以上的操作,我们都可以通过 select_related() 和 prefetch_related() 较少对数据库的访问。

4、操作尽量在数据库中完成而不是在内存中

1.在大多数查询中,使用 filter() 和 exclude() 在数据库中做过滤,而不是在获取所有数据之后在 Python 里的 for 循环里筛选数据

2.在同一个 model 的操作中,如果有涉及到其他字段的操作,可以用到 F 表达式

3.使用annotate函数在数据库中做聚合(aggregate)的操作

5、使用唯一索引来查询单个对象

 正常主键查询,或者使用索引查询(普通索引、联合索引、唯一索引)

6、如果知道需要什么数据,那么就立刻查出来

能一次性查询所有需要的相关的数据的话,就一次性查询出来,不要在循环中做多次查询,因为那样会多次访问数据库。

7、不要查询你不需要的数据

1. 使用 values() 和 values_list() 函数

如果需求仅仅是需要某几个字段的数据,可以用到的数据结构为 dict 或者 list,可以直接使用这两个函数来获取数据。

2. 使用 defer() 和 only()

如果明确知道只需要,或者不需要什么字段数据,可以使用这两个方法,一般常用在 textfield 上,避免加载大数据量的 text 字段

3. 使用 count()

如果想要获取总数,使用 count() 方法,而不是使用 len() 来操作,如果数据有一万条,len() 操作会导致这一万条数据都加载到内存里,然后计数。

4. 使用 exists()

如果仅仅是想查询数据是否至少存在一条可以使用 if QuerySet.exists() 而不是 if queryset 的形式

5. 使用 update() 和 delete()

能够批量更新和删除的操作就使用批量的方法,挨个去加载数据,更新数据,然后保存是不推荐的

6. 直接使用外键的值

如果需要外键的值,直接调用早就在这个 object 中的字段,而不是加载整个关联的 object 然后取其主键id

如:

entry.blog_id

而不是:

entry.blog.id

7. 如果不需要排序的结果,就不要order_by()

每一个字段的排序都是数据库的操作需要额外消耗性能的,所以如果不需要的话,尽量不要排序

如果在 Meta.ordering 中有一个默认的排序,而你不需要,可以通过 order_by() 不添加任何参数的方法来取消排序

为数据库添加索引,可以帮助提高排序的性能

8、使用批量的方法

1. 批量创建

对于多条 model 数据的创建,尽可能的使用 bulk_create() 方法,这是要优于挨个去 create() 的

2. 批量更新

bulk_update 方法也优于挨个数据在 for 循环中去 save()

3. 批量 insert

对于 ManyToMany 方法,使用 add() 方法的时候添加多个参数一次性操作比多次 add 要好

my_band.members.add(me, my_friend)

  要优于:

my_band.members.add(me)
my_band.members.add(my_friend) 

4. 批量 remove

当去除 ManyToMany 中的数据的时候,也是能一次性操作就一次性操作:

my_band.members.remove(me, my_friend)

  要好于:

my_band.members.remove(me)
my_band.members.remove(my_friend)

  

       

 

标签:QuerySet,数据库,queryset,django,使用,entry,查询,优化
From: https://www.cnblogs.com/shaoyishi/p/17849996.html

相关文章

  • Django11days
    Cookie和session发展史cookie补充django操作cookiesessiondjango操作session三种方法———————————————————————————————————————————————————————————————————————————————————————......
  • 数据库服务器开启内存大页优化及机制
    一、背景在一次Oracle数据库健康检查报告中,显示PageTables所占用内存过大,建议配置大页PageTables(页表):用于将内存的虚拟地址翻译成物理地址,随着内存地址分配得越来越多,这个需要从Linux分页了解起二、Linux分页在计算机操作系统中,内存分页是一种内存管理方案,也是现代操作系统......
  • 记一次sql查询慢的原因分析(金仓)
    在进行数据筛选时,遇到了一个处理4000万条数据的问题(时序数据),在发现这个表很大后,就对其加了索引,然后金仓执行以下sqlSELECTA,B,C,timeFROMTableWHEREid=#{param.id}ORDERBYtime  ,但是发现并没有什么用,查询时间需要7秒左右,就很超乎我的想象,但是有时候就会特别快,大概......
  • 火山引擎ByteHouse:如何优化ClickHouse物化视图能力?
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群近期,火山引擎ByteHouse升级了基于ClickHouse的物化视图能力,为解决数据量爆炸式增长带来的查询速度和响应时间缓慢等问题提供了有效手段。火山引擎ByteHouse是一款云原生数据仓库,为用户提供......
  • 火山引擎ByteHouse:如何优化ClickHouse物化视图能力?
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群近期,火山引擎ByteHouse升级了基于ClickHouse的物化视图能力,为解决数据量爆炸式增长带来的查询速度和响应时间缓慢等问题提供了有效手段。火山引擎ByteHouse是一款云原生数据仓库,为用户......
  • sqlalchemy快速使用,sqlalchemy原生操作,sqlalchemy操作表,一对多关系,多对多关系,scoped线
    1sqlalchemy快速使用......
  • Oracle创建查询账户,并授予查询其他用户表权限
    要在Oracle数据库中创建新的查询用户,以便可以访问表和视图等信息一、登录DBA权限的账号1、可以使用命令窗口登录,进行数据库管理connsys/sys123assysdba;2、也可以使用plsql登录以上两种方式自选其一登录。二、新建表空间、用户此部分操作由于在另一篇随笔中有详细记录......
  • 磁盘管理(性能参数查询!!!!!!!!!!!)
    别瞎捣鼓,这些才是Linux磁盘管理命令精华!点击关注......
  • 服务器性能参数查询(top、free、uptime等其他)
    Linux常用的性能指标命令原创 Reathin 沐雨花飞蝶 2023-11-2008:20 发表于江西收录于合集#linux7个#linux系统2个沐雨花飞蝶一位咸鱼Coder的学习、生活与见闻102篇原创内容公众号 uptimeuptime命令用于显示系统的运行时间和平均负载。......
  • Django 修改默认外键关联到其他字段
    默认值关联classOrg(models.Model):"""组织架构"""name=models.CharField("组织机构名称",max_length=100,default='')pid=models.ForeignKey('self',verbose_name="上级",on_dele......