先做性能分析 - 两个工具

  • django.db.connection
from django.db import connection
# context
print connection.queries
# content
''' result is:
[{
'time': '0.002',
'sql': u'SELECT `django_session`.`session_key`, `django_session`.`session_data`, `django_session`.`expire_date` FROM `django_session` WHERE (`django_session`.`session_key` = 5584f8d708ddc2d5e32831885fc36084 AND `django_session`.`expire_date` > 2013-05-07 10:39:36 )'}]
'''
  • django_debug_toolbar link

标准的数据库优化技巧

  • Indexes, 分析应该添加什么样的索引,使用 django.db.models.Field.db_index
  • 使用对应的字段类型
title = models.CharField(max_length=100, blank=True, db_index=True, verbose_name=u'标题')

理解QuerySets

理解QuerySet的求值过程

  • QuerySets是惰性的
news_list = News.object.all()
# 此时并未执行数据库查询
print news_list # 用时方执行查询操作
  • 何时它们被执行.
# 用时方执行查询操作
print news_list
  • 数据如何被缓存
# 这样的QuerySet没有被缓存
print([e.headline for e in Entry.objects.all()])
print([e.pub_date for e in Entry.objects.all()])
# 这么做
entries = Entry.objects.all()
print([e.headline for e in entries])

理解被缓存的属性

  • QuerySet 会被缓存
  • 不可被调用的属性会被缓存
>>> news = News.objects.get(id=1)
>>> news.channel # 此时的channel对象会从数据库取出
>>> news.channel # 这时的channel是缓存的版本,不会造成数据库访问
  • 方法的调用每次都会触发数据库查询
>>> news = News.objects.get(id=1)
>>> news.authors.all() # 执行查询
>>> news.authors.all() # 再次执行查询
  • 注意
  • 模板系统不允许使用括号,但它会自动调用可被调用的属性
  • 自定义的属性需要由你来实现缓存。

使用with模板标签

在模板中使用QuerySet缓存,需要使用with标签

使用iterator()

获取大量数据时

news_list = News.objects.filter(title__contains=u'违法')
for news in news_list.iterator():
print news

让数据库做它自己的工作

基本概念

  • 使用 filter and exclude 在数据库层面执行过滤操作
news_list = News.objects.filter(title__contains=u'和谐').exclude(status=1)
  • 使用 F() object query expressions 在同一模型中使用不同字段进行对比过滤
# 查询所有title和sub_title相同的数据
queryset = News.objects.filter(title=F('sub_title'))
  • 使用 注解
# 给每个对象添加一个news_count的属性
cl = Channel.objects.filter(parent__id=1).annotate(news_count=Count('news'))
print cl[0].news_count
  • 如果这些还不足以生成你需要的SQL的话,继续往下看:

使用 QuerySet.extra()

显式的执行SQL语句

cl = Channel.objects.filter(parent__id=1).extra(
select={
'another_news_count': 'SELECT COUNT(*) FROM web_news WHERE web_news.channel_id = web_channel.id'
}
)
print cl[0].another_news_count

使用原生的SQL

cl = Channel.objects.raw('SELECT * FROM web_channel WHERE parent_id = 1')
print cl
# <RawQuerySet: 'SELECT * FROM web_channel WHERE parent_id = 1'>
for c in cl:
print c

预加载数据

尽量一次加载你需要的数据

  • QuerySet.select_related() ,针对foreign key 和 one-to-one
news = News.objects.select_related().get(id=372924135)
print news.channel # 不会访问数据库
  • QuerySet.prefetch_related() ,1.4中存在, 和select_related()类似,针对many-to-many

不要获取你不需要的数据

使用 QuerySet.values() 和 values_list()

当只需要一个字段的值,返回list或者dict时,使用

  • values
news_list = News.objects.values('title').filter(channel__id=1)
print news_list
# [{'title': ''}, ...]
  • values_list
news_list = News.objects.values_list('title').filter(channel__id=1)
print news_list
# [('新闻标题',),('新闻标题', ) ...]

使用 QuerySet.defer() 和 only()

  • QuerySet.defer() 来延迟加载某字段,加载时会产生额外查询
news_list = News.object.defer('title').all()
n = news_list[0]
print n.title # 会产生额外的查询语句
  • QuerySet.only() 只加载某字段,之后读取任何属性都会产生查询

使用 QuerySet.count()

如果你只是想要获取有多少数据,不要使用 len(queryset) 。

nl = News.objects.filter(channel__id=2)
nl.count()
# SELECT COUNT(*) FROM `web_news` WHERE `web_news`.`channel_id` = 2 ; 'time': '0.014'
len(nl)
# 'time': '0.422'

使用 QuerySet.exists()

如果你只是想要知道是否至少存在一个结果,不要使用 if querysets 。

不要过度使用 count() 和 exists()

比如,假设有一个Email的model,有一个 body 的属性和一个多对多关系的User属性,下面的模板代码是最优的:

{% if display_inbox %}
{% with emails=user.emails.all %}
{% if emails %}
<p>You have {{ emails|length }} email(s)</p>
{% for email in emails %}
<p>{{ email.body }}</p>
{% endfor %}
{% else %}
<p>No messages today.</p>
{% endif %}
{% endwith %}
{% endif %}

它是最优的是因为:

  • 因为QuerySet是惰性的,如果 'display_inbox' 是False的话,这不会产生数据库查询。
  • 使用 with 意味着我们会存储 user.emails.all 在一个变量中供后面使用,这允许被缓存以便重用。
  • {% if emails %} 其实是调用 QuerySet.__nonzero__() ,在数据库层面执行 user.emails.all() ,然后返回结果,放入缓存。
  • {{ emails|length }} 的使用将调用 QuerySet.__len__(),数据已在缓存
  • for 循环的email数据已经在缓存中了。
  • with的使用是关键
  • 每次的QuerySet.count()调用都会产生查询

