专栏首页强仔仔如何优雅的解决n 1查询!!!

如何优雅的解决n 1查询!!!

我们在写代码的时候非常忌讳出现n+1次查询,这就意味的你的循环有多少次,就会查询多少次数据库,这是很恐怖的场景。

因为每次服务调用mysql查询的时候,都是一件很耗费性能的操作,下面我们举个例子,来说说n+1的触发场景及解决方案。

业务需求

需要查询指定用户的订单详细信息,详细信息不仅仅包含订单本身的信息,还包含其它信息。这个时候童鞋们往往会采用,如下所示的方式进行数据获取。

 /**
* 订单mapper
*/
private OrderMapper orderMapper;

/**
* 订单商品mapper
*/
private OrderFeeMapper orderFeeMapper;

/**
* 查询用户id指定的所有订单列表信息
* @param userId
* @return
*/
public List<OrderDetail> getOrderDetailList(int userId) {
    // 查询订单列表数据
    List<Order> orderList = orderMapper.getOrders(userId);
    List<OrderDetail> orderDetailList = new ArrayList<>();
    for (Order order : orderList) {
        OrderDetail orderDetail =new OrderDetail();
        // 查询订单对应费用信息
        OrderFee orderFee = orderFeeMapper.getOrderFeeDetail(order.getOrderId());
        orderDetail.setOrderFee(orderFee);
        // 添加到集合中
        orderDetailList.add(orderDetail);
        }
    return orderDetailList;
}

如果这个用户订单量少还好,一旦这个用户订单量超级大,这个操作的响应时间将会非常长,长到你无法忍受的地步,那我们要怎么进行优化呢?

n+1改为1+1模式

我们可以将n次查询的条件添加到一个集合中,然后通过in语句一次性查询出我们需要的数据,这样就可以避免n+1次查询的出现,可以大大提高我们的执行效率,代码如下所示:

/**
* 订单mapper
*/
private OrderMapper orderMapper;

/**
* 订单商品mapper
*/
private OrderFeeMapper orderFeeMapper;

/**
* 查询用户id指定的所有订单列表信息
* @param userId
* @return
*/
public List<OrderDetail> getOrderDetailList(int userId) {
    // 查询订单列表数据
    List<Order> orderList = orderMapper.getOrders(userId);
    List<OrderDetail> orderDetailList = new ArrayList<>();
    List<String> orderIdList = new ArrayList<>();
    for (Order order : orderList) {
        OrderDetail orderDetail =new OrderDetail();
        // 添加订单到集合中
        orderIdList.add(order.getOrderId());
        // 添加到集合中
        orderDetailList.add(orderDetail);
    }
    List<OrderFee> orderFeeList = orderFeeMapper.getOrderFeeList(orderIdList);
    // 递归将orderFeeList中费用信息设置到对应订单的orderDetail对象中(具体代码省略)
    setOrderDetail(orderFeeList,orderDetailList);
    return orderDetailList;
}

连接查询失效场景

童鞋们可能会问为啥不采用mysql连接查询,一下子将相关表数据一起查询出来。这边主要出于如下考虑:

笛卡儿积

连接查询其实就是笛卡尔积的应用,一张表的查询操作可能会很快,但是多张表联查就会非常慢,因为他们的数据量是n*m,所以有时候采用连接查询,还不如分成多次查询来的快。

分库分表

如果系统的数据库采用的是分库分表,这个时候有些表是不能够进行连接查询,我们只能分多次查询,然后组装到一起。

数据来源不一致

如果订单的数据是从第三方接口获取的,那我们自然没办法进行连表查询。

总结

我们写代码的时候一定要特别注意n+1查询出现,循环体内要多检查几遍,是否有子查询的出现。

后记

童鞋们要记住,每一种模式都存在一定的缺陷,数据量不一样,模式的执行效率天差地别。童鞋们有空的话可以思考如下问题:

  1. n+1模式修改为1+1模式需要注意哪些问题?
  2. mysql中in语句长度是否有限制(或者说sql长度是否有限制,如果有那是多少)?
  3. n+1中如果n的数值非常大,要如何优化(因为直接查询组装成in,查询效率也会很差)?

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何高效的处理第三方接口数据?

    很多公司业务都需要进行第三方接口的对接工作,特别是那种大部分数据都来自第三方的项目。比如亚马逊商家服务的saas系统,基本上所有的数据都来自亚马逊平台。

    林老师带你学编程
  • 第三方接口超大数据获取方案(下载-解析-入库模式)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    林老师带你学编程
  • Redis和Mongodb应用场景研究

    现在的分布式项目基本都会用到redis和mongodb,可是redis和mongdb到底有什么不同呢,今天我就基于我们公司的项目来具体介绍一下redis和mon...

    林老师带你学编程
  • 【微服务】160:Elasticsearch高级使用

    昨天学了match匹配和term匹配,这是两种最基础也很重要的查询方式,使用起来也简单。

    刘小爱
  • 技术人的人情债,开发就是这么坑!

    IT故事会
  • 数据集成中间件知识点总结

      数据集成是把不同来源、格式、特点性质的数据在逻辑上或物理上有机地集中,从而为企业提供全面的数据共享。

    用户3003813
  • Python脚本自动下载小说

            本人喜欢在网上看小说,一直使用的是小说下载阅读器,可以自动从网上下载想看的小说到本地,比较方便。最近在学习Python的爬虫,受此启发,突然就想...

    py3study
  • JSON基础

    本文整理自《JSON必知必会》一书,主要是章节的简介,把前三个章的内容简单叙述了,算是需要JSON的基础知识。以后有机会看情况再写一些Java的jsonobje...

    FunTester
  • 推荐一款功能强大的Tomcat 管理监控工具,可替代Tomcat Manager

    再点Manager App,即可进入Tomcat自带的Manager这个应用,此处可以单独部署/卸载每一个应用。可以看到在Manager应用里提供的功能是很有限...

    Jerry Wang
  • 运行耗时统计

    wfaceboss

扫码关注云+社区

领取腾讯云代金券