性能优化-常见方案
索引,并行,异步,分片,批量,集合转map,池化,预先分配
-
批量
为什么网络请求小包合并成大包会提高性能?主要原因有两个:
- 减少无谓的请求头,如果你每个请求只有几字节,而头却有几十字节,无疑效率非常低下。
- 减少回复的ack包个数。把请求合并后,ack包数量必然减少,确认和重发的成本就会降低。
参考:消息队列设计精要
-
降低锁的粒度
-
降低事务范围,可以使用编程式事务
非DB操作不放在事务中 -
非核心逻辑异步化
区分出核心、非核心逻辑,将核心逻辑同步处理,非核心逻辑进行异步化,主要有以下两种方式 1. 线程池异步化。如果失败采用延迟失败处理,失败一定次数可以发送消息到mq中处理,或者定时任务处理 2. mq -
都是核心业务
CF并行处理 -
自动分页查询
@Test public void test08() { ExecutorService executorService = Executors.newFixedThreadPool(10); // 每次查询条数 int pageSize = 100; // 页码 int pageNo = 1; List<TbUserInfo> tbUserInfos; while (true) { PageRequest pageRequest = new PageRequest(); pageRequest.setPageNo(pageNo); pageRequest.setPageSize(pageSize); tbUserInfos = this.userInfoMapper.queryPagePhysics(pageRequest); if (CollectionUtils.isEmpty(tbUserInfos)) { break; } // 业务处理 final List<TbUserInfo> finalTbUserInfos = tbUserInfos; executorService.execute(() -> { System.out.println("tbUserInfos = " + finalTbUserInfos); }); // 如果当前页查询的数据没拉满,则表示是最后一页 if (tbUserInfos.size() < pageSize) { break; } pageNo++; } }案例:
导出订单查询结果的所有商品信息
功能描述:
订单查询是调用es接口查询的,可以理解为是透传的查询功能,分页查询很好实现。 订单商品明细导出是导出符合条件查询的所有商品信息,这个不难导出,难点是如何优化导出大量数据时的阻塞问题 针对导出商品明细功能优化 使用分页配合CountDownLatch+线程池异步处理,优化查询结果慢的问题 步骤如下 1. 查询总条数,计算共多少页 2. 使用CompletableFuture配合CountDownLatch将每次查到的数据放在List中,最后再将总结果集导出伪代码如下
public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(100); // 总条数 int total = 100_0000; // 分页大小 int batchNum = 2000; // 总页数 int totalPage = total % batchNum == 0 ? total / batchNum : total / batchNum + 1; CountDownLatch countDownLatch = new CountDownLatch(totalPage); for (long i = 0; i < totalPage; i++) { long currPage = i; executorService.execute(() -> { // 执行分页查询 log.info(String.format("第%s页,每页%s条", currPage, batchNum)); countDownLatch.countDown(); }); } // 主线程等待子线程执行完成 countDownLatch.await(); System.out.println("执行完成"); } -
异步+分片批处理
public class PoolTest { private static ThreadPoolExecutor threadPoolExecutor; static { threadPoolExecutor = new ThreadPoolExecutor(10, 20, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(100), new CustomizableThreadFactory("某业务线程池"), new ThreadPoolExecutor.CallerRunsPolicy()); threadPoolExecutor.prestartAllCoreThreads(); threadPoolExecutor.allowCoreThreadTimeOut(true); } public static void main(String[] args) { List<Integer> nums = Arrays.asList(1, 2, 2, 3, 4, 5, 6); List<List<Integer>> partition = Lists.partition(nums, 5); partition.stream().map(innerList -> { return CompletableFuture.runAsync(() -> { System.out.println(innerList); }, threadPoolExecutor); }).collect(Collectors.toList()).forEach(CompletableFuture::join); } }
- 大数据导入、导出优化
导入导出都使用EasyExcel处理,因为它重写了POI的对excel的解析,使用更少的内存能读到更多的数据。
导出优化:
方案一:异步化处理,即主线程响应请求,子线程处理具体的任务,让导出的文件上传文件服务器里或者oss里,去另外一个页面查询
方案一:自动分页查询,将每次分页查询的结果写到excel中
导入优化:
分批次 + MyBatis批量插入 + MyBatis批量插入异步化处理
MySQL优化
- 慢查询与走不走索引没有必然关系,关键还要看扫描的行数rows,扫描行数越少那么效率肯定也会越高
-
串行转并行
使用CompletableFuture.allOf(a,b,c).join(); -
空间换时间:
针对for循环查询库,前置查询再通过toMap/groupingBy分组
需求:
将遍历查询改为一次查询转map 比如:现要查询指定班级1、2、3下所有的学生 班级表属性:班级id、班级名称 学生表属性:学生id,学生姓名、归属班级id实现步骤:
1. 先获取指定班级信息 List<Classes> classes 2. 获取所有指定班级中的学生 List<Student> stus 3. 对 List<Student> stus使用班级id进行toMap,得到各个班级下的所有的学生Map<String,List<Student>> students 4. 将students中分组的数据塞到A中 -
业务条件限制,比如分页查询参数要求必填某项条件、限制只能查询最近几个月的
-
业务上限制-->索引优化-->多线程并行调用-->分批次多线程并行调用
性能优化-如何爽玩多线程来开发本篇给读者带来切实可行的多线程代码套路,完整代码复制可用,流程图以及亮点详解,给你的项目增 - 掘金 (juejin.cn)
-
数据冗余
将业务中需要从不同接口查到的数据放在同一个地方,放缓存中后边需要这些数据直接从缓存中拿数据弊端:存在数据不一致性问题

浙公网安备 33010602011771号