• ORM性能


    一、ORM性能优化

    • 能用values,就别用对象进行查询
    • 非要用对象的时候使用 select_related 主动连表
    • prefetch_related 子查询
    • only只拿某个字段,defer排出字段

    1、利用标准数据库优化技术

    传统数据库优化技术博大精深,不同的数据库有不同的优化技巧,但重心还是有规则的。在这里算是题外话,挑两点通用的说说:

    • 索引,给关键的字段添加索引,性能能更上一层楼,如给表的关联字段,搜索频率高的字段加上索引等。Django建立实体的时候,支持给字段添加索引,具体参考Django.db.models.Field.db_index。按照经验,Django建立实体之前应该早想好表的结构,尽量想到后面的扩展性,避免后面的表的结构变得面目全非。
    • 使用适当字段类型,本来varchar就搞定的字段,就别要text类型。

    2、了解Django的QuerySets

      了解Django的QuerySets对象,对优化简单程序有至关重要的作用。QuerySets是有缓存的,一旦取出来,它就会在内存里呆上一段时间,尽量重用它。

    # 了解缓存属性:
    >>> entry = Entry.objects.get(id=1)
    >>> entry.blog   # 博客实体第一次取出,是要访问数据库的
    >>> entry.blog   # 第二次再用,那它就是缓存里的实体了,不再访问数据库
    >>> entry = Entry.objects.get(id=1)
    >>> entry.authors.all()   # 第一次all函数会查询数据库
    >>> entry.authors.all()   # 第二次all函数还会查询数据库
    • all,count exists是调用函数(需要连接数据库处理结果的),注意在模板template里的代码,模板里不允许括号,但如果使用此类的调用函数,一样去连接数据库的,能用缓存的数据就别连接到数据库去处理结果。还要注意的是,自定义的实体属性,如果调用函数的,记得自己加上缓存策略。
    • 利用好模板的with标签:

    模板中多次使用的变量,要用with标签,把它看成变量的缓存行为吧。

    • 使用QuerySets的iterator():

    通常QuerySets先调用iterator再缓存起来,当获取大量的实体列表而仅使用一次时,缓存行为会耗费宝贵的内存,这时iterator()能帮到你,iterator()只调用iterator而省 去了缓存步骤,显著减少内存占用率,具体参考相关文档。

    3、数据库的工作就交给数据库本身计算,别用Python处理

    • 使用 filter and exclude 过滤不需要的记录,这两个是最常用语句,相当是SQL的where
    • 同一实体里使用F()表达式过滤其他字段
    • 使用annotate对数据库做聚合运算

    不要用python语言对以上类型数据过滤筛选,同样的结果,python处理复杂度要高,而且效率不高, 白白浪费内存

    • extra虽然扩展性不太好,但功能很强大,如果实体里需要需要增加额外属性,不得已时,通过extra来实现,也是个好办法
    • 使用原生的SQL语句 如果发现Django的ORM已经实现不了你的需求,而extra也无济于事的时候,那就用原生SQL语句

    4、如果需要就一次性取出你所需要的数据

    单一动作(如:同一个页面)需要多次连接数据库时,最好一次性取出所有需要的数据,减少连接数据库次数。

    此类需求推荐使用QuerySet.select_related() (主动连表)和 prefetch_related()(被动连表)

    相反,别取出你不需要的东西,模版templates里往往只需要实体的某几个字段而不是全部,这时QuerySet.values() 和 values_list(),对你有用,它们只取你需要的字段,返回字典dict和列表list类型的东西,在模版里够用即可,这可减少内存损耗,提高性能

    同样QuerySet.defer()only()对提高性能也有很大的帮助,一个实体里可能有不少的字段,有些字段包含很多元数据,比如博客的正文,很多字符组成,Django获取实体时(取出实体过程中会进行一些python类型转换工作),我们可以延迟大量元数据字段的处理,只处理需要的关键字段,这时QuerySet.defer()就派上用场了,在函数里传入需要延时处理的字段即可;而only()和defer()是相反功能

    使用QuerySet.count()代替len(queryset),虽然这两个处理得出的结果是一样的,但前者性能优秀很多。同理判断记录存在时,QuerySet.exists()比if queryset实在强得太多了

    5、懂减少数据库的连接数

    使用 QuerySet.update() 和 delete(),这两个函数是能批处理多条记录的,适当使用它们事半功倍;如果可以,别一条条数据去update delete处理。

    对于一次性取出来的关联记录,获取外键的时候,直接取关联表的属性,而不是取关联属性,如:

    entry.blog.id
    优于
    entry.blog__id
    
    
    # 善于使用批量插入记录,如:
    Entry.objects.bulk_create([
        Entry(headline="Python 3.0 Released"),
        Entry(headline="Python 3.1 Planned")
    ])
    优于
    Entry.objects.create(headline="Python 3.0 Released")
    Entry.objects.create(headline="Python 3.1 Planned")
    # 前者只连接一次数据库,而后者连接两次
    
    
    # 还有相似的动作需要注意的,如:多对多的关系,
    my_band.members.add(me, my_friend)
    优于
    my_band.members.add(me)
    my_band.members.add(my_friend)

    二、多数据库/读写分离/一主多从/分库分表

    #1.配置文件
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'cnblog',  # 要连接的数据库,连接前需要创建好
            'USER': 'root',  # 连接数据库的用户名
            'PASSWORD': '',  # 连接数据库的密码
            'HOST': '127.0.0.1',  # 连接主机,默认本级
            'PORT': 3306  # 端口 默认3306
        },
        'db': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        },
        'db2': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
        }
    }
    
    
    #2.数据库迁移命令
    python manage.py makemigrations
    python manage.py migrate --database db2
    
    
    
    #3.读写分离
    #手动使用(方式一)
    def de_test(request):
        s = models.Student.objects.using('db2').all()  ##使用using选择数据库不加的话就是默认(default)的数据库
        models.Student.objects.using('db2').create(name='xxx', )
        print(s)
        for i in s:
            i.save(using='db2')
    
        return render(request,'index.html')
    
    
    #自动(方式二)
    # 1.项目根目录下(myrouter.py)
    class Router:
        def db_for_read(self,model,**kwargs):
            return 'default'
        def db_for_write(self,model,**kwargs):
            return 'db2'
    
    
    #2.配置文件
    DATABASE_ROUTERS = ['myrouter.Router']
    
    
    #3.使用
    def de_test(request):
        s = models.Student.objects.all()
        models.Student.objects.create(name='xxx', )
        print(s)
        return render(request,'index.html')
    
    
    ####################################################
    #一主多从
    # 项目根目录下(myrouter.py)
    import random
    class Router:
        def db_for_read(self,model,**kwargs):
            return random.choice(['db','db2','default'])
        def db_for_write(self,model,**kwargs):
            return 'db2'
    
    
    ####################################################
    #分库分表
    # app01  db1     app02  db2
    # 项目根目录下(myrouter.py)
    class Router:
        def db_for_read(self,model,**kwargs):
            if model._meta.app_label == 'app01':
                return 'db'
            elif model._meta.app_label == 'app02':
                return 'db2'
        def db_for_write(self,model,**kwargs):
            if model._meta.app_label == 'app01':
                return 'db'
            elif model._meta.app_label == 'app02':
                return 'db2'

    三、ORM中的批量操作

    1、数据模型定义

    from django.db import models
    
    class Product(models.Model):
        name = models.CharField(max_length=200)
        price = models.DecimalField(max_digits=10, decimal_places=2)

    2、批量插入数据

    批量插入数据的时候,首先要创建一个对象的列表,然后调用bulk_create方法,一次将列表中的数据插入到数据库中。

    product_list_to_insert = list()
    for x in range(10):
        product_list_to_insert.append(Product(name='product name ' + str(x), price=x))
    Product.objects.bulk_create(product_list_to_insert)

    3、批量更新数据

    批量更新数据时,先进行数据过滤,然后再调用update方法进行一次性地更新。下面的语句将生成类似update....frrom....的SQL语句。

    Product.objects.filter(name__contains='name').update(name='new name')

    4、批量删除数据

    批量更新数据时,先是进行数据过滤,然后再调用delete方法进行一次性删除。下面的语句讲生成类似delete from ... where ... 的SQL语句。

    Product.objects.filter(name__contains='name query').delete()
  • 相关阅读:
    基于FPGA的ARP协议实现
    Modelsim 仿真错误集锦
    基于FPGA的IIC驱动设计
    状态机跑飞的解决办法
    基于FPGA的检测时钟脉冲的高电平及低电平的中点标志位设计
    基于FPGA的UART实现
    基于FPGA的数字秒表设计
    Matlab的常用调试方法
    基于FPGA的花样流水灯
    **time_limited.sof文件
  • 原文地址:https://www.cnblogs.com/bubu99/p/11635394.html
Copyright © 2020-2023  润新知