当前位置:网站首页>聊聊接口性能优化的11个小技巧
聊聊接口性能优化的11个小技巧
2022-07-25 19:02:00 【InfoQ】
前言
1.索引
优化索引- 该sql语句加索引了没?
- 加的索引生效了没?
- mysql选错索引了没?
1.1 没加索引
whereorder byshow index from `order`;
show create table `order`;
ALTER TABLEALTER TABLE `order` ADD INDEX idx_name (name);
CREATE INDEXCREATE INDEX idx_name ON `order` (name);
DROP INDEXALTER TABLE `order` DROP INDEX idx_name;
DROP INDEXDROP INDEX idx_name ON `order`;
1.2 索引没生效
explainexplain select * from `order` where code='002';



1.3 选错索引
force index2. sql优化

3. 远程调用

3.1 并行调用

CallableCompleteFuturepublic 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.2 数据异构

4. 重复调用
重复调用4.1 循环查数据库
public List<User> queryUser(List<User> searchList) {
if (CollectionUtils.isEmpty(searchList)) {
return Collections.emptyList();
}
List<User> result = Lists.newArrayList();
searchList.forEach(user -> result.add(userMapper.getUserById(user.getId())));
return result;
}
public List<User> queryUser(List<User> searchList) {
if (CollectionUtils.isEmpty(searchList)) {
return Collections.emptyList();
}
List<Long> ids = searchList.stream().map(User::getId).collect(Collectors.toList());
return userMapper.getUserByIds(ids);
}
4.2 死循环
while(true) {
if(condition) {
break;
}
System.out.println("do samething");
}
CAS自旋锁4.3 无限递归
public void printCategory(Category category) {
if(category == null
|| category.getParentId() == null) {
return;
}
System.out.println("父分类名称:"+ category.getName());
Category parent = categoryMapper.getCategoryById(category.getParentId());
printCategory(parent);
}
5. 异步处理

核心逻辑非核心逻辑多线程mq5.1 线程池
线程池
5.2 mq
mq
6. 避免大事务
@Transactional
- 少用@Transactional注解
- 将查询(select)方法放到事务外
- 事务中避免远程调用
- 事务中避免一次性处理太多数据
- 有些功能可以非事务执行
- 有些功能可以异步处理
7. 锁粒度
加锁7.1 synchronized
synchronized在方法上加锁在代码块上加锁public synchronized doSave(String fileUrl) {
mkdir();
uploadFile(fileUrl);
sendMessage(fileUrl);
}
public void doSave(String path,String fileUrl) {
synchronized(this) {
if(!exists(path)) {
mkdir(path);
}
}
uploadFile(fileUrl);
sendMessage(fileUrl);
}
分布式锁7.2 redis分布式锁
public void doSave(String path,String fileUrl) {
try {
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
if(!exists(path)) {
mkdir(path);
uploadFile(fileUrl);
sendMessage(fileUrl);
}
return true;
}
} finally{
unlock(lockKey,requestId);
}
return false;
}
synchronizedpublic void doSave(String path,String fileUrl) {
if(this.tryLock()) {
mkdir(path);
}
uploadFile(fileUrl);
sendMessage(fileUrl);
}
private boolean tryLock() {
try {
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true;
}
} finally{
unlock(lockKey,requestId);
}
return false;
}
7.3 数据库分布式锁
- 表锁:加锁快,不会出现死锁。但锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 行锁:加锁慢,会出现死锁。但锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
- 间隙锁:开销和加锁时间界于表锁和行锁之间。它会出现死锁,锁定粒度界于表锁和行锁之间,并发度一般。
行锁间隙锁表锁8.分页处理
List<User> users = remoteCallUser(ids);
分页处理同步调用异步调用8.1 同步调用
jobList<List<Long>> allIds = Lists.partition(ids,200);
for(List<Long> batchIds:allIds) {
List<User> users = remoteCallUser(batchIds);
}
googleguavaLists.partition8.2 异步调用
某个接口异步调用List<List<Long>> allIds = Lists.partition(ids,200);
final List<User> result = Lists.newArrayList();
allIds.stream().forEach((batchIds) -> {
CompletableFuture.supplyAsync(() -> {
result.addAll(remoteCallUser(batchIds));
return Boolean.TRUE;
}, executor);
})
9.加缓存
加缓存9.1 redis缓存
redismemcachedString json = jedis.get(key);
if(StringUtils.isNotEmpty(json)) {
CategoryTree categoryTree = JsonUtil.toObject(json);
return categoryTree;
}
return queryCategoryTreeFromDb();

