记录一次接口优化的过程。接口响应时间从500s下降到5s。

记录一次接口优化的过程。接口响应时间从500s下降到5s。

接口说明:

该接口通过用户导入的一年内每天的厂区用电功率数据来计算用户安装储能设备后的收益情况。

用电功率数据具体为每15分钟一条,一年约有 12*30*24*4 = 34560 条。

代码循环情况为:一层循环(根据经验,储能设备1-35台) 二层循环(月份,12) 三层循环(天,大约30)四层循环(根据业务模型,一天分为18个时间段)共计需要循环:35*12*30*18 = 226,800 次。

业务模型,一天分为18个时间段:

步骤:

一、分析代码,列出怀疑的耗时代码,对该段代码进行计时

        1、怀疑点A  这段代码在主代码流程里(不参与循环),getDayPowerInfo方法就是一次性读取整年用电功率数据的方法,返回按月分组的的二维数组,二维数组里共有 (约)34560 条数据。

TimeInterval interval = DateUtil.timer();
System.err.println("开始计时:"+interval.start());
Map<String, Double[][]> monthAndDayPowerInfo = getDayPowerInfo(req.getCompanyId());
System.err.println("getDayPowerInfo耗时:"+interval.intervalSecond());

         平均耗时:13秒

         2、怀疑点B 这段代码在二层循环里(按月循环)

 // 单天单台的理论收益值
Double Price_standard = provinceMonthIncomeService.calVoltageDayIncome(year + "", month + "",req.getVoltageId());
System.err.println("t_1 耗时:"+interval.intervalSecond());

            平均耗时:240ms (乘以循环次数 35台12月之后,耗时100.8秒

            3、怀疑点C 这段代码在二层循环里(按月循环)

// 用电波形
Map<Long, List<BasedataElecRuleDO>> ruleMap = elecRuleService.getVoltageYearMonthElecRule(Arrays.asList(voltageId), year + "", month + "");
System.err.println("t_2:"+interval.intervalSecond());

        平均耗时:70ms(乘以循环次数 35台12月之后,耗时29.4秒

        4、怀疑点D 这段代码在二层循环里(按月循环)

// 电价
Map<Long, List<BasedataElecPriceDO>> priceMap = elecPriceService.priceByVoltageIdList(year + "",month + "", Arrays.asList(voltageId));
System.err.println("t_3:"+interval.intervalSecond());

          平均耗时:40ms(乘以循环次数 35台12月之后,耗时16.8秒

          5、怀疑点E 这段代码在二层循环里(按月循环)

// 实际用电量(根据波形)Map<Integer, Double> elecTypeEnergyMap = getElecTypeEnergy(year + "", month + "", req.getVoltageId(),req.getCompanyId());
System.err.println("t_4:"+interval.intervalSecond());

          平均耗时:460ms(乘以循环次数 35台12月之后,耗时193.2秒

二、分析并进行优化

        1、怀疑点A,这段代码主要耗时在查询3万多的数据,该表目前50w数据

              原始SQL如下

SELECT* 
FROMcal_company_load_data 
WHEREcompany_id = 50 AND deleted = 0
ORDER BYload_date ASC

        优化方法:

               1)添加索引

                2)减少查询的字段

SELECTpower,load_date
FROMcal_company_load_data 
WHEREcompany_id = 50 AND deleted = 0
ORDER BYload_date ASC

        优化结果: 11s  ->   3s 

        目前仍旧不是很满意,如有高手,请帮忙指正。

        2、怀疑点B,这个代码主要耗时点为,2次查询SQL和1次查询外部接口

// 1. 获取年月电价
Map<Long, List<BasedataElecPriceDO>> voltagePriceMap =priceService.priceByVoltageIdList(year, month, Arrays.asList(voltageId));
List<BasedataElecPriceDO> priceList = voltagePriceMap.get(voltageId);// 2. 获取年月规则
Map<Long, List<BasedataElecRuleDO>> voltageRuleMap =ruleService.getVoltageYearMonthElecRule(Arrays.asList(voltageId), year, month);
List<BasedataElecRuleDO> ruleList = voltageRuleMap.get(voltageId);// 调用外部接口
HttpResponse response = HttpUtil.createPost(rankUrl.get(0).getName()).header("Content-Type", "application/json; charset=UTF-8").body(jsonParam).execute();

                优化方法:

                1)电价和规则(波形)的获取,可以提取到代码主流程里进行统一查询,然后整合成一个map,以月份为key,再把map传入该方法使用,这样可以避免在月份的循环里去查SQL

//在主代码流程里进行统一查询
TreeMap<String, List<BasedataElecRuleDO>> voltageByMonth =elecRuleService.getVoltageYearElecRule(voltageId, yearS + "");
TreeMap<String, List<BasedataElecPriceDO>> priceByMonth =elecPriceService.getVoltageYearElecPrice(yearS + "", voltageId);// 把整合的map传入该方法
Double Price_standard = provinceMonthIncomeService.calVoltageDayIncome(year + "", month + "",req.getVoltageId(),ruleMap,priceMap);// 在方法中直接从map里取,不用再查数据库
// 1. 获取年月电价
List<BasedataElecPriceDO> priceList = voltagePriceMap.get(voltageId);
// 2. 获取年月规则
List<BasedataElecRuleDO> ruleList = voltageRuleMap.get(voltageId);

                2) 查询外部接口暂时无法优化,耗时约50ms

                优化结果:460ms ->  80ms  (乘以循环次数 35台12月之后,耗时33.6秒)       

        3、怀疑C和怀疑点D,问题一样都是在二层循环里进行SQL查询

