浅论数据库聚合:合理使用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(n²) 时间复杂度
对象转换开销: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.mzph.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. 事实表结构 定义:事实表是维度建模的核心,由外键(关联维度表)、度量值(可量化的业务指标)及退化维度(冗余的维度属性)组成。其本质是记录业务过程中的度量事件,例如电商订单金额、商品库存量等。 场景识别:适用于…