• orm查询优化


    1. 理解 QuerySet

    1.1  QuerySet 的懒加载

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

    1. def t7(request):
    2. # 不查询数据库,只要是首次返回queryset,都会受懒加载的影响,不去查询数据库
    3. models.Text_one.objects.filter(name='xx')
    4. # 查询数据库
    5. data = models.Text_one.objects.filter(name='xx')[0:10]
    6. return HttpResponse('ok')

    1.2 数据什么时候被加载

    迭代、使用步长分片、len(),list()数据库才会被查询

    1.3 数据是怎么被保存在内存中的

    每一个 QuerySet 都会有一个缓存来减少对数据库的访问操作,理解其中的运行原理能帮助我们写出最有效的代码。

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

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

    1. # orm
    2. def t7(request):
    3. # 不查询数据库,只要是首次返回queryset,都会受懒加载的影响,不去查询数据库
    4. data_query = models.Text_one.objects.filter(name='xx')
    5. # 只查询一次数据库
    6. a = [item['name'] for item in data_query]
    7. # 每次循环都查询数据库
    8. b = [item['name'] for item in models.Text_one.objects.filter(name='xx')]
    9. return HttpResponse('ok')

    2. 单个model 的object缓存的操作

    2.1 多次获取外键字段

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

    1. # orm
    2. def t7(request):
    3. # 查询数据库
    4. data_obj = models.Text_one.objects.filter(name='xx').first()
    5. # 查询数据库
    6. name1 = data_obj.userinfo
    7. # 缓存机制,不查询数据库
    8. name2 = data_obj.userinfo
    9. return HttpResponse('ok')

     2.2 多对多字段,多次获取一个字段

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

    1. # orm
    2. def t7(request):
    3. # 查询数据库
    4. data_obj = models.Text_one.objects.filter(name='xx').first()
    5. # 查询数据库
    6. name1 = data_obj.auth.all()
    7. # 查询数据库
    8. name2 = data_obj.auth.all()
    9. return HttpResponse('ok')

    2.3 外键关系的查询优化

    (6条消息) select_related和prefetch_related的用法与区别_骑台风走的博客-CSDN博客https://blog.csdn.net/qq_52385631/article/details/126695685?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166357771516800184128511%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=166357771516800184128511&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-126695685-null-null.nonecase&utm_term=select&spm=1018.2226.3001.4450

    其他优化

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

    1. 在大多数查询中,使用 filter() 和 exclude() 在数据库中做过滤,而不是在获取所有数据之后在 Python 里的 for 循环里筛选数据
    2. 在同一个 model 的操作中,如果有涉及到其他字段的操作,可以用到 F 表达式
    3. 使用 annotate 函数在数据库中做聚合(aggregate)的操作

    4. 如果某些查询比较复杂,可以使用原生的 SQL 语句

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

    1. 在使用 get() 来查询单条数据的时候,有两个理由使用唯一索引(unique)或 普通索引(db_index)

    1. 一个是基于数据库索引,查询会更快,
    2. 另一个是如果多条数据都满足查询条件,查询会慢得多,而在唯一索引的约束下则保证这种情况不会发生

    所以使用下面的 id 进行匹配 会比 name字段匹配快得多,因为 id 字段在数据库中有索引且是唯一的

    首先, name字段上没有索引,会导致数据库获取速度慢

    其次,查询并不能保证只返回一个对象,如果匹配上来多个对象,且从数据库中检索并返回数百数千条记录,后果会很严重,其实就会报错,get() 能接受的返回只能是一个实例数据。

    1. def t7(request):
    2. # 快
    3. data_obj = models.Text_one.objects.get(id=1)
    4. # 中
    5. data_obj = models.Text_one.objects.get(name='xx')
    6. # 慢
    7. data_obj = models.Text_one.objects.get(name__startswith='xx')
    8. return HttpResponse('ok')

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

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

    所以这就需要理解并且用到 select_related() 和 prefetch_related() 函数

    不要查询你不需要的数据

    使用 values() 和 values_list() 函数

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

    使用 defer() 和 only()

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

    使用 count()

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

    使用 exists()

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

    使用 update() 和 delete()

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

    直接使用外键的值

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

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

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

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

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

     使用批量的方法

    1. 批量创建

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

    2. 批量更新

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

    3. 批量 insert

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

    4. 批量 remove

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

    参考文章

    (42条消息) Django笔记二十八之数据库查询优化汇总_vv安的浅唱的博客-CSDN博客_django减少数据库查询icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_43354181/article/details/125699467

  • 相关阅读:
    基于SSM的农业信息管理系统的设计与实现(有报告)。Javaee项目。ssm项目。
    【c++】类和对象
    生成验证码图片
    剑指offer56 数组中只出现一次的两个数字
    跟我一起写个虚拟机 .Net 7(三)- 安装LC-3 模拟器和编译器
    代理技术的崭新纪元:Socks5代理和代理IP的多重应用
    第一章:最新版零基础学习 PYTHON 教程(第七节 - Python 中的语句、缩进和注释)
    Python趣味操作推荐
    MySQL:事务的概念 | ACID特性 | 事务并发存在的问题 | 事务处理命令
    ExcelBDD PHP Guideline
  • 原文地址:https://blog.csdn.net/qq_52385631/article/details/126936867