9.2 二级缓存
二级缓存caffeine<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.0</version>
</dependency>
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(){
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
//Caffeine配置
Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
//最后一次写入后经过固定时间过期
.expireAfterWrite(10, TimeUnit.SECONDS)
//缓存的最大条数
.maximumSize(1000);
cacheManager.setCaffeine(caffeine);
return cacheManager;
}
}
@Service
public class CategoryService {
@Cacheable(value = "category", key = "#categoryKey")
public CategoryModel getCategory(String categoryKey) {
String json = jedis.get(categoryKey);
if(StringUtils.isNotEmpty(json)) {
CategoryTree categoryTree = JsonUtil.toObject(json);
return categoryTree;
}
return queryCategoryTreeFromDb();
}
}

10. 分库分表
分库分表
根据id取模,比如:id=7,有4张表,则7%4=3,模为3,路由到用户表3。
给id指定一个区间范围,比如:id的值是0-10万,则数据存在用户表0,id的值是10-20万,则数据存在用户表1。
一致性hash算法
垂直水平分库:是为了解决数据库连接资源不足问题,和磁盘IO的性能瓶颈问题。
分表:是为了解决单表数据量太大,sql语句查询数据时,即使走了索引也非常耗时问题。此外还可以解决消耗cpu资源问题。
分库分表:可以解决 数据库连接资源不足、磁盘IO的性能瓶颈、检索数据耗时 和 消耗cpu资源等问题。
11. 辅助功能
11.1 开启慢查询日志
slow_query_log慢查询开关
slow_query_log_file慢查询日志存放的路径
long_query_time超过多少秒才会记录日志
setset global slow_query_log='ON';
set global slow_query_log_file='/usr/local/mysql/data/slow.log';
set global long_query_time=2;
my.cnf[mysqld]
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 2
11.2 加监控
监控Prometheus监控预警
- 接口响应时间
- 调用第三方服务耗时
- 慢查询sql耗时
- cpu使用情况
- 内存使用情况
- 磁盘使用情况
- 数据库使用情况

11.3 链路跟踪
skywalking

traceId边栏推荐
- Interface automation test platform fasterrunner series (I) - introduction, installation and deployment, startup service, access address, configuration supplement
- 什么是hpaPaaS平台?
- qt之编译成功但程序无法运行
- 如何创建一个有效的帮助文档?
- 给生活加点惊喜,做创意生活的原型设计师丨编程挑战赛 x 选手分享
- App test point (mind map)
- Go code checking tool
- Basic mode of music theory
- 一个免费的镜像下载仓库网站
- Interface automation test platform fasterrunner series (III) - operation examples
猜你喜欢

微软Azure和易观分析联合发布《企业级云原生平台驱动数字化转型》报告

C 调的满级和玄

果链“围城”:傍上苹果,是一场甜蜜与苦楚交错的旅途

阿里云技术专家秦隆:可靠性保障必备——云上如何进行混沌工程?

There are several browser cores. How to upgrade if the browser version is too low

A brief history from object detection to image segmentation

怎样设计产品帮助中心?以下几点不可忽视

MySQL子查询篇(精选20道子查询练习题)

给生活加点惊喜,做创意生活的原型设计师丨编程挑战赛 x 选手分享

How to change the chords after the tune of the song is changed
随机推荐
Pixel2Mesh从单个RGB图像生成三维网格ECCV2018
接口自动化测试平台FasterRunner系列(二)- 功能模块
【小程序开发】宿主环境详解
JS 基本类型 引用类型 深/浅克隆复制
Yarn 安装与使用教程[通俗易懂]
这种动态规划你见过吗——状态机动态规划之股票问题(上)
Share six practical applet plug-ins
Pymoo学习 (6):终止条件
Pyqt5 click qtableview vertical header to get row data and click cell to get row data
#yyds干货盘点# 面试必刷TOP101:反转链表
给生活加点惊喜,做创意生活的原型设计师丨编程挑战赛 x 选手分享
怎么禁止使用360浏览器(怎么才能把自带的浏览器停用)
从目标检测到图像分割简要发展史
qt之编译成功但程序无法运行
The understanding of domain adaptation in transfer learning and the introduction of three technologies
SQL Server 2019 安装教程
【开源工程】STM32C8T6+ADC信号采集+OLED波形显示
进程通信(SystemV通信方式:共享内存,消息队列,信号量)
Korean AI team plagiarizes shock academia! One tutor with 51 students, or plagiarism recidivist
The difference between PHP equal to = = and identity equal to = = =