数据库:django ORM如何处理N+1查询
2014-05-26 10:19
323 查看
数据库N+1查询是个常见的问题,简单描述场景如下
在生成列表页面时,首先执行一次
然后逐条获取category.name,又需要执行N次
所以N+1问题其实应该叫做1+N 问题,这只是一个数据库设计模式的问题.但是会对数据库带来很大的压力,一个简单的列表页可能会有几百次数据库查询
N+1问题并不是ORM独有,只是使用orm的时候,数据库表中的行变成一个对象,于是很自然的就容易使用上面的方法来进行查询 不使用orm进行编程的情况,一般直接用子查询或者inner join
子查询或者inner join对数据库来说,也是很费资源的操作,因为需要锁表,高并发的情况下很容易锁死
要解决1+N问题一般有3种方法
数据库反范式设计,说直白点,就是把表合并,设计成冗余表,这可能会带来两个问题
表中存在大量的重复数据项
表中出现大量的空项,整个表格变成一个稀疏矩阵(sparse matrix)
所以,这种方案显然存储效率不高,但是如果针对这两种情况进行优化,也算是是一种不错的解决办法,MongoDB就是这样干的
加缓存 把整个列表页加上缓存. 这样 无论是继续执行1+N次查询,还是用inner join 1次查询搞定,都可以.
这种方法的缺点是
更新缓存 需要成本,增加了代码复杂度
某些场景要求数据实时性,无法使用缓存
把N+1次查询变成2次查询
简单说 先执行
再执行一次
数据库本身处理不了高并发,因为我们只能保证单个数据项的操作是原子的,而数据库的查询是以 列表为基本单元,这是个天然矛盾,无解
数据库设计范式不在web framework能力范围内,所以django的ORM 只支持后面两种做法
Article.ojbects.select_related() 这就是inner join
Article.objects.prefetch_related('category') 这是2次查询
本文地址: http://lutaf.com/156.htm 鲁塔弗原创文章,欢迎转载,请附带原文链接
基本场景
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 join
Article.objects.prefetch_related('category') 这是2次查询
本文地址: http://lutaf.com/156.htm 鲁塔弗原创文章,欢迎转载,请附带原文链接
相关文章推荐
- 数据库:django ORM如何处理N+1查询
- 如何让本地程序调用并处理基于Django所开发网站的数据库
- 如何正确处理数据库中的Null
- DetailsView中如何进行数据验证,如何处理数据库的异常
- 当数据库出现页损坏或校验和出错时如何处理
- 处理上百万条的数据库如何提高处理查询速度
- SQL中数据库表进行互导时,如何处理标识种子列
- 如何正确处理数据库中的Null
- php 处理上百万条的数据库如何提高处理查询速度
- Django的多数据库的处理(垂直分库和水平分库)
- 如何处理数据库中海量数据,以及处理数据库海量数据的经验和技巧
- 数据库查询优化方案(处理上百万级记录如何提高处理查询速度)
- 如何处理数据库字段是bigint型?
- (精)如何利用T_SQL实现数据库备份与还原处理之一
- 如何处理连接数据库JavaBean的错误信息
- SQL中数据库表进行互导时,如何处理标识种子列
- 带格式的文本串,如何使用BCP导入数据库, 单引号‘’如何处理
- 学会如何处理数据库中的NULL
- (精)如何利用T_SQL实现数据库备份与还原处理之一--(如何利用sql语句,正确备份数据库)
- 枚举类型在数据库中的处理(查询的时候如何处理)