在对怀疑点B的优化中,其实我已经把对于C和D的查询放到里代码主流程里,然后整合成map在循环里使用,所以这里其实不用再优化了。

        红色为原代码,方法里去查SQL了,蓝色为优化后代码,从map里取数据。

        优化结果:40ms+70ms  -> 1ms+1ms  (乘以循环次数 35台12月之后,耗时<1秒)

        4、怀疑点E  该方法主要是通读取用户导入的负载数据(3万多条那个)来计算用户实际使用的电能。

        优化方法:

                1) 这段代码经过上下游业务分析,发现与该接口业务不是强相关,完全可以单独形成一个接口,前端可以同时调用这2个接口,以减少页面等待的总时间。

// 分离出一个接口
@PostMapping(value = "/calEnergyUsed")
@ApiOperation("根据负载功率曲线计算实际用电量")
public CommonResult<Map<Integer, Double>> calEnergyUsed(Long companyId,Long voltageId) {Map<Integer, Double>resp=companyLoadDataService.calEnergyUsed(companyId,voltageId);return success(resp);
}

         2) 该接口与怀疑点A一样,查询了3w条数据,所以也和A的优化方法一样,对SQL进行优化

        优化结果:

        460ms (注意这里是分月查询DB,乘以循环次数 35台12月之后,总耗时193.2秒)->  5秒(注意这里是一次查询全年数据)

目前优化总结:

目前5个怀疑点的总耗时由 13秒+100.8秒+29.4秒+16.8秒+193.2秒 = 360 秒 ,优化到

3s +33.6秒+<1秒+5秒(并行,忽略) =  37 秒  似乎还是无法接受

三、进一步优化

经过分析,发现怀疑点B,还有优化空间。

怀疑点B 这段代码在二层循环里(按月循环)

 // 单天单台的理论收益值
Double Price_standard = provinceMonthIncomeService.calVoltageDayIncome(year + "", month + "",req.getVoltageId());
System.err.println("t_1 耗时:"+interval.intervalSecond());

我们仔细观察B的代码,发现该B与一层循环没有数据上的关系,他只与二层循环(月份循环)有关。由于他比较耗时,也就是说,我们可以将此方法抽离出大的循环之外,以减少该方法的循环次数。简单计算一下:本来需要循环 35*12次,提取出来之后,只需循环12次。

// 在大循环之前,提前对每个月份的月理论收益值进行循环计算,整合成map,再传递到后面的循环里去使用
Map<Integer,Double> monthAndSaveTheory = new HashMap<>();
for (Map.Entry<String, Double[][]> stringEntry : monthAndDayPowerInfo.entrySet()) {String yearAndMonth = stringEntry.getKey();Integer year = Integer.parseInt(yearAndMonth.split("-")[0]);Integer month = Integer.parseInt(yearAndMonth.split("-")[1]);......// 单天单台的理论收益值Double Price_standard = provinceMonthIncomeService.calVoltageDayIncome(year+"", month + "",req.getVoltageId(),ruleMap,priceMap);monthAndSaveTheory.put(month, Price_standard);
}

