ORM的1+N查询问题及解决办法

本文介绍了数据库查询的1+N问题,该问题会给数据库带来很大压力。以Article关联Category为例,生成列表页时先查文章列表,再逐条获取分类信息,会产生多次查询。同时给出了多种优化方案,如查询时join子表、分步优化等,并分析了各方案优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天小熙学习资料,看到了一个有意思的问题,数据库查询的1+N问题。查了下资料,汇总下前人的经验,分享下。

一. 1+N问题

  1. 场景:

    基本场景 : Article(文章)关联Category(分类)

     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 %} 
    
  2. 过程:

    (1)在生成列表页面时,首先执行一次 (页面获取文章列表)

     select * from article limited 0,N 
    

    (2)然后逐条获取 category.name,又需要执行N次 (在文章类获取到后,再次获取文章下一级的分类)

     select name from category where id = category_id 
    
  3. 结论

    所以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. 查询的时候join子表。(不推荐)

    (1). 优点:一次查询.

    (2). 缺点: 做分页查询的时候不准,如果关联子表多,笛卡尔积会非常大,消耗资源,高并发可能有死锁问题

  2. 分步优化

    (1). 描述:先发起主表的1次查询,把主表的ID都收集起来, 再发起一次性查询把所有子表的数据抓取出来。

    (2). 优点: 把1+N查询变成了1+1查询,避免了系统的死锁,提高了并发响应能力

    (3). 缺点: 第二次查询的结果需要人工处理拼装到对象里,没有了工具的自动化辅助,会花费更多的cpu时间。

  3. 查询的时候不join子表, 另外发起select去抓取子表数据

    (1)缺点: 1+N查询问题, 发起的sql数量会非常恐怖

  4. 利用懒加载延缓抓取的时机

    (1). 优点: 一开始只有1次主表查询,不会立即产生1+N

    (2). 缺点:但是立马在循环里get被懒加载的属性, 那么和第四种方案没有什么区别,这种方法在Hibenrate里有一个变种,就是通过设置batch fetch size来减少N次查询的次数(感兴趣的话可以点击详解描述链接看看)。

小熙借鉴了下大佬的问题描述鲁塔弗原创文章,自己替换了下解决的方法,有兴趣可以看看其他的。欢迎联系,辩论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值