数据库n+1问题解决方案

标签: 数据库 问题解决 | 发表时间:2015-08-14 17:50 | 作者:lizhuang
出处:http://www.iteye.com
数据库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 join
Article.objects.prefetch_related('category') 这是2次查询
本文地址: http://lutaf.com/156.htm 鲁塔弗原创文章,欢迎转载,请附带原文链接

已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [数据库 问题解决] 推荐:

数据库系统load飙高问题解决思路

- - BlogJava-qileilove
数据库服务器器load 飙高的报警,比如:.   如何处理load 异常飙高的报警呢. 本文尝试从原理,原因,解决方法来阐述这类问题的解决思路.   CPU作为服务器的关键资源经常成为性能瓶颈的根源,CPU使用率高并不总是意味着CPU工作繁忙,它有可能是正在等待其他子系统. 在进行性能分析时,将所有子系统当做一个整体来看是非常重要的,因为在子系统中可能会出现瀑布效应.

数据库n+1问题解决方案

- - 数据库 - ITeye博客
数据库N+1查询是个常见的问题,简单描述场景如下.     #----列表页模板{% for a in Article.objects.all %}. 在生成列表页面时,首先执行一次. 然后逐条获取category.name,又需要执行N次. 所以N+1问题其实应该叫做1+N 问题,这只是一个数据库设计模式的问题.但是会对数据库带来很大的压力,一个简单的列表页可能会有几百次数据库查询.

Spring MVC 3.2.4 ResponseBody 编码问题解决

- - 编程语言 - ITeye博客
首先请确保Spring版本为3.2.4. 问题1:使用@ResponseBody注解,返回对象类型时,如Map,中文字符,在客户端会显示为???. 解决办法:请检查依赖jar包,确保spring-context-support.jar的版本也是3.2.4,则可显示中文;. 问题2:使用@ResponseBody注解,返回String时,中文字符,在客户端会显示为???,并且contextType中会缺失encoding值,即为text/html但是,没有后面的encode.

Weblogic部署CAS3.5问题解决方案

- - ITeye博客
webLogic版本:10.3.6.0. cas服务端版本:3.5.2.1. 部署一套SSO环境,选用CAS最新版做为服务端,在开发阶段选用Tomcat6做为应用容器未出现任何问题,业务功能整合完毕后准备将CAS服务端移植到weblogic环境下,然后问题出现了. 在整个部署过程中,主要遇到两个问题.

问题解决型沟通(肆):基本原则

- Tim - 透明思考 - Thoughts
一次通常的多人参加的沟通,有一些基本的原则,我把它叫做“SOFT原则”:. Subject:每次沟通应该有且仅有一个主题. 如果有多个主题,也许需要不同的人、不同的时间、不同的方式. One conversation:任何时间仅有一个人在说话,其他人应该听这个人说. Facilitator:应该有一个主持人.

CSS HACK:IE6、IE7、IE8、Firefox兼容性问题解决方案

- - 博客园_首页
1.区别IE和非IE浏览器. background:blue; /*非IE 背景藍色*/. background:red \9; /*IE6、IE7、IE8背景紅色*/. 2.区别IE6,IE7,IE8,FF. 【区别符号】:「\9」、「*」、「_」. background:blue; /*Firefox 背景变蓝色*/.

OpenStack安装部署管理中常见问题解决方法

- - 企业架构 - ITeye博客
1.1、控制节点与网络控制器区别. OpenStack平台中有两种类型的物理节点,控制节点和计算节点. 控制节点包括网络控制、调度管理、api服务、存储卷管理、数据库管理、身份管理和镜像管理等,计算节点主要提供nova-compute服务. 控制节点的服务可以分开在多个节点,我们把提供nova-network服务的节点称为网络控制器.

Android屏幕大小适配问题解决

- - CSDN博客移动开发推荐文章
1、长度(真实长度):英寸、inch. 2、分辨率:density 每英寸像素数  dpi(密度). 4、dip的公式:px /dip=dpi/160  所以 dip 类似于英寸、长度(dp=dip,sp类似于dip)  dip=160*inch. 当dip一定时,dpi 越大,px就越大. 5、广义分辨率=长px*宽px.

hudson不能部署项目的问题解决

- - BlogJava_首页
     在搭建持续集成服务过程中,按网上操作一步步来的,但是就是无法热部署项目到Tomcat下. 后来经过努力调试总结了解决方法:.    hudson自动部署插件: http://hudson-ci.org/downloads/plugins/deploy/ . Hudson热部署的必须关键点:.

实践Oracle与DB2区别及问题解决

- - ITeye博客
项目进入开发阶段的时候,为了方便,一直使用Oracle数据库进行开发, 所以很多sql语句都是在oracle能正常创建的,后期由于项目中嵌入了IBM的产品及其他因素,所以不得不使用db2数据库,切换数据库过程中的区别还是有点大,如:创建表、视图、存储过程、Ibatis支持等等、、、,下面就总结一下我从Oracle数据库切换到DB2数据库碰到的一些问题及如何解决.