接口性能优化-并发优化
1. 批量操作:高效处理大量数据
对数据List分页,处理后可以先保存到MQ, 插入主表记录,MQ消费保存明细记录和日志记录
List<List<Long>> allIds = Lists.partition(ids,200);查询数据时,可以组装为ids, 批量查询或删除.
2. 异步处理:解放接口响应
public UserInfo getUserInfo(Long id) throws InterruptedException, ExecutionException
{
final UserInfo userInfo = new UserInfo();
CompletableFuture userFuture = CompletableFuture.supplyAsync(() - >
{
getRemoteUserAndFill(id, userInfo);
return Boolean.TRUE;
}, executor);
CompletableFuture bonusFuture = CompletableFuture.supplyAsync(() - >
{
getRemoteBonusAndFill(id, userInfo);
return Boolean.TRUE;
}, executor);
CompletableFuture growthFuture = CompletableFuture.supplyAsync(() - >
{
getRemoteGrowthAndFill(id, userInfo);
return Boolean.TRUE;
}, executor);
CompletableFuture.allOf(userFuture, bonusFuture, growthFuture).join();
userFuture.get();
bonusFuture.get();
growthFuture.get();
return userInfo;
}
3. 缓存利用:用空间换时间
4. 预处理:提前做好准备工作
预取思想很容易理解,就是提前把要计算查询的数据,初始化到缓存。如果你在未来某个时间需要用到某个经过复杂计算的数据,才实时去计算的话,可能耗时比较大。
5. 池化资源:避免重复创建
6. 并行执行:充分利用多核优势
7. 索引优化:为数据检索插上翅膀
8. 避免大事务:保持事务的精简
10. 深分页优化:破解分页性能难题
12. 锁粒度控制:找到并发访问的平衡点
13. 数据压缩:减少网络传输的负担
14. 服务拆分:让接口更专注高效
第一种
第二种:
列表10000条,
先拆分为100个大小100的list,分批操作.
100个list循环提交线程池,调用接口,并返回结果.
不直接更新数据库, 每100个批次,保存到redis或者MQ. 异步保存明细或日志.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.poi.ss.formula.functions.T;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BatchCall {
private static final Logger logger = LoggerFactory.getLogger(BatchCall.class);
private static final int BATCH_SIZE = 8;
private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(BATCH_SIZE);
public static void main(String[] args) {
try {
// 模拟待处理数据;
List<T> dataList = new ArrayList<>(10000);
//guava的分页工具
//List<List<Long>> allIds = Lists.partition(ids,200);
/*循环*/
AtomicInteger batchIndex = new AtomicInteger(0);
while (batchIndex.get() * BATCH_SIZE < dataList.size()) {
int currentBatchIndex = batchIndex.getAndIncrement();
CountDownLatch latch = new CountDownLatch(BATCH_SIZE);
List<T> batch = getBatch(dataList, currentBatchIndex, BATCH_SIZE);
for (T data : batch) {
executorService.submit(() -> {
try {
processData(data, latch);
} catch (Exception e) {
logger.error("Error processing data: " + data, e);
// 考虑是否需要在出现异常时让latch减计数
latch.countDown();
}
});
}
latch.await(); // 等待所有任务完成
logger.info("Batch {} processed successfully.", currentBatchIndex + 1);
}
} catch (InterruptedException e) {
logger.error("Processing interrupted", e);
Thread.currentThread().interrupt();
} finally {
/*最好写在系统优雅停机时触发*/
shutdown(executorService);
}
}
// 提取批次数据的逻辑
private static List<T> getBatch(List<T> dataList, int batchIndex, int batchSize) {
return dataList.subList(batchIndex*batchSize,(batchIndex+1)*batchSize-1);
}
/**
* @param data
* @param latch
* @throws InterruptedException
* 调用第三方平台
*/
private static void processData(T data, CountDownLatch latch) throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
logger.info("Data processed: {}", data);
/*处理完一笔交易,就 -1 */
latch.countDown();
}
/**
* @param service
* 关闭线程池
*/
private static void shutdown(ScheduledExecutorService service) {
service.shutdown(); // 尝试优雅关闭线程池,等待线程处理完
try {
if (!service.awaitTermination(60, TimeUnit.SECONDS)) {
service.shutdownNow(); // 强制关闭线程池,不等待线程处理结果
}
} catch (InterruptedException e) {
service.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
本文链接:
/archives/1753542946810
版权声明:
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
刘同学的小站!
喜欢就支持一下吧