浅论数据库聚合:合理使用LambdaQueryWrapper和XML

news2025/3/13 22:04:42

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、数据库聚合替代内存计算(关键优化)
  • 二、批量处理优化
  • 四、区域特殊处理解耦
  • 五、防御性编程增强


前言

技术认知点:使用 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) -> {
    // ...构建po
    statsService.save(po); // 每次插入产生一次IO
});

// 优化后(批量插入)
List<bbbPO> batchList = new ArrayList<>(areaMap.size());
areaMap.forEach((areaId, areaList) -> {
    // ...构建po
    batchList.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.coloradmin.cn/o/2311415.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS 7.9 安装 ClickHouse 文档

1. 环境准备 确保系统为 CentOS 7.9&#xff0c;并已安装 Docker。如果未安装 Docker&#xff0c;请先安装 Docker。 安装 Docker # 卸载旧版本 Docker&#xff08;如果有&#xff09; sudo yum remove -y docker docker-client docker-client-latest docker-common docker-…

WPS条件格式:B列的值大于800,并且E列的值大于B列乘以0.4时,这一行的背景标红

一、选择数据区域 选中需要应用条件格式的区域&#xff08;例如A2:E100 &#xff09;。 二、打开条件格式 点击“开始”选项卡&#xff0c;选择“条件格式” > “新建规则”。 三、选择规则类型 选择“使用公式确定要设置格式的单元格”。 四、输入公式 在公式框中输入以…

MWC 2025 | 紫光展锐联合移远通信推出全面支持R16特性的5G模组RG620UA-EU

2025年世界移动通信大会&#xff08;MWC 2025&#xff09;期间&#xff0c;紫光展锐联合移远通信&#xff0c;正式发布了全面支持5G R16特性的模组RG620UA-EU&#xff0c;以强大的灵活性和便捷性赋能产业。 展锐芯加持&#xff0c;关键性能优异 RG620UA-EU模组基于紫光展锐V62…

AI-Ollama本地大语言模型运行框架与Ollama javascript接入

1.Ollama Ollama 是一个开源的大型语言模型&#xff08;LLM&#xff09;平台&#xff0c;旨在让用户能够轻松地在本地运行、管理和与大型语言模型进行交互。 Ollama 提供了一个简单的方式来加载和使用各种预训练的语言模型&#xff0c;支持文本生成、翻译、代码编写、问答等多种…

PROFINET转PROFIBUS从案例剖析网关模块的协议转换功能

一、 案例背景 在当下追求高效协同的工业自动化生产体系里&#xff0c;设备间的无缝互联互通堪称关键要素。某企业的生产车间中&#xff0c;有一台性能稳定的变频器&#xff0c;其配备的是PROFIBUS接口。与此同时&#xff0c;操控整个生产线的核心大脑——西门子1500 PLC&…

VEC系列-RabbitMQ 入门笔记

消息队列&#xff08;MQ&#xff09;对于开发者来说是一个经常听到的词汇&#xff0c;但在实际开发中&#xff0c;大多数人并不会真正用到它。网上已经有很多关于 MQ 概述和原理的详细讲解&#xff0c;官网文档和技术博客也都介绍得很深入&#xff0c;因此&#xff0c;我在这里…

第5章 使用OSSEC进行监控(网络安全防御实战--蓝军武器库)

网络安全防御实战--蓝军武器库是2020年出版的&#xff0c;已经过去3年时间了&#xff0c;最近利用闲暇时间&#xff0c;抓紧吸收&#xff0c;总的来说&#xff0c;第5章开始进入主机安全&#xff08;HIDS&#xff09;领域了&#xff0c;2022年的时候有幸做过终端安全一段时间&a…

安装IK分词器;IK分词器配置扩展词库:配置扩展字典-扩展词,配置扩展停止词字典-停用词

安装IK分词器&#xff1b;IK分词器配置扩展词库&#xff1a;配置扩展字典-扩展词&#xff0c;配置扩展停止词字典-停用词 安装IK分词器IK分词配置扩展词库配置扩展字典-扩展词配置停止词字典-停用词测试配置字典前配置字典后 本文 ElasticSearch 版本为&#xff1a;7.17.9&…

pgsql行列转换

目录 一、造测试数据 二、行转列 1.函数定义 2.语法 3.示例 三、列转行 1.函数定义 2.语法 3.示例 一、造测试数据 create table test ( id int, json1 varchar, json2 varchar );insert into test values(1,111,{111}); insert into test values(2,111,222,{111,22…

Nginx 开启Baise认证

开启Baise认证 需要再站点Server配置中添加一下配置&#xff0c;添加htpasswd文件 server{auth_basic "HTTP Basic Authentication";auth_basic_user_file /etc/nginx/htpasswd;# 其他配置信息... }如果你的 Linux 服务器没有安装 htpasswd 工具&#xff0c;可以通…

基于python实现的疫情数据可视化分析系统

基于python实现的疫情数据可视化分析系统 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat 系统功能实现 总体设计 系统实现 系统功能模块 系统首页可以查看首页、疫情信息、核酸检测、新闻资讯、个人中心、后…

计算机毕业设计SpringBoot+Vue.js陕西民俗网(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Win7重装不翻车!ISO镜像安全下载渠道+BIOS设置避雷手册

一、写在前面&#xff1a;为什么你需要这份教程&#xff1f; 当电脑频繁蓝屏、系统崩溃甚至无法开机时&#xff0c;重装系统可能是最后的救命稻草。但市面上的教程往往存在三大痛点&#xff1a; ⚠️ 镜像来源不明导致系统被植入后门 ⚠️ 启动盘制作失败反复折腾 ⚠️ 操作失…

[项目]基于FreeRTOS的STM32四轴飞行器: 四.LED控制

基于FreeRTOS的STM32四轴飞行器: 四.LED控制 一.配置Com层二.编写驱动 一.配置Com层 先在Com_Config.h中定义灯位置的枚举类型&#xff1a; 之后定义Led的结构体&#xff1a; 定义飞行器状态&#xff1a; 在Com_Config.c中初始化四个灯&#xff1a; 在Com_Config.h外部声明…

计算机毕业设计SpringBoot+Vue.js青年公寓服务平台(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

VScode 中文符号出现黄色方框的解决方法

VScode 中文符号出现黄色方框的解决方法 我的vscode的python多行注释中会将中文字符用黄色方框框处&#xff1a; 只需要打开设置搜索unicode&#xff0c;然后将这一项的勾选取消掉就可以了&#xff1a; 取消之后的效果如下&#xff1a; 另一种情况&#xff1a;中文显示出现黄色…

JCRQ1河马算法+四模型对比!HO-CNN-GRU-Attention系列四模型多变量时序预测

JCRQ1河马算法四模型对比&#xff01;HO-CNN-GRU-Attention系列四模型多变量时序预测 目录 JCRQ1河马算法四模型对比&#xff01;HO-CNN-GRU-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于HO-CNN-GRU-Attention、CNN-GRU-Attent…

探索低空经济,无人机及载人直升机低空应用技术详解

探索低空经济时&#xff0c;无人机及载人直升机低空应用技术是核心要素。以下是对这两类技术的详细解析&#xff1a; 一、无人机低空应用技术 1. 飞行控制技术 无人机需要强大的飞行控制系统&#xff0c;这涉及传感器融合、飞行器稳定性控制、自动化飞行和紧急情况下的自动避…

GStreamer —— 2.3、Windows下Qt加载GStreamer库后运行 - “教程3:动态管道“(附:完整源码)

运行效果&#xff08;音频&#xff09; 简介 上一个教程演示了GStreamer 概念。本教程中的管在它设置为 playing 状态之前完全构建。这没关系。如果 我们没有采取进一步的行动&#xff0c;数据会到达 pipeline 的 pipeline 和 pipeline 将生成错误消息并停止。但 我们将采取进一…

【Java数据结构】前K个高频单词

前K个高频单词 692. 前K个高频单词 - 力扣&#xff08;LeetCode&#xff09; 解决这个问题我们先得知道每个单词出现的次数&#xff0c;用map存储下来&#xff0c;然后将出现次数最多的通过建立小根堆解决top-K问题 &#xff0c;重点是top-K的求取。 1.建立map 首先我们可以…