Skip to content

Commit 489fdf0

Browse files
committed
优化接口总结
1 parent e8cdebb commit 489fdf0

File tree

1 file changed

+226
-0
lines changed

1 file changed

+226
-0
lines changed
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
## 前言
2+
最近对外接口偶现504超时问题,真枪实弹搞了一次接口性能优化。在这里结合优化过程,总结了接口优化的八个要点,希望对大家有帮助呀~
3+
- 数据量比较大,批量操作数据入库
4+
- 耗时操作考虑异步处理
5+
- 恰当使用缓存
6+
- 优化程序逻辑、代码
7+
- SQL优化
8+
- 压缩传输内容
9+
- 考虑使用文件/MQ等其他方式暂存,异步再落地DB
10+
- 跟产品讨论需求最恰当,最舒服的实现方式
11+
12+
嘻嘻,先看一下我们对外转账接口的大概流程吧
13+
![](https://user-gold-cdn.xitu.io/2020/5/30/1726375c0d0162f3?w=1393&h=586&f=png&s=50348)
14+
15+
### 1. 数据量比较大,批量操作数据入库
16+
批量插入优化方法应该是我们最容易想到的啦~
17+
18+
**优化前**
19+
20+
```
21+
//for循环单笔入库
22+
for(TransDetail detail:list){
23+
insert(detail);
24+
}
25+
```
26+
**优化后**
27+
28+
```
29+
// 批量入库,mybatis demo实现
30+
<insert id="insertBatch" parameterType="java.util.List">
31+
insert into trans_detail( id,amount,payer,payee) values
32+
<foreach collection="list" item="item" index="index" separator=",">(
33+
#{item.id}, #{item.amount},
34+
#{item.payer},#{item.payee}
35+
)
36+
</foreach>
37+
</insert>
38+
```
39+
**性能对比**
40+
| 单位(ms) | for循环单笔入库 | 批量入库 |
41+
|-----|-----|------|
42+
| 500条 | 611 | 449 |
43+
| 1000条 | 1420 | 1128 |
44+
45+
**解析**
46+
- 批量插入性能更好,更加省时间,为什么呢?
47+
48+
```
49+
打个比喻:假如你需要搬一万块砖到楼顶,你有一个电梯,电梯一次可以放适量的砖(最多放500),
50+
你可以选择一次运送一块砖,也可以一次运送500,你觉得哪种方式发方便,时间消耗小?
51+
```
52+
53+
54+
### 2.耗时操作考虑异步处理
55+
56+
**优化前:**
57+
58+
59+
60+
61+
**优化后:**
62+
63+
![](https://user-gold-cdn.xitu.io/2020/5/30/172636b9f5238802?w=999&h=644&f=png&s=67707)
64+
65+
**性能对比:**
66+
67+
假设一个联行号匹配6ms
68+
| |同步| 异步|
69+
|-----|-----|------|
70+
| 500条 | 3000ms | 0 |
71+
| 1000条| 6000ms | 0|
72+
73+
74+
**解析:**
75+
- 因为联行号匹配比较耗时,放在异步处理的话,可以同步联机返回可以省掉这部分时间,大大提升接口性能,并且不会影响到转账主流程功能。
76+
- 除了这个例子,平时我们类似功能,如用户注册成功后,短信邮件通知,也是可以异步处理的,这个优化点香饽饽的~
77+
- 所以,太耗时的操作,在不影响主流程功能的情况下,可以考虑开子线程异步处理的啦。
78+
79+
### 3.恰当使用缓存
80+
在适当的业务场景,恰当地使用缓存,是可以大大提高接口性能的。这里的缓存包括:Redis,JVM本地缓存,memcached,或者Map等。
81+
82+
这次转账接口,使用到缓存啦,举个简单例子吧~
83+
84+
**优化前:**
85+
86+
87+
![](https://user-gold-cdn.xitu.io/2020/5/30/17263c548352f480?w=396&h=719&f=png&s=36528)
88+
89+
**优化后:**
90+
91+
92+
![](https://user-gold-cdn.xitu.io/2020/5/30/17263cc73ccd1e76?w=723&h=785&f=png&s=70764)
93+
94+
**解析:**
95+
- 把热点数据放到缓存,不用每次查询都去DB拉取,节省了这部分查SQL的耗时,美滋滋呀~
96+
- 当然,不是什么数据都适合放到缓存的哦,访问比较频繁的热点数据才考虑缓存起来呢~
97+
98+
### 4.优化程序逻辑、代码
99+
优化程序逻辑,优化程序代码,是可以节省耗时的。
100+
101+
我这里也就本次的转账接口优化,举个例子吧~(优化前,联行号查询了两次)
102+
103+
**优化前:**
104+
105+
```
106+
107+
punlic void process(Req req){
108+
//检验参数,包括联行号(前端传来的payeeBankNo可以为空,但是如果后端没匹配到,会抛异常)
109+
checkTransParams(Req req);
110+
//Save DB
111+
saveTransDetail(req);
112+
}
113+
114+
void checkTransParams(Req req){
115+
//check Amount,and so on.
116+
checkAmount(req.getamount);
117+
//check payeebankNo
118+
if(Utils.isEmpty(req.getPayeeBankNo())){
119+
String payeebankNo = getPayeebankNo(req.getPayeeAccountNo);
120+
if(Utils.isEmpty(payeebankNo){
121+
throws Exception();
122+
}
123+
}
124+
}
125+
126+
int saveTransDetail(req){
127+
String payeebankNo = getPayeebankNo(req.getPayeeAccountNo);
128+
req.setPayeeBankNo(payeebankNo);
129+
insert(req);
130+
...
131+
}
132+
133+
```
134+
**优化后:**
135+
136+
```
137+
void checkTransParams(Req req){
138+
//check Amount,and so on.
139+
checkAmount(req.getamount);
140+
//check payeebankNo
141+
if(Utils.isEmpty(req.getPayeeBankNo())){
142+
String payeebankNo = getPayeebankNo(req.getPayeeAccountNo);
143+
if(Utils.isEmpty(payeebankNo){
144+
throws Exception();
145+
}
146+
}
147+
//查询到有联行号,直接设置进去啦,这样等下入库不用再差多一次
148+
req.setPayeeBankNo(payeebankNo);
149+
}
150+
151+
int saveTransDetail(req){
152+
insert(req);
153+
...
154+
}
155+
```
156+
157+
**解析:**
158+
- 对于优化程序逻辑、代码,是可以降低接口耗时的。以上demo只是一个很简单的例子,就是优化前payeeBankNo查询了两次,但是其实只查一次就可以了。很多时候,我们都知道这个点,但就是到写代码自己又忘记了呀~所以,写代码的时候,留点心吧,优化你的程序逻辑、代码哦。
159+
- 除了以上demo这点,还有其它的,如优化if复杂的逻辑条件,考虑是否可以调整顺序,或者for循环,是否重复实例化对象等等,这些都是我们需要关注的优化点。
160+
161+
之前我这篇文章,也提了几个优化点啦,有兴趣的朋友可以看一下哈~
162+
163+
[写代码有这些想法,同事才不会认为你是复制粘贴程序员](https://juejin.im/post/5dfe2e72518825125f39a2de#heading-1)
164+
165+
### 5.优化SQL
166+
167+
很多时候,你的接口性能瓶颈就在SQL这里,慢查询需要我们重点关注的呢。
168+
169+
我们可以通过这些方式优化我们的SQL:
170+
- 加索引
171+
- 避免返回不必要的数据
172+
- 优化sql结构
173+
- 分库分表
174+
- 读写分离
175+
176+
有兴趣的朋友可以看一下我这篇文章呢,很详细的SQL优化点:
177+
178+
[后端程序员必备:书写高质量SQL的30条建议](https://juejin.im/post/5e624d156fb9a07ca80ab6f2)
179+
180+
181+
### 6.压缩传输内容
182+
183+
压缩传输内容,让文件更小,因此传输会更快啦。10M带宽,传输10k的报文,一般比传输1M的会快呀;打个比喻,一匹千里马,它驮着一百斤的货跑得快,还是驮着10斤的货物跑得快?
184+
185+
186+
传输100个转账对账对象耗时:
187+
188+
传输500个转账对象的耗时:
189+
190+
191+
**解析:**
192+
- 如果你的接口性能不好,然后传输报文很大,这时候是可以考虑压缩文件内容传输的,最后优化效果可能很不错哦~
193+
194+
195+
### 7.考虑使用文件/MQ等其他方式暂存,异步再落地DB
196+
如果数据太大,落地数据库实在是慢,可以考虑先用文件的方式保存,或者考虑MQ,先落地,再异步保存到数据库~
197+
> 本次转账接口,如果是并发开启,10个并发度,每个批次1000笔数据,数据库插入会特别耗时,大概10秒左右,这个跟我们的数据库机制有关,并发情况下,因为优先保证同步,所以并行的插入变成串行啦,就很耗时。
198+
199+
**优化前:**
200+
201+
![](https://user-gold-cdn.xitu.io/2020/5/30/172649d6bd1017bf?w=704&h=490&f=png&s=43540)
202+
203+
**优化后:**
204+
205+
![](https://user-gold-cdn.xitu.io/2020/5/30/17264a156b8b9691?w=778&h=518&f=png&s=50459)
206+
**解析:**
207+
- 如果你的耗时瓶颈就在数据库插入操作这里了,那就考虑文件保存或者MQ或者其他方式暂存吧,文件保存数据,对比一下耗时,有时候会有意想不到的效果哦。
208+
209+
210+
### 8.跟产品讨论需求最恰当,最舒服的实现方式
211+
这点个人觉得还是很重要的,有些需求需要好好跟产品沟通的。
212+
213+
> 比如有个用户连麦列表展示的需求,产品说要展示所有的连麦信息,如果一个用户的连麦列表信息好大,你拉取所有连麦数据回来,接口性能就降下来啦。如果产品打桩分析,会发现,一般用户看连麦列表,也就看前几页~因此,奸笑,哈哈~ 其实,那个超大分页加载问题也是类似的。即limit +一个超大的数,一般会很慢的~~
214+
215+
### 总结
216+
217+
本文呢,基于一次对外接口耗时优化的实践,15秒降到4秒左右,总结了优化接口性能的八个点,希望对大家日常开发有帮助哦~嘻嘻,有兴趣可以逛逛我的github哈,本文会收藏到github里滴
218+
> https://github.com/whx123/JavaHome
219+
220+
221+
### 公众号
222+
![](https://user-gold-cdn.xitu.io/2020/5/16/1721b50d00331393?w=900&h=500&f=png&s=389569)
223+
- 欢迎关注我个人公众号,交个朋友,一起学习哈~
224+
225+
226+

0 commit comments

Comments
 (0)