数据库N+1查询是个常见的问题,简单描述场景如下

基本场景

  1. class Category(models.Model):
  2. name = models.CharField(max_length=30)
  3. class Article(models.Model):
  4. title = models.CharField(max_length=30)
  5. body = models.TextField()
  6. category = models.ForeignKey(Category)
  7. time    = models.DateTimeField()
  8. #----列表页模板
  9. {% for a in Article.objects.all %}
  10. {{ a.title }}
  11. {{ a.category.name }}
  12. {% 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种方法

  1. 数据库反范式设计,说直白点,就是把表合并,设计成冗余表,这可能会带来两个问题

    1. 表中存在大量的重复数据项
    2. 表中出现大量的空项,整个表格变成一个稀疏矩阵(sparse matrix)

    所以,这种方案显然存储效率不高,但是如果针对这两种情况进行优化,也算是是一种不错的解决办法, MongoDB就是这样干的

  2. 加缓存 把整个列表页加上缓存. 这样 无论是继续执行1+N次查询,还是用inner join 1次查询搞定,都可以.

    这种方法的缺点是

    1. 更新缓存 需要成本,增加了代码复杂度
    2. 某些场景要求数据实时性,无法使用缓存
  3. 把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 join
  • Article.objects.prefetch_related('category') 这是2次查询

性能优化详情参考:http://www.cnblogs.com/zknublx/p/6197382.html

数据库:django ORM如何处理N+1查询的更多相关文章

  1. 数据库开发-Django ORM的多对多查询

    数据库开发-Django ORM的多对多查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.联合主键问题 CREATE TABLE `employees` ( `emp_no` ...

  2. 数据库开发-Django ORM的一对多查询

    数据库开发-Django ORM的一对多查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.联合主键问题 CREATE TABLE `employees` ( `emp_no` ...

  3. 数据库开发-Django ORM的单表查询

    数据库开发-Django ORM的单表查询 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.查询集 1>.查询集相关概述 查询会返回结果的集,它是django.db.mod ...

  4. Django ORM --- 建表、查询、删除基础

    1.什么是ORM ORM的全称是Object Relational Mapping,即对象关系映射.它的实现思想就是将关系数据库中表的数据映射成为对象,以对象的形式展现,这样开发人员就可以把对数据库的 ...

  5. Django ORM中的模糊查询

    ORM映射 什么是ORM映射?在笔者认为就是对SQL语句的封装,所写语句与SQL对应语句含义相同,使开发更加简单方便,不过也是存在弊端的,使程序运行效率下降.例如: UserInfo.objects. ...

  6. django ORM多对多正向查询时查询返回结果为None

    表 class Books(models.Model): '''书籍''' id = models.AutoField(primary_key=True) name = models.CharFiel ...

  7. Django orm 常用查询筛选总结

    本文主要列举一下django orm中的常用查询的筛选方法: 大于.大于等于 小于.小于等于 in like is null / is not null 不等于/不包含于 其他模糊查询 model: ...

  8. 优化Django ORM中的性能问题(含prefetch_related 和 select_related)

    Django是个好工具,使用的很广泛. 在应用比较小的时候,会觉得它很快,但是随着应用复杂和壮大,就显得没那么高效了.当你了解所用的Web框架一些内部机制之后,才能写成比较高效的代码. 怎么查问题 W ...

  9. Django学习之数据库与ORM

    二.ORM表模型 表(模型)的创建: 1.ORM之增(create.save) 一对多(ForeignKey): 多对多(ManyToManyField()): 2.ORM之删(delete) 3.O ...

随机推荐

  1. 1.初识Linux

    1.严格来讲,Linux不是一个操作系统,只是Linux系统的内核,它是计算机的软件与硬件连通的枢纽. 2.关机和重启指令 关机:(1)init 0   关闭前清理和更新有关信息         (2 ...

  2. Python之路----------shutil模块

    高级的文件.文件夹.压缩包 处理模块 复制文件: import shutil f1 = open('test') f2 = open('test2','w') shutil.copyfileobj(f ...

  3. css ul li 横向排列

    因为li是块级元素,默认占一行的,要想实现横向排列,一般通过以下两个方法:float:left这样设置有一个问题,li浮动以后则脱离了文本流,即不占位置,如果它的父级元素有具体的样式且没有固定宽高,建 ...

  4. Syntax highlighting in fenced code blocks

    Python @requires_authorization def somefunc(param1='', param2=0): r'''A docstring''' if param1 > ...

  5. chattr的常用参数详解

    chattr的常用参数详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在实际生产环境中,有的运维工程师不得不和开发和测试打交道,在我们公司最常见的就是部署接口.每天每个人部署的 ...

  6. CCNET+MSBuild+SVN实现每日构建

    最近开始将源代码迁移到SVN,于是便考虑到如何从SVN定期获取源码,自动编译并部署以减轻工作量并提高工作效率.通过多方搜集资料并进行研究,基本实现了这个功能.对于每日构建的概念就不具体展开了,可以在各 ...

  7. linux下flash的安装

    linux的初学者可能会遇到各种各样的问题,其中的问题就有一个,linux下的flash插件怎么安装呢? 首先前往flash官网下载好对应的文件,然后提取里面的一个叫做 libflashplayer. ...

  8. Linux下常用yum命令

    linux各发行版有多种包管理机制,下面介绍基于RedHat系的yum包管理命令: yum -y install xxx                                     无需询 ...

  9. Entity Framework7 入门之全功能.NET版本下使用EF7(含源码)另附数据迁移常见错误处理

    Entity Framework7 入门之全功能.NET(Console, WinForms, WPF等)使用EF7 昨天,我们介绍了EF的新特性和开发计划,如果你还不了解,请移步 Entity Fr ...

  10. CheckBox 半选中状态

    <input type='checkbox' />可以半选中,这个特性,很多浏览器都支持,包括Firefox,Chrome和IE 用 input.indeterminate 这个属性来获取 ...