数据库:django ORM如何处理N+1查询
数据库N+1查询是个常见的问题,简单描述场景如下
基本场景
- class Category(models.Model):
- name = models.CharField(max_length=30)
- class Article(models.Model):
- title = models.CharField(max_length=30)
- body = models.TextField()
- category = models.ForeignKey(Category)
- time = models.DateTimeField()
- #----列表页模板
- {% for a in Article.objects.all %}
- {{ a.title }}
- {{ a.category.name }}
- {% endfor %}
在生成列表页面时,首先执行一次
select * from article limited 0,N
然后逐条获取category.name,又需要执行N次
select name from category where id = category_id
所以N+1问题其实应该叫做1+N 问题,这只是一个数据库设计模式的问题.但是会对数据库带来很大的压力,一个简单的列表页可能会有几百次数据库查询
N+1问题并不是ORM独有,只是使用orm的时候,数据库表中的行变成一个对象,于是很自然的就容易使用上面的方法来进行查询 不使用orm进行编程的情况,一般直接用子查询或者inner join
select a.*,c.name from article a,category b where a.category_id = b.id
子查询或者inner join对数据库来说,也是很费资源的操作,因为需要锁表,高并发的情况下很容易锁死
要解决1+N问题一般有3种方法
数据库反范式设计,说直白点,就是把表合并,设计成冗余表,这可能会带来两个问题
- 表中存在大量的重复数据项
- 表中出现大量的空项,整个表格变成一个稀疏矩阵(sparse matrix)
所以,这种方案显然存储效率不高,但是如果针对这两种情况进行优化,也算是是一种不错的解决办法, MongoDB就是这样干的
加缓存 把整个列表页加上缓存. 这样 无论是继续执行1+N次查询,还是用inner join 1次查询搞定,都可以.
这种方法的缺点是
- 更新缓存 需要成本,增加了代码复杂度
- 某些场景要求数据实时性,无法使用缓存
把N+1次查询变成2次查询
简单说 先执行
select *,category_id from article limited 0,N然后遍历结果列表,取出所有的category_id,去掉重复项
再执行一次
select name from category where id in (category id list)
性能优化
把子查询/join查询 分成两次,是 高并发网站数据库调优中非常有效的常见做法,虽然会花费更多的cpu时间,但是避免了系统的死锁,提高了并发响应能力
数据库本身处理不了高并发,因为我们只能保证单个数据项的操作是原子的,而数据库的查询是以 列表为基本单元,这是个天然矛盾,无解
数据库设计范式不在web framework能力范围内,所以django的ORM 只支持后面两种做法
Article.ojbects.select_related()这就是inner joinArticle.objects.prefetch_related('category')这是2次查询
性能优化详情参考:http://www.cnblogs.com/zknublx/p/6197382.html
数据库:django ORM如何处理N+1查询的更多相关文章
- 数据库开发-Django ORM的多对多查询
数据库开发-Django ORM的多对多查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.联合主键问题 CREATE TABLE `employees` ( `emp_no` ...
- 数据库开发-Django ORM的一对多查询
数据库开发-Django ORM的一对多查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.联合主键问题 CREATE TABLE `employees` ( `emp_no` ...
- 数据库开发-Django ORM的单表查询
数据库开发-Django ORM的单表查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.查询集 1>.查询集相关概述 查询会返回结果的集,它是django.db.mod ...
- Django ORM --- 建表、查询、删除基础
1.什么是ORM ORM的全称是Object Relational Mapping,即对象关系映射.它的实现思想就是将关系数据库中表的数据映射成为对象,以对象的形式展现,这样开发人员就可以把对数据库的 ...
- Django ORM中的模糊查询
ORM映射 什么是ORM映射?在笔者认为就是对SQL语句的封装,所写语句与SQL对应语句含义相同,使开发更加简单方便,不过也是存在弊端的,使程序运行效率下降.例如: UserInfo.objects. ...
- django ORM多对多正向查询时查询返回结果为None
表 class Books(models.Model): '''书籍''' id = models.AutoField(primary_key=True) name = models.CharFiel ...
- Django orm 常用查询筛选总结
本文主要列举一下django orm中的常用查询的筛选方法: 大于.大于等于 小于.小于等于 in like is null / is not null 不等于/不包含于 其他模糊查询 model: ...
- 优化Django ORM中的性能问题(含prefetch_related 和 select_related)
Django是个好工具,使用的很广泛. 在应用比较小的时候,会觉得它很快,但是随着应用复杂和壮大,就显得没那么高效了.当你了解所用的Web框架一些内部机制之后,才能写成比较高效的代码. 怎么查问题 W ...
- Django学习之数据库与ORM
二.ORM表模型 表(模型)的创建: 1.ORM之增(create.save) 一对多(ForeignKey): 多对多(ManyToManyField()): 2.ORM之删(delete) 3.O ...
随机推荐
- RabbitMQ学习之集群部署
我们先搭建一个普通集群模式,在这个模式基础上再配置镜像模式实现高可用,Rabbit集群前增加一个反向代理,生产者.消费者通过反向代理访问RabbitMQ集群. 架构图如下: 设计架构可以如下:在一个集 ...
- Spring操作指南-IoC基础环境配置(基于XML)
- delphi之事件
delphi的事件如上图所示: 图中oncloseup代表的是日期选择下拉框关闭时触发的事件. //事件定义 procedure Ondatechange(Sender: TObject); //事件 ...
- IOS 调用系统照相机和相册
/** * 调用照相机 */ - (void)openCamera { UIImagePickerController *picker = [[UIImagePickerController all ...
- 导入csv文件到mysql
原文 给自己做备份的,高手们请忽略. 数据太大,用数据库客户端软件直接导入非常卡,还是直接执行SQL吧. 1.指定文件路径. 2.字段之间以逗号分隔,数据行之间以\r\n分隔(我这里文件是以\n分隔的 ...
- AD中各层的说明
PCB各层说明: 1.丝印层(OverLay,Silkscreen):有顶层丝印和底层丝印.用来画器件轮廓,器件编号和一些图案等. 2.信号层(SignalLayer):对于两层板,主要是TopLay ...
- Android 自定义View (五)——实践
前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...
- Python爬虫爬取糗事百科段子内容
参照网上的教程再做修改,抓取糗事百科段子(去除图片),详情见下面源码: #coding=utf-8#!/usr/bin/pythonimport urllibimport urllib2import ...
- gradle使用
查看子工程引用的jar包 gradle child_project:dependencies > dep.txt
- linux命令行中单个减号表示标准输入输出流
tar cvf - /src/myprojectsrc 这里本来是要输出到一个.tar文件的,目标.tar文件应该放在上面命令的减号处,因此这里的减号表示标准输出流. 减号单独作为命令行参数时,是标准 ...