• 如何高性能地使用django的orm


             在我的上一篇文章中《关于django项目是否要使用外键》中总结过,对于小系统,是建议大家尽量地使用外键,因为它的易用性和便利性;对于大系统,最好不要使用外键,因为要考虑到系统的性能,可移植性等。同时引出了如何高性能地使用orm的问题,我下面会对小系统(使用外键)和大系统(不使用外键)的情况下如何使用orm的问题进行说明分析。
             django的orm是非常强大的,它把数据库相关的操作都隔离了开来,让我们能够像访问普通的变量一样去访问数据库的数据,所以django是非常适合新手来使用,功能足够强大。当时orm也带来很多问题,过于高度的封装,忽略了效率,导致执行效率很差,所以如何使用高效地orm也是很重要的。一般来说,我们的接口都是不外乎是一个数据表的’增删查改‘,所以我就分别对这几个操作进行例子化分。
     
    先假设表 t_table1, 拥有一个外键a
    1. 查: 这里可以分为  单个数据查询, 以及 列表查询
    1.1 单个数据查询: 
    通常的代码是  object =  t_table1.objects.filter(id=1).first()
    在我们执行了这句代码之后,获取到object对象,它实际上执行的sql语句并不是一句,而是2句:
    select * from table1 where id=id
    select * from a where id=id
     
    从sql的效果来看,这个效率很低,其实这2句sql语句可以优化为一句,利用连表的形式进行多表查询。
    上面代码可以优化为这样:object =  t_table1.objects.select_related().filter(id=1).first()
    加了一个select_related(),  那么实际执行的sql语句就变成了
    select * from table1 
         INNER JOIN a  on a.id=table1.a_id
    where id=id
    执行效率会高很多,试想一下,假如这里有10个外键,那么效率提升了就非常大了
     
    1.2 列表查询
    通常的列表查询的代码:
    objects =  t_table1.objects.all()
    ret = [ model_to_dict(i) for i in objects]
    它实际上执行的sql语句数量是非常庞大的,假如t_table1数据一共有100条,参考上面单个数据数据的执行方式,
    它实际的执行是这样的:
    for i in range(100):
         select * from table1 where id=id
         select * from a where id=id
    这个就是orm的不足,按照上面ret = [ model_to_dict(i) for i in objects] 这句代码,它是每循环一次就执行一次sql语句。
    等于执行了200条的sql语句。
    如果t_table1 有10000条数据,有10个外键,那么就等于执行了20万条sql语句,简直是不可想象。
    优化方案:
    把代码改为 
    ret =  t_table1.objects.select_related().all().values()
    这个values() 这个方法我就不详细说,有兴趣可以查查资料 https://www.cnblogs.com/rgxx/p/10382664.html
    改为这样之后,它实际上执行的sql语句是从20万条sql,变成了1条,效率优化非常明显:
    select * from table1 
         INNER JOIN a  on a.id=table1.a_id
     
    2. 增
    新增单个数据,通常的代码是:
    t_table1.objects.create(a_id=id)
    或者是
    object = t_table1()
    object.a_id = id
    object.save()
     
    批量创建:在批量创建数据的时候,使用这种方式就会有效率的问题
    假如要创建100个数据:
    for i in range(100):
          t_table1.objects.create(a_id=id) 
    这样实际上执行的sql语句就会变成了100条:
    for i in range(100):
         insert into t_table1(a_id) values(id)
     
     
    object_list=[]
    for i in range(100):
         object = t_table1()
         object.a_id = id
         object_list.append(object) 
    t_table1.objects.bulk_create(object_list)
    优化后执行的sql语句只有一条,效率提高了不知道多少倍:
    insert into t_table1(a_id) values(id1),(id2), (id3),  (id4),  (id5), .... (id100);
        
    3. 删 :
    单个数据删除:  
    t_table1.objects.filter(id=id).delete()  
    或者是
    object = t_table1.objects.filter(id=id).first()
    object.delete()
     
    批量删除:
    这里要注意的是,假如需要删除全部数据,参考上面查询的优化项,不要进行循环删除:
    objects = t_table1.objects.all()
    for i in objects:
        objects.delete()
    如果执行这样的代码,会造成执行了非常多的sql语句,假如这样有100条数据,等于执行下面的sql语句
    for i in range(100):
         select * from table1 where id=id
         select * from a where id=id
         delete from table1 where id=id
    等于执行了300条的sql语句,好恐怖
    优化方案:
        t_table1.objects.all().delete 即可, 这样就只是会执行一句的sql语句:
    delete from table1 where id=id
     
    4. 改:
    单个数据修改:  
    t_table1.objects.filter(id=id).update(a_id=id)  
    或者是
    object = t_table1.objects.filter(id=id).first()
    object.a_id=id
    object.save()
     
    批量修改:
    这里要注意的是,假如需要批量修改或者修改全部数据,参考上面查询的优化项,不要进行循环修改:
    objects = t_table1.objects.all()
    for i in objects:
        i.a_id=id
        objects.save()
    如果执行这样的代码,会造成执行了非常多的sql语句,假如这样有100条数据,等于执行下面的sql语句
    for i in range(100):
         select * from table1 where id=id
         select * from a where id=id
         update table1 set a_id=id where id=id
    等于执行了300条的sql语句
    优化方案:
        直接使用 t_table1.objects.all().update(a_id=id)  
    上面的4个总结是在使用orm的时候比较通用的经验,无论是小系统(有外键)和大系统(无外键)都适用。
     
    5. 复杂的查询条件:
    对于一个比较成熟的系统来说,肯定会遇到需要非常复杂的搜索条件的需求,例如bug系统中,会有很多筛选项,状态,处理人,创建时间等等。查询条件是一个非常复杂的一个组成。
     
    对于拥有外键的小系统来说,这些复杂的条件都可以使用 Q 这个对象来构造查询条件,这里有参考资料:
    例如需要查询id > 5 或者是 a的id > 2 的数据;

    t_table1.objects.select_related().filter(Q(id>5 | Q(a__id>2))

    这里变换为sql语句就是:

    select * from table1 
         INNER JOIN a  on a.id=table1.a_id
    where id>5 and a.id>2
    但是对于没有外键的大系统来说,是没法使用Q来构造条件的,因为Q有一种语法是 “__” ,这种是专门用于外键的。
    举个例子,上面这句代码
    t_table1.objects.select_related().filter(Q(id>5 | Q(a__id>2)),a 这个字段是t_table1的外键,它能够通过 a__xxx,  来访问a对应的表的属性;
    比如a, 有一个叫name的字符串的属性,我想筛选t_table1 中 的a 的name等于‘hello’的数据,那么可以这么写:
    t_table1.objects.select_related().filter(Q(a__name='hello')
     
    但是假如没有外键,我们是不能这么做的,想想假如我们不用外键,会怎么定义t_table1表就明白了,我们会把表定义为:
    class t_table1:
          a_id=models.IntegerField()
     
    那么这时候我们应该怎么做,想要实现这么复杂的查询,我们只能借助于编写原始sql语句。
    使用sql语句有两种方式:
    1、Manager.raw(raw_queryparams=Nonetranslations=None)
    刚刚的筛选语句可以改为:
    t_table1.objects.raw('
         select * from table1 
                INNER JOIN a  on a.id=table1.a_id
         where id>5 and a.id>2
    '):
    2. 使用 Executing custom SQL directly
    from django.db import connection

    def my_custom_sql(self):
        with connection.cursor() as cursor:
        #执行sql语句
            cursor.execute(‘
     
             select * from table1 
                     INNER JOIN a  on a.id=table1.a_id
             where id>5 and a.id>2
             ’)
        #查出一条数据
            row = cursor.fetchone()
        #查出所有数据
            #row = cursor.fetchall()
        return row

     
    所以对于不含外键的大系统,原生的sql语句是没法绕开的一道坎,必须要使用的。
    至于含有外键的小系统,只用orm已经足够。
    现如今我做的所有大系统都是orm和sql并用的。
    我的原则是: 简单查询使用orm,因为足够方便。复杂查询使用sql语句,因为足够强大。
     
     
     
     
     
     
  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    Scapy 工具介绍
    ubuntu虚拟机使用open-vm-tools代替vmware-tools
    docker、vmware和PD的区别
    ubuntu查看OpenGL版本
    SQL Server 常用近百条SQL语句(收藏版)
    Intellij IDEA 如何去掉 @Autowired 注入警告
    awtk-linux-fb 使用 double framebuffer 闪烁的问题
  • 原文地址:https://www.cnblogs.com/wilken/p/12419236.html
Copyright © 2020-2023  润新知