淺論數據庫聚合:合理使用LambdaQueryWrapper和XML

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔

文章目錄

  • 前言
  • 一、數據庫聚合替代內存計算(關鍵優化)
  • 二、批量處理優化
  • 四、區域特殊處理解耦
  • 五、防御性編程增強


前言

技術認知點:使用 XML 編寫 SQL 聚合查詢并不會導致所有數據加載到內存,反而能 大幅減少內存占用并提升性能。

        LocalDateTime localDateTime = TimeUtilTool.startOfDay();LocalDateTime crossTime = LocalDateTime.now().minusDays(1);List<AAA> list = SERVICE1.list(new LambdaQueryWrapper<AAA>().between(AAA::GETTIME, localDateTime.minusDays(1), localDateTime));Map<String, List<AAA>> areaMap = list.stream().collect(Collectors.groupingBy(AAA::getAreaId));

一個對象占得內存很小,可能只有1kb;但是當一百萬條時,數據量就達到了接近1個G,如果這時候處理數據,極易出現OOM;
應用層計算的劣勢
GC壓力:大量臨時對象增加垃圾回收頻率
多次遍歷內存:stream().collect(groupingBy) 導致 O(n2) 時間復雜度
對象轉換開銷:MyBatis 將每條記錄轉換為 PO 對象消耗資源
全量數據加載:即使只需要統計值,仍需傳輸所有字段

所以要學習數據庫聚合


