如何優雅的解決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

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