优化结果:

33.6秒 ->  1秒

再次优化总结:

一次优化:3s +33.6秒+<1秒+5秒(并行,忽略) =  37 秒

二次优化:3s+1s+<1秒+5秒(并行,忽略) = 5秒

目前这个接口接口已经由500秒 优化到5秒 

四、再进一步优化

目前看来,最大的耗时为3w条数据的SQL查询时间(3s),待续........

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1419799.html

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

相关文章

Collection工具类

Collection工具类的介绍 Collection 是一个操作Set、List和Map等集合的工具类Collection中提供了一些列静态的方法对集合元素进行排序、查询和修改的等操作 Collection的排序操作&#xff08;均为Static方法&#xff09; 1&#xff0c;reverse&#xff08;List&#xff09;&…

uniapp管理后台编写,基于uniadmin和vue3实现uniapp小程序的管理后台

一&#xff0c;创建uniAdmin项目 打开开发者工具Hbuilder,然后点击左上角的文件&#xff0c;点新建&#xff0c;点项目。如下图。 选择uniadmin&#xff0c;编写项目名&#xff0c;然后使用vue3 记得选用阿里云服务器&#xff0c;因为最便宜 点击创建&#xff0c;等待项目创…

CRAY-1向量流水处理部分

向量流水线 可并行 数据独立操作类型不相同 可链接 操作数直接传入到下一条指令 这里我们复习一下&#xff0c;CRAY-1向量流水处理部分的相关知识点&#xff1a; CRAY-1启动访存、把元素送往功能部件及结果存入Vi都需要1拍的传送延迟。 CRAY-1访存流水线的建立需要6拍&#…

即插即用篇 | YOLOv8引入局部自注意力 HaloAttention | 为参数高效的视觉主干网络扩展局部自注意力

本改进已集成到 YOLOv8-Magic 框架。 我们提出了Axial Transformers,这是一个基于自注意力的自回归模型,用于图像和其他组织为高维张量的数据。现有的自回归模型要么因高维数据的计算资源需求过大而受到限制,要么为了减少资源需求而在分布表达性或实现的便捷性上做出妥协。相…

小程序获取手机号,用户昵称,头像

一、手机号 在微信小程序中&#xff0c;获取用户手机号也需要用户的明确授权。你可以使用 button 组件的 open-type 属性设置为 getPhoneNumber 来实现这个功能。当用户点击这个按钮时&#xff0c;会弹出一个对话框请求用户的授权。如果用户同意&#xff0c;你可以在 bindgetp…

string类常见题目详解(二) —— 仅仅反转字母、字符串中的第一个唯一字母、字符串最后一个单词的长度、验证回文串、字符串相加

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

第四百九十八回

文章目录 1. 概念介绍2. 使用方法2.1 固定样式2.2 自定义样式 3. 示例代码4. 内容总结 我们在上一章回中介绍了"GetMaterialApp组件"相关的内容&#xff0c;本章回中将介绍使用get显示SnackBar.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在介…

【C语言】/*操作符(下)*/

目录 一、操作符的分类 二、二进制和进制转换 2.1 进制 2.2 进制之间的转换 三、原码、反码、补码 四、单目操作符 五、逗号表达式 六、下标引用操作符[] 七、函数调用操作符() 八、结构体成员访问操作符 8.1 直接访问操作符(.) 8.2 间接访问操作符(->) 九、操作符…

跳跃游戏 II解题思路详解

题解 跳跃游戏 II &#x1f91a;我的博客&#x1f95b;前言 跳跃游戏 II描述示例提示 题解初步思考思路细节代码实现完整代码 END&#x1f4a0;END&#x1f3d5;️公众号 &#x1f91a;我的博客 欢迎光临我的博客&#xff1a;https://blog.csdn.net/qq_52434217?typeblog &a…

Leedcode题目:移除链表元素

