Tianyi's Blog Tianyi's Blog
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)

Tianyi

一直向前,永不停止
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)
  • Java

  • Golang

  • JVM的奇妙世界

  • Spring

  • Spring增强封装

  • Redis

  • MySQL

  • RabbitMQ

  • Kafka

  • 分享

    • Idea的基本使用
    • Gradle 实践操作指南及最佳实践
    • python环境
    • BadTasteCode && 优化
    • 优化实践
      • 高性能驾驶舱聚合实践
  • 后端
  • 分享
tianyi
2025-11-28
目录

优化实践

# 高性能驾驶舱聚合实践

  • 背景:/api/report/v1/homeDash/stat/list 需要同时返回安检、工单、抄表三大板块的集团级指标。原实现直接 selectList 出 20万~80万行,再在 Java Stream 里 groupingBy, 单次请求要 90s+。
  • 痛点:MyBatis 默认的 BaseMapper#selectList 只能按单条记录映射,导致海量数据跨进程搬运;复杂 SQL 写在注解里也难维护,更放大调优成本。

落地步骤

  1. 抽象聚合输出 三张统计表都最终映射为 HomeDashBizStatVo,所以直接在 Mapper 层暴露 aggregateForHomeDash(String statPeriodValue, String accessGrpCd, String deletedFlg),统一返回 VO,避免实体→VO 二次转换。
  2. SQL 下沉至 XML 把业务聚合写入 ncis-backend-report-service/src/main/resources/mybatis/yasdb/mapper/report/*.xml,示例:
   <select id="aggregateForHomeDash" resultType="com.saiit.ncis.report.module.report.vo.HomeDashBizStatVo">
       SELECT ACCESS_GRP_CD AS accessGrpCd,
              MAX(CIS_DIVISION) AS cisDivision,
              ROUND(
                  CASE WHEN COALESCE(SUM(INSPECTION_COUNT), 0) > 0
                       THEN COALESCE(SUM(COMPLETED_COUNT), 0) * 100.0 / COALESCE(SUM(INSPECTION_COUNT), 0)
                       ELSE 0 END, 2) AS inspectionRate,
              COALESCE(SUM(INSPECTION_COUNT), 0) AS inspectionCount,
              COALESCE(SUM(ABNORMAL_COUNT), 0) AS abnormalCount
       FROM BI_SC_STAT
       WHERE DELETED_FLG = #{deletedFlg}
         AND STAT_PERIOD_VALUE = #{statPeriodValue}
       <if test="accessGrpCd != null and accessGrpCd != ''">
           AND ACCESS_GRP_CD = #{accessGrpCd}
       </if>
       GROUP BY ACCESS_GRP_CD
   </select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 精简 WHERE:只保留一次 DELETED_FLG = ?,充分利用复合索引 (DELETED_FLG, STAT_PERIOD_VALUE, ACCESS_GRP_CD)。
  • 聚合逻辑由数据库完成,返回行数≈访问组数量,天然压缩 IO。
  1. Service 统一接口 在 IBi*StatService 增加 aggregateForHomeDash,实现类注入 ACTIVE_FLAG = "U" 后转调 Mapper。后续如需加缓存/多租户过滤,可在 Service 层集中处理。
  2. Processor 仅做排序 Sc/Order/MeterHomeDashStatProcessor 直接消费服务返回值,只做排序即可,不再 groupingBy or sumInt。代码更薄,更符合“单方法 <30 行”的规范。

效果与收益

  • 性能:接口耗时从 90s → 2s,主要得益于数据量级从 20 万+ 行降到几十行。
  • 资源:CPU/内存占用显著下降,减少 GC 抖动;数据库只需做一次聚合扫描,配合索引走范围查询。
  • 维护:复杂 SQL 统一放在 XML,参数检查、字段别名一目了然,DBA 可以直接复用 SQL 做 Explain 或加 hint。

最佳实践 Checklist

  1. 认清数据粒度:REST 返回的粒度若已是“访问组”,就不应该在应用层拿“明细户”再聚合。
  2. 优先让数据库干数据库的活:SUM/ROUND/GROUP BY 属于典型 OLAP 场景,交给 SQL 引擎天然更快。
  3. SQL 同步沉淀:复杂语句放 XML,结合 写动态条件;Java 保持接口签名即可。
  4. 守护索引策略:复合索引字段顺序要匹配 WHERE,必要时追加 GROUP BY 列提高覆盖率。
  5. 接口分层单一职责:Processor 只负责业务编排/排序,Service 负责数据准备,Mapper 负责 SQL ——层层清晰,后续扩展(并行查询 / 缓存)也更简单。

按这个套路,后续任何“驾驶舱类”接口都能快速复制:先确定最终粒度,再写一次聚合 SQL,下游组件天然受益。

完善页面 (opens new window)
BadTasteCode && 优化

← BadTasteCode && 优化

最近更新
01
小工具提示词
12-13
02
【IDEA神器】Show Comment 插件配置指南:如何优雅地屏蔽公共类注释?
12-12
03
MySQL 优化思路
06-12
更多文章>
Theme by Vdoing | Copyright © 2021-2025 Tandy | 粤ICP备2023113440号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式