如何优雅的解决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,查询效率也会很差)?

想要更多干货、技术猛料的孩子,快点拿起手机扫码关注我,我在这里等你哦~

林老师带你学编程https://wolzq.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章