6. 事件回调思想:拒绝阻塞等待。
如果你调用一个系统B
的接口,但是它处理业务逻辑,耗时需要10s
甚至更多。然后你是一直 阻塞等待,直到系统B的下游接口返回 ,再继续你的下一步操作吗?这样 显然不合理 。
我们参考 IO多路复用模型 。即我们不用阻塞等待系统B
的接口,而是先去做别的操作。等系统B
的接口处理完,通过事件回调通知,我们接口收到通知再进行对应的业务操作即可。
如果大家忘记了IO模型,可以复习一下我的文章:看一遍就理解:IO模型详解
7. 远程调用由串行改为并行
假设我们设计一个APP首页的接口,它需要查用户信息、需要查banner信息、需要查弹窗信息等等。如果是串行一个一个查,比如查用户信息200ms
,查banner信息100ms
、查弹窗信息50ms
,那一共就耗时350ms
了,如果还查其他信息,那耗时就更大了。
其实我们可以改为并行调用,即查用户信息、查banner信息、查弹窗信息,可以同时 并行发起 。
最后接口耗时将大大降低 。有些小伙伴说,不知道如何使用并行优化接口?
我之前写过一篇文章并行优化接口的文章,保姆级别的!大家可以看一下,看完会有用的:后端思维篇,手把手教你写一个并行调用模板
8. 锁粒度避免过粗
在高并发场景,为了防止 超卖等情况 ,我们经常需要 加锁来保护共享资源 。但是,如果加锁的粒度过粗,是很影响接口性能的。
什么是加锁粒度呢?
其实就是就是你要锁住的范围是多大。 比如你在家上卫生间,你只要锁住卫生间就可以了吧 ,不需要将整个家都锁起来不让家人进门吧,卫生间就是你的加锁粒度。
不管你是synchronized
加锁还是redis
分布式锁,只需要在共享临界资源加锁即可,不涉及共享资源的,就不必要加锁。这就好像你上卫生间,不用把整个家都锁住,锁住卫生间门就可以了。
比如,在业务代码中,有一个ArrayList
因为涉及到多线程操作,所以需要加锁操作,假设刚好又有一段比较耗时的操作(代码中的slowNotShare
方法)不涉及线程安全问题。 反例加锁,就是一锅端,全锁住 :
//不涉及共享资源的慢方法
private void slowNotShare() {
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
}
}
//错误的加锁方法
public int wrong() {
long beginTime = System.currentTimeMillis();
IntStream.rangeClosed(1, 10000).parallel().forEach(i -> {
//加锁粒度太粗了,slowNotShare其实不涉及共享资源
synchronized (this) {
slowNotShare();
data.add(i);
}
});
log.info("cosume time:{}", System.currentTimeMillis() - beginTime);
return data.size();
}
正例:
public int right() {
long beginTime = System.currentTimeMillis();
IntStream.rangeClosed(1, 10000).parallel().forEach(i -> {
slowNotShare();//可以不加锁
//只对List这部分加锁
synchronized (data) {
data.add(i);
}
});
log.info("cosume time:{}", System.currentTimeMillis() - beginTime);
return data.size();
}
9. 切换存储方式:文件中转暂存数据
如果数据太大,落地数据库实在是慢的话, 就可以考虑先用文件的方式暂存 。先保存文件,再异步 下载文件,慢慢保存到数据库 。
这里可能会有点抽象,给大家分享一个,我之前的一个真实的优化案例吧。
之前开发了一个转账接口。如果是并发开启,10个并发度,每个批次
1000
笔转账明细数据,数据库插入会特别耗时, 大概6秒左右 ;这个跟我们公司的数据库同步机制有关,并发情况下,因为优先保证同步,所以并行的插入变成串行啦,就很耗时。
优化前 ,1000
笔明细转账数据,先落地DB
数据库,返回处理中给用户,再异步转账。如图:
记得当时压测的时候,高并发情况,这1000
笔明细入库,耗时都比较大。所以我转换了一下思路, 把批量的明细转账记录保存的文件服务器,然后记录一笔转账总记录到数据库即可 。接着异步再把明细下载下来,进行转账和明细入库。最后优化后,性能提升了 十几倍 。
优化后 ,流程图如下:
如果你的接口耗时瓶颈就 在数据库插入操作这里 ,用来批量操作等,还是效果还不理想,就可以考虑用文件或者MQ
等暂存。有时候批量数据放到文件,会比插入数据库效率更高。
10. 索引
提到接口优化,很多小伙伴都会想到 添加索引 。没错, 添加索引是成本最小的优化 ,而且一般优化效果都很不错。
索引优化这块的话,一般从这几个维度去思考:
- 你的SQL加索引了没?
- 你的索引是否真的生效?
- 你的索引建立是否合理?
10.1 SQL没加索引
我们开发的时候,容易疏忽而忘记给SQL添加索引。所以我们在写完SQL
的时候,就顺手查看一下 explain
执行计划。
explain select * from user_info where userId like '%123';
你也可以通过命令show create table
,整张表的索引情况。
show create table user_info;
如果某个表忘记添加某个索引,可以通过alter table add index
命令添加索引
alter table user_info add index idx_name (name);
一般就是:SQL
的where
条件的字段,或者是order by 、group by
后面的字段需需要添加索引。
10.2 索引不生效
有时候,即使你添加了索引,但是索引会失效的。 田螺哥整理了索引失效的常见原因 :
10.3 索引设计不合理
我们的索引不是越多越好,需要合理设计。比如:
- 删除冗余和重复索引。
- 索引一般不能超过
5
个 - 索引不适合建在有大量重复数据的字段上、如性别字段
- 适当使用覆盖索引
- 如果需要使用
force index
强制走某个索引,那就需要思考你的索引设计是否真的合理了
11. 优化SQL
处了索引优化,其实SQL还有很多其他有优化的空间。
-
接口优化
+关注
关注
0文章
4浏览量
1352
发布评论请先 登录
相关推荐
评论