数据库: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 ...
随机推荐
- [SharePoint 2010] 自定义字段类型开发(二)
在SharePoint 2010中实现View Action Button效果. http://www.sharepointblogs.be/blogs/vandest/archive/2008/06 ...
- WORD学习之制作座位表
1.点击插入-表格-插入表格,将表格的列数设置为6,行数为10,再点击“确定”按钮
- Web Word和Excel
暂时收集点资料备用 Excel http://www.cnblogs.com/downmoon/archive/2011/05/30/2063258.html http://www.cnblogs.c ...
- 前端开发--面试题整理(JS篇)
1.截取字符串abcdace的acealert('abcdace'.substring(4)); 2.规避javascript多人开发函数重名问题命名空间封闭空间js模块化mvc(数据层.表现层.控制 ...
- 就最近学习MVC4.0的页面用法学到的东西
最近进了一家新公司,学习的东西还是蛮多的,首先了解的是@using(new Ajax.beginForm("",null,new AjaxOptions() { OnSuccess ...
- Mysql中的少用函数
1.查询时需要转换类型,大多发生在数字和字符串.时间和字符串之间 Mysql提供了两个个类型转换函数:CAST和CONVERT CAST() 和CONVERT() 函数可用来获取一个类型的值,并产生另 ...
- Linux中的命令 make -f 是什么意思
出处:http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380146d96864968d4e414c422460 ...
- qt qml ajax 获取 json 天气数据示例
依赖ajax.js类库,以下代码很简单的实现了获取天气json数据并展示的任务 [TestAjax.qml] import QtQuick 2.0 import "ajax.js" ...
- 005_重写 Standard Delete Button
以后会用JS直接删除,但是在加载.js时候出现问题,会在以后进一步追踪完善: <apex:page standardController="Opportunity" > ...
- [转]SSAS没有注册类 (异常来自 HRESULT:0x80040154 (REGDB_E_CLASSNOTREG)) (Microsoft Visual Studio)的解决办法
转自:http://www.cnblogs.com/xvqm00/archive/2011/07/15/2107338.html 打开SSAS 数据源视图浏览数据时,提示 没有注册类别 (异常来自 H ...