原始代碼分析

 @XxlJob("MethodDD")public void MethodDD(){LocalDateTime localDateTime = TimeUtilTool.startOfDay();LocalDateTime crossTime = LocalDateTime.now().minusDays(1);List<AAA> list = SERVICE1.list(new LambdaQueryWrapper<AAA>().between(AAA::GETTIME, localDateTime.minusDays(1), localDateTime));Map<String, List<AAA>> areaMap = list.stream().collect(Collectors.groupingBy(AAA::getAreaId));List<BBB> result = SAVEDATA(areaMap, crossTime);saveAreaStatisticsDaily(result, crossTime);}private List<BBB> SAVEDATA(Map<String, List<AAA>> areaMap, LocalDateTime crossTime) {List<CCCC> ccc = cacheTool.areaDictionary();List<BBB> result = new ArrayList<>();areaMap.forEach((areaId, areaList)->{BBB po = new BBB();Optional<CCCC> first = ccc.stream().filter(ccc -> ccc.getId().toString().equals(areaId)).findFirst();first.ifPresent(ccc -> {po.setAreaId(areaId);if(ccc.getId().toString().equals(areaId)){po.setAreaName(AreaNameBuilder.getAreaName(ccc));}Double carSpeed = 0.0;if (areaList == null || areaList.isEmpty()) {// 處理空列表的情況carSpeed = 0.0;} else {double totalSpeed = areaList.parallelStream()  .mapToDouble(AAA::getCarSpeed).sum();carSpeed = totalSpeed / areaList.size();}po.setMeanSpeed(new BigDecimal(carSpeed));po.setFlow(areaList.size());Map<String, List<AAA>> carTypeMap = areaList.stream().collect(Collectors.groupingBy(AAA::getCarType));carTypeMap.forEach((carType, carTypeList) ->{if (carType.equals("1")){po.setSmallCCCARFlow(carTypeList.size());} else if (carType.equals("2")){po.setMediumLargeBBBULLFlow(carTypeList.size());} else if (carType.equals("3")){po.setSmallMediumttttFlow(carTypeList.size());}else if (carType.equals("4")){po.setLargettttFlow(carTypeList.size());}else if (carType.equals("5")){po.setHazardousChemicalCCCARFlow(carTypeList.size());}else if (carType.equals("6")){po.setMotorcycle(carTypeList.size());}else if (carType.equals("7")){po.setOther(carTypeList.size());}});});po.setCrossTime(crossTime);result.add(po);statsService.save(po);});List<String> areaIds = areaMap.keySet().stream().toList();for (CCCC ccc : ccc) {if (!areaIds.contains(ccc.getId().toString())){BBB po = new BBB();po.setAreaId(ccc.getId().toString());po.setAreaName(AreaNameBuilder.getAreaName(ccc));po.setCrossTime(crossTime);result.add(po);statsService.save(po);}}return result;}

首先,用戶有一個定時任務,每天凌晨統計卡口數據,并將結果保存到數據庫。當前代碼可能存在性能問題,尤其是當數據量大的時候,全量查詢和處理會導致內存和性能問題。

  1. 全量數據加載到內存:使用trafficCCCARService.list查詢所有符合條件的數據,如果數據量很大,會導致內存壓力,甚至OOM。
  2. 多次遍歷數據流:在處理每個區域的數據時,多次使用流操作進行分組和統計,可能導致性能下降。
  3. 頻繁的數據庫寫入操作:在SAVEDATA方法中,每次處理一個區域就調用statsService.save(po),這樣頻繁的數據庫插入操作效率低下。
  4. 硬編碼的區域ID判斷:在saveAreaStatisticsDaily方法中,直接判斷特定的區域ID,這樣的代碼難以維護,且不符合面向對象的設計原則。

首先,全量數據的問題,可以考慮分頁查詢或者使用數據庫的聚合功能,減少數據傳輸量。
其次,多次遍歷數據流可以通過合并處理邏輯來減少遍歷次數。
數據庫寫入操作應該批量進行,而不是逐條插入。
硬編碼的問題可以通過枚舉或配置來解決:代碼中存在重復的區域ID判斷,這部分應該抽象出來,使用更靈活的方式處理,比如使用Map來映射區域ID和對應的字段,避免大量的if-else語句。

一、數據庫聚合替代內存計算(關鍵優化)

LambdaQueryWrapper和XML

  1. XML 只是定義 SQL 的方式:無論是 XML 還是 LambdaQueryWrapper,最終都會生成 SQL 發送到數據庫執行
  2. 性能差異的根源:在于 SQL 本身的執行效率 和 數據傳輸量,而非 XML/Lambda 的代碼形式

關鍵區別:

優化前(LambdaQueryWrapper):拉取全量原始數據到應用層 → 內存計算(危險!)
優化后(XML 聚合):在數據庫層完成聚合 → 只返回計算結果(安全高效)

這時候要在數據庫層面進行處理了;

// 新增 DAO 方法
@Select("SELECT area_id, " +"COUNT(*) AS flow, " +"AVG(car_speed) AS mean_speed, " +"SUM(CASE car_type WHEN '1' THEN 1 ELSE 0 END) AS small_CCCAR_flow, " +"SUM(CASE car_type WHEN '2' THEN 1 ELSE 0 END) AS medium_large_BBBULL_flow " +// 其他車型..."FROM holo_CCCAR_feature_radar " +"WHERE cross_time BETWEEN #{start} AND #{end} " +"GROUP BY area_id")
List<AreaStatDTO> getAreaStats(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end);// 優化后入口方法
@XxlJob("MethodDD")
public void MethodDD() {LocalDateTime end = LocalDateTime.now().truncatedTo(ChronoUnit.DAYS);LocalDateTime start = end.minusDays(1);// 1. 數據庫聚合計算List<AreaStatDTO> stats = CCCARRecordDAO.getAreaStats(start, end);// 2. 構建統計對象List<bbbPO> statsList = buildStatistics(stats, start);// 3. 批量存儲statsService.saveBatch(statsList);// 4. 區域級統計saveAreaStatisticsDaily(statsList, start);
}

優化效果
數據量減少:假設原始數據10萬條 → 聚合后100條區域數據

執行時間:從1200ms → 200ms

內存消耗:從800MB → 10MB

二、批量處理優化

  1. 批量插入代替逐條插入
// 原代碼(逐條插入)
areaMap.forEach((areaId, areaList) -> {// ...構建postatsService.save(po); // 每次插入產生一次IO
});// 優化后(批量插入)
List<bbbPO> batchList = new ArrayList<>(areaMap.size());
areaMap.forEach((areaId, areaList) -> {// ...構建pobatchList.add(po);
});
statsService.saveBatch(batchList); // 一次批量插入
  1. 消除冗余流操作
// 原代碼(兩次遍歷)
Map<String, List<AAA>> areaMap = list.stream().collect(groupingBy(...));
areaMap.forEach(...);// 優化后(合并處理)
list.stream().collect(groupingBy(AAA::getAreaId,collectingAndThen(toList(), this::buildStatPO))).values().forEach(...);

四、區域特殊處理解耦

  1. 定義區域配置策略
public enum SpecialArea {TUNNEL_1669("1669", "rightOfCrossTunnel"),TUNNEL_1670("1670", "leftOfCrossTunnel");private final String areaId;private final String fieldName;// 靜態映射表private static final Map<String, SpecialArea> ID_MAP = Arrays.stream(values()).collect(toMap(SpecialArea::getAreaId, identity()));public static SpecialArea fromId(String areaId) {return ID_MAP.get(areaId);}
}// 優化后的區域統計方法
private void saveAreaStatisticsDaily(List<bbbPO> stats, LocalDateTime time) {CCCCCPO dailyStat = new CCCCCPO();dailyStat.setCrossTime(time);stats.forEach(po -> {SpecialArea area = SpecialArea.fromId(po.getAreaId());if (area != null) {BeanUtils.setProperty(dailyStat, area.getFieldName(), po.getFlow());}});dailyStat.setFlow(stats.stream().mapToInt(bbbPO::getFlow).sum());SERVICE1.save(dailyStat);
}

五、防御性編程增強

  1. 空值安全處理
// 平均速度計算優化
BigDecimal meanSpeed = areaList.stream().map(AAA::getCarSpeed).filter(Objects::nonNull).collect(Collectors.collectingAndThen(Collectors.averagingDouble(Double::doubleValue),avg -> avg.isNaN() ? BigDecimal.ZERO : BigDecimal.valueOf(avg)));
  1. 并行流安全控制
// 明確指定自定義線程池
ForkJoinPool customPool = new ForkJoinPool(4);
try {customPool.submit(() -> areaList.parallelStream()// ...處理邏輯).get();
} finally {customPool.shutdown();
}

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/72592.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/72592.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/72592.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Ubuntu 22.04安裝NVIDIA A30顯卡驅動

一、安裝前準備 1.禁用Nouveau驅動 Ubuntu默認使用開源Nouveau驅動&#xff0c;需要手動禁用&#xff1a; vim /etc/modprobe.d/blacklist-nouveau.conf # 添加以下內容&#xff1a; blacklist nouveau options nouveau modeset0 # 更新內核并重啟&#xff1a; update-initr…

Docker Desktop 4.38 安裝與配置全流程指南(Windows平臺)

一、軟件定位與特性 Docker Desktop 是容器化應用開發與部署的一體化工具&#xff0c;支持在本地環境創建、管理和運行Docker容器。4.38版本新增GPU加速支持、WSL 2性能優化和Kubernetes 1.28集群管理功能&#xff0c;適用于微服務開發、CI/CD流水線搭建等場景。 二、安裝環境…

音視頻入門基礎:RTP專題(15)——FFmpeg源碼中,獲取RTP的視頻信息的實現

一、引言 通過FFmpeg命令可以獲取到SDP文件描述的RTP流的視頻壓縮編碼格式、色彩格式&#xff08;像素格式&#xff09;、分辨率、幀率信息&#xff1a; ffmpeg -protocol_whitelist "file,rtp,udp" -i XXX.sdp 本文以H.264為例講述FFmpeg到底是從哪個地方獲取到這…

深度學習---卷積神經網絡

一、卷積尺寸計算公式 二、池化 池化分為最大池化和平均池化 最常用的就是最大池化&#xff0c;可以認為最大池化不需要引入計算&#xff0c;而平均池化需要引出計算&#xff08;計算平均數&#xff09; 每種池化還分為Pooling和AdaptiveAvgPool Pooling(2)就是每2*2個格子…

netty中Future和ChannelHandler

netty中的Future&#xff0c;繼承自 jdk中的Future&#xff0c;&#xff0c; jdk中的Future&#xff0c;很垃圾&#xff0c;只能同步阻塞獲取結果&#xff0c;&#xff0c;&#xff0c; netty中的Future進行了升級&#xff0c;&#xff0c;可以addListener()異步獲取結果&…

java 初學知識點總結

自己總結著玩 1.基本框架 public class HelloWorld{ public static void main(String[] args){ }//類名用大寫字母開頭 } 2.輸入&#xff1a; (1)Scanner:可讀取各種類型&#xff0c;字符串相當于cin>>; Scanner anew Scanner(System.in); Scan…

質量屬性場景描述

為了精確描述軟件系統的質量屬性&#xff0c;通常采用質量屬性場景&#xff08;Quality Attribute Scenario&#xff09;作為描述質量屬性的手段。質量屬性場景是一個具體的質量屬性需求&#xff0c;使利益相關者與系統的交互的簡短陳述。 質量屬性場景是一種用于描述系統如何…

數據可攜帶權的多重價值與實踐思考

文章目錄 前言一、數據可攜帶權的提出與立法二、數據可攜帶權的多重價值1、推動數據要素市場化配置2、促進市場競爭與創新3、強化個人數據權益 三、數據可攜帶權的實踐挑戰1、數據安全與隱私保護面臨風險2、接口差異導致數據遷移成本高昂3、可攜帶的數據范圍尚存爭議 數據可攜帶…

藍橋每日打卡--分考場

#藍橋#JAVA#分考場 題目描述 n個人參加某項特殊考試。 為了公平&#xff0c;要求任何兩個認識的人不能分在同一個考場。 求是少需要分幾個考場才能滿足條件。 輸入描述 輸入格式&#xff1a; 第一行&#xff0c;一個整數n(1≤n≤100)&#xff0c;表示參加考試的人數。 …

RMAN備份bug-審計日志暴漲(select action from gv$session)

問題概述 /oracle 文件系統使用率過大&#xff0c;經過檢查是審計日志過大,/oracle 目錄 197G 審計日志占用70G&#xff0c;每6個小時產生大量審計日志&#xff0c;日志內容全是select action from gv$session &#xff0c;猜測可能跟備份有關&#xff0c; $>df -h /oracle…

在Blender中給SP分紋理組

在Blender中怎么分SP的紋理組/紋理集 其實紋理組就是材質 把同一組的材質分給同一組的模型 導入到sp里面自然就是同一個紋理組 把模型導入SP之后 就自動分好了

Nuxt:Nuxt3框架中onBeforeMount函數 和onBeforeRouteUpdate函數區別介紹 【超詳細!】

提示&#xff1a;在 Nuxt3 中&#xff0c;onBeforeMount 和 onBeforeRouteUpdate 是兩個不同場景下使用的鉤子函數&#xff0c;分別對應 Vue 組件生命周期 和 路由導航守衛。以下是它們的詳細解釋和對比&#xff1a; 文章目錄 一、onBeforeMount&#xff08;Vue 生命周期鉤子&a…

華為 Open Gauss 數據庫在 Spring Boot 中使用 Flyway

db-migration&#xff1a;Flyway、Liquibase 擴展支持達夢&#xff08;DM&#xff09;、南大通用&#xff08;GBase 8s&#xff09;、OpenGauss 等國產數據庫。部分數據庫直接支持 Flowable 工作流。 開源代碼倉庫 Github&#xff1a;https://github.com/mengweijin/db-migrat…

java 查找兩個集合的交集部分數據

利用了Java 8的Stream API&#xff0c;代碼簡潔且效率高 import java.util.stream.Collectors; import java.util.List; import java.util.HashSet; import java.util.Set;public class ListIntersection {public static List<Long> findIntersection(List<Long> …

雙足機器狗開發:Rider - Pi

雙足機器狗開發:Rider - Pi https://github.com/YahboomTechnology/Rider-Pi-Robot 項目介紹 Rider - Pi是一款為開發者、教育工作者和機器人愛好者設計的桌面雙輪腿式機器人,它基于樹莓派CM4核心模塊構建,具備多種先進功能和特點: 硬件特性 核心模塊:采用樹莓派CM4核…

Android12 添加開機鈴聲

系統默認是沒有播放開機鈴聲的功能&#xff0c;MTK有一套自己的開機鈴聲處理邏輯&#xff0c;代碼在/vendor/mediatek/proprietary/operator/frameworks/bootanimation/MtkBootanimation下&#xff0c;但是在10之后MTK就不在維護這部分代碼了。直接使用會有很多編譯報錯&#x…

3.6V-30V寬壓輸入降壓同步IC內置MOS,電流4A/5A/6A,可以滿足汽車應急電源,BMS電池,電池組USB口輸出等儲能應用

今天給大家介紹一下這三款產品&#xff0c;分別是CJ92340,輸入電壓4.5V-30V&#xff0c;輸出可調&#xff0c;電流負載能力可達4A&#xff0c;頻率350KHZ。CJ92350,輸入電壓3.6V-30V&#xff0c;輸出可調&#xff0c;頻率可調&#xff0c;帶載能力達5A。CJ92360,輸入電壓3.6V-3…

代碼隨想錄算法訓練營第35天 | 01背包問題二維、01背包問題一維、416. 分割等和子集

一、01背包問題二維 二維數組&#xff0c;一維為物品&#xff0c;二維為背包重量 import java.util.Scanner;public class Main{public static void main(String[] args){Scanner scanner new Scanner(System.in);int n scanner.nextInt();int bag scanner.nextInt();int[…

010---基于Verilog HDL的分頻器設計

文章目錄 摘要一、時序圖二、程序設計2.1 rtl2.2 tb 三、仿真分析四、實用性 摘要 文章為學習記錄。繪制時序圖&#xff0c;編碼。通過修改分頻值參數&#xff0c;實現一定范圍分頻值內的任意分頻器設計。 一、時序圖 二、程序設計 2.1 rtl module divider #(parameter D…

維度建模事實表技術基礎解析(以電商場景為例)

維度建模事實表技術基礎解析(以電商場景為例) 1. 事實表結構 定義:事實表是維度建模的核心,由外鍵(關聯維度表)、度量值(可量化的業務指標)及退化維度(冗余的維度屬性)組成。其本質是記錄業務過程中的度量事件,例如電商訂單金額、商品庫存量等。 場景識別:適用于…