题目&#xff1a; 这个题目就是要我们将我们的链表中的值是val的节点删除。 我们题目提供的接口是 传入了指向一个链表的第一个节点的指针&#xff0c;和我们要删除的元素的值val&#xff0c;不只要删除第一个&#xff0c; 思路 我们这里可以创建一个新的链表&#xff0c;…

鸿蒙开发接口Ability框架:【DataAbilityHelper模块(JS端SDK接口)】

DataAbilityHelper模块(JS端SDK接口) 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 本模块接口仅可在FA模型下使用。 使用说明 使用前根据具体情况引入如下模块 import featureAbility from …

万能自定义表单系统源码开源版 支持普通表单、付费报名、预约服务等三合一功能

源码简介 高效、灵活地收集和管理数据对于各项运营和决策至关重要&#xff0c;方便了各行业对数据收集的多样化需求。分享一个万能自定义表单系统源码开源&#xff0c;该系统拥有强大的自定义功能和广泛的适用性&#xff0c;支持普通表单、付费报名、预约服务等三合一功能。 …

心理应用工具包 psychtoolbox 绘制小球走迷宫

psychtoolbox 是 MATLAB 中的一个工具包&#xff0c;对于科研人员设计实验范式来说是不二之选&#xff0c;因为它可以操作计算机的底层硬件&#xff0c;精度可以达到帧的级别。 文章目录 一、实验目的二、psychtoolbox 的下载安装三、Psychtoolbox 的基本使用四、完整代码 一、…

VSCode:隐藏工程中的文件和目录

VSCode&#xff1a;设置搜索时的排除目录_vscode全局搜索排除掉某些目录-CSDN博客 介绍了如何排除搜索目录 有时也需要隐藏工程中不必关注的文件和目录。 假设工程中的文件结构如下 $ tree . ├── doc │ └── readme.txt ├── m.cpp └── user_guide 可以通过如下方…

[算法][差分数组][leetcode]1094. 拼车

地址&#xff1a; https://leetcode.cn/problems/car-pooling/description/ 解法一&#xff1a;暴力解法 class Solution {public boolean carPooling(int[][] trips, int capacity) {//特殊条件判断if(nulltrips||capacity<0){return false;}int [] d new int[1001];//暴…

词令蚂蚁庄园今日答案如何在微信小程序查看蚂蚁庄园今天问题的正确答案?

词令蚂蚁庄园今日答案如何在微信小程序查看蚂蚁庄园今天问题的正确答案&#xff1f; 1、打开微信&#xff0c;点击搜索框&#xff1b; 2、打开搜索页面&#xff0c;选择小程序搜索&#xff1b; 3、在搜索框&#xff0c;输入词令搜索点击进入词令微信小程序&#xff1b; 4、打开…

任务:单域,域树的搭建

一、单域&#xff1a; 搭建所需的系统&#xff1a;win2016 sever&#xff0c;win10 1.在创建域前&#xff0c;先设置静态ip 先查看win2016 sever的IP&#xff0c; ip&#xff1a;192.168.154.133 网关&#xff1a;192.168.154.2 DNS服务器&#xff1a;192.168.154.2 设置…

Offline: Overcoming Model Bias for Robust Offline Deep Reinforcement Learning

EAAI 2023 paper Intro model-free的离线强化学习由于价值函数估计问题存在训练的稳定性以及鲁棒性较低。本文提出基于模型的方法&#xff0c;同构构建稳定的动力学模型帮助策略的稳定训练。 method 本文基于模型的方法&#xff0c;所构造的转移模型输入状态动作&#xff0…

代码随想录算法训练营第36期DAY18

DAY18 二叉树的层序遍历 102二叉树的层序遍历 “队列先进先出&#xff0c;符合一层一层遍历的逻辑&#xff0c;而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。” 二叉树层序遍历模版&#xff1a; /** * Definition for a binary tree node. * struct TreeNode { *…

ASP.NET一个简单的媒体播放器的设计与实现

摘 要 本论文所描述的播放器是在Microsoft Visual Studio .NET 2003平台下利用Visual Basic.NET语言完成的。使用Visual Basic.NET提供的Windows Media Player控件以及文件处理&#xff0c;最终实现一款别致的&#xff0c;贴近用户操作习惯的媒体播放器。 该播放器实现了对WAV…