页面相应太慢容易导致响应超时,404,断开的管道等各种问题。
总结项目中几个接口优化的方案:

1.并行操作。

接口优化方案 - 图1
循环使用CompletableFuture多线程调用方法体,等待全部完成后进行接下来的操作,会将串行变并行运行,查询效率提高。
CompletableFuture.supplyAsync(() ->{
方法体
});

  1. List<CompletableFuture<WordResult>> futures = new ArrayList<>();
  2. for (int i = 0; i < firstElementlist.size(); i++) {
  3. String elementId = firstElementlist.get(i).getTempletKey();
  4. futures.add(CompletableFuture.supplyAsync(() -> {
  5. WordResult wordResulTOne = selectWordElement(taskNo, templetKeyId, elementId);
  6. return wordResulTOne;
  7. }));
  8. }
  9. //等待全部完成
  10. CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();

2.另起线程操作

对可以不用实时相应的数据查询,另起线程进行操作。
比如:批量提交、批量导入万级以上的数据等等。

  1. try {
  2. Thread thread = new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. //批量插入数据
  6. batchAdd();
  7. }
  8. });
  9. thread.start();
  10. } catch (Exception e) {
  11. log.info("批量提交失败!{}", e.getMessage());
  12. }

3.将单次操作改成批量操作

如果有循环100次,那么就会远程调用数据库100次,是非常耗时的操作。所以需要批量查一次,就能查出所有的结果,减少远程调用的次数。如果数据量很大,需要分批次操作数据库,建议每次查询量不超过500次。
1.将循环中单个查询改成批量查询
首先查出所有的数据list集合,在根据java8的stream流的方法对某个字段进行分组,转换成map的形式,之后对map进行操作。

  1. //批量查询出所有的数据
  2. List<InstitutionRiskCollection> scoreList = selectByScoreItemList(idList,taskNo);
  3. //将list转换成map的形式
  4. Map<String, List<InstitutionRiskCollection>> elementRisksMap = new HashMap<>();
  5. if (CollUtil.isNotEmpty(elementRisks)) {
  6. elementRisksMap = scoreList.stream().collect(Collectors.groupingBy(InstitutionRiskCollection::getElementId));
  7. }

mapper.java

  1. List<InstitutionRiskCollection> selectByScoreItemList(@Param("scoreItemKeyList") List<String> scoreItemKeyList, String taskNo);

mapper.xml

  1. <select id="selectByScoreItemListAndTaskNo" resultMap="BaseResultMap">
  2. select
  3. <include refid="Base_Column_List"/>
  4. from INSTITUTION_RISK_COLLECTION
  5. where
  6. TASK_NO = #{taskNo,jdbcType=VARCHAR}
  7. <if test="scoreItemKeyList != null and scoreItemKeyList.size > 0">
  8. AND
  9. SCORE_ITEM IN
  10. <foreach collection="scoreItemKeyList" index="index" item="index"
  11. open="(" separator="," close=")">
  12. #{index,jdbcType=VARCHAR}
  13. </foreach>
  14. </if>
  15. </select>

2.批量插入
mapper.java

  1. int batchAdd(List<InstitutionRiskResult> list);

mapper.xml

  1. <insert id="batchAdd" parameterType="java.util.List">
  2. INSERT INTO INSTITUTION_RISK_RESULT
  3. ( RISK_RESULT_KEY,INSTITUTION_RISK_KEY,TEMPLET_ID, RISK_SCORE, MEASURES_SCORE,
  4. RISK_LEVEL,MEASURES_LEVEL,SURPLUS_LEVELKEY,MESSAGE)
  5. VALUES
  6. <foreach collection="list" item="item" separator=",">
  7. (#{item.riskResultKey},#{item.institutionRiskKey},#{item.templetId},#{item.riskScore},#{item.measuresScore},
  8. #{item.riskLevel},#{item.measuresLevel},#{item.surplusLevelKey},#{item.message})
  9. </foreach>
  10. </insert>

3.批量更新
mapper.java

  1. int batchUpdate(@Param("riskScoreList") List<InherentRiskScore> riskScoreList);

mapper.xml

  1. <update id="batchUpdate">
  2. <foreach collection="riskScoreList" item="index" index="index" open="" close="" separator=";">
  3. UPDATE INHERENT_RISK_SCORE
  4. SET TOTAL_SCORE=#{index.totalScore,jdbcType=VARCHAR}
  5. WHERE INHERENT_SCORE_ID= #{index.inherentScoreId,jdbcType=VARCHAR}
  6. </foreach>
  7. </update>

ps:批量更新操作有两种方法,1.循环update语句2.case when
1.循环update效率要比case when效率高一些。如果数据量很大的话,还是建议分批更新

4.异步处理

比如有个用户请求接口中,需要做业务操作,发站内通知,和记录操作日志。为了实现起来比较方便,通常我们会将这些逻辑放在接口中同步执行,势必会对接口性能造成一定的影响。
接口优化方案 - 图2
这个接口表面上看起来没有问题,但如果你仔细梳理一下业务逻辑,会发现只有业务操作才是核心逻辑,其他的功能都是非核心逻辑。
ps:
在这里有个原则就是:核心逻辑可以同步执行,同步写库。非核心逻辑,可以异步执行,异步写库。