使用 QuerySet.update() 和 delete()

  • 批量更新使用 QuerySet.update()
  • 批量删除使用 QuerySet.delete()
  • 批量操作不会调用类中定义的 save() 或 delete() 方法

直接使用外键的值

获取频道ID:

news.channel_id

而不是:

news.channel.id

批量插入

  • 用 django.db.models.query.QuerySet.bulk_create() 批量创建对象,减少SQL查询的 数量。比如
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")
  • 这同样适用于 ManyToManyFields, 因此,这么做
team.members.add(me, my_friend)
  • ...而不是这么做
team.members.add(me)
team.members.add(my_friend)
  • ...这里 team 和 members 是多对多的关系。

「Django」数据库访问优化的更多相关文章

  1. 数据库访问优化漏斗法则- 四、减少数据库服务器CPU运算

    数据库访问优化漏斗法则这个优化法则归纳为5个层次:1.减少数据访问次数(减少磁盘访问)2.返回更少数据(减少网络传输或磁盘访问)3.减少交互次数(减少网络传输)4.减少服务器CPU开销(减少CPU及内 ...

  2. Django查询数据库性能优化

    现在有一张记录用户信息的UserInfo数据表,表中记录了10个用户的姓名,呢称,年龄,工作等信息. models文件 from django.db import models class Job(m ...

  3. 高效解决「SQLite」数据库并发访问安全问题,只这一篇就够了

    Concurrent database access 本文译自:https://dmytrodanylyk.com/articles/concurrent-database/ 对于 Android D ...

  4. 「Django」rest_framework学习系列-API访问跨域问题

    #以中间件方式解决API数据访问跨域问题1.API下新建文件夹下写PY文件a.引入内置类继承: from django.middleware.common import MiddlewareMixin ...

  5. 数据库访问优化之四:减少数据库服务器CPU运算

    1.使用绑定变量 绑定变量是指SQL中对变化的值采用变量参数的形式提交,而不是在SQL中直接拼写对应的值. 非绑定变量写法:Select * from employee where id=123456 ...

  6. 「Django」contenttypes基本用法

    当一张表和多个表ForeignKey关联,并且多个FK中只能选择其中一个或其中n个时,可以利用contenttypes,只需定义三个字段就搞定! contenttypes 是Django内置的一个应用 ...

  7. 「Django」rest_framework学习系列-序列化

    序列化方式一 :在业务类里序列化数据库数据 class RolesView(APIView): def get(self,request,*args,**kwargs): roles = models ...

  8. 「Django」学习之路,持续更改

    一.setting设置 1.设置 局域网可以部署连接 ALLOWED_HOSTS = ['*.besttome.com','192.168.1.100'] 2.static配置 STATIC_URL ...

  9. 「Django」与mysql8连接的若干问题

    1.setting配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', #数据库驱动名 'NAME': 'my_tes ...

随机推荐

  1. 基于C#的机器学习--机器学习的基本知识

    机器学习的基本知识 ,…用n个观测值测量.但我们不再对Y的预测感兴趣,因为我们不再有Y了,我们唯一感兴趣的是在已有的特征上发现数据模式: ​ 在前面的图中,我们可以看到这样的数据本身更适合于非线性方法 ...

  2. 1.openldap介绍

    1.openldap介绍 OpenLDAP是轻型目录访问协议(Lightweight Directory Access Protocol,LDAP)的自由和开源的实现,在其OpenLDAP许可证下发行 ...

  3. 15 分钟用 ML 破解一个验证码系统

    人人都恨验证码——那些恼人的图片,显示着你在登陆某网站前得输入的文本.设计验证码的目的是,通过验证你是真实的人来避免电脑自动填充表格.但是随着深度学习和计算机视觉的兴起,现在验证码常常易被攻破. 我拜 ...

  4. c# WPS DLL及其调用

    1.dll分享(含xsl及docx的dll) 链接:https://pan.baidu.com/s/1c1ImV14OndmvIb4W-_WL2A 密码:d2rx 2.方法: 1.先在类的前面(类外面 ...

  5. 18软工实践-第八次作业(课堂实战)-项目UML设计(团队)

    目录 团队信息 分工选择 课上分工 课下分工 ToDolist alpha版本要做的事情 燃尽图 UML 用例图 状态图 活动图 类图 部署图 实例图 对象图 时序图 包图 通信图 贡献分评定 课上贡 ...

  6. 找xpath好用的工具(比较少用,针对只能在IE上打开的网站)

    有一些网站只能在IE浏览器里打开,不像firefox那样有好多好用的插件来找元素的xpath,css path等. 当然现在IE也可以,F12出现像firebug那样的窗口,来查看元素. 这里呢在介绍 ...

  7. js获取窗口滚动条高度、窗口可视范围高度、文档实际内容高度、滚动条离浏览器底部的高度

    1.获取窗口可视范围的高度 //获取窗口可视范围的高度 function getClientHeight(){ var clientHeight=0; if(document.body.clientH ...

  8. c 读取文本

    #include <stdio.h> #include <stdlib.h> #include <string.h> #define max 10 #define ...

  9. 大型Java web项目分布式架构演进-分布式部署

    http://blog.csdn.net/binyao02123202/article/details/32340283/ 知乎相关文章https://www.zhihu.com/question/2 ...

  10. dom对象转成jquery对象时候 变成数组 jquery转成dom时候 取数组第一个