我們在寫代碼的時候非常忌諱出現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查詢出現,循環體內要多檢查幾遍,是否有子查詢的出現。
後記
童鞋們要記住,每一種模式都存在一定的缺陷,數據量不一樣,模式的執行效率天差地別。童鞋們有空的話可以思考如下問題:
- n+1模式修改爲1+1模式需要注意哪些問題?
- mysql中in語句長度是否有限制(或者說sql長度是否有限制,如果有那是多少)?
- n+1中如果n的數值非常大,要如何優化(因爲直接查詢組裝成in,查詢效率也會很差)?
想要更多幹貨、技術猛料的孩子,快點拿起手機掃碼關注我,我在這裏等你哦~
林老師帶你學編程:https://wolzq.com