|
| 1 | +## 前言 |
| 2 | +最近对外接口偶现504超时问题,真枪实弹搞了一次接口性能优化。在这里结合优化过程,总结了接口优化的八个要点,希望对大家有帮助呀~ |
| 3 | +- 数据量比较大,批量操作数据入库 |
| 4 | +- 耗时操作考虑异步处理 |
| 5 | +- 恰当使用缓存 |
| 6 | +- 优化程序逻辑、代码 |
| 7 | +- SQL优化 |
| 8 | +- 压缩传输内容 |
| 9 | +- 考虑使用文件/MQ等其他方式暂存,异步再落地DB |
| 10 | +- 跟产品讨论需求最恰当,最舒服的实现方式 |
| 11 | + |
| 12 | +嘻嘻,先看一下我们对外转账接口的大概流程吧 |
| 13 | + |
| 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 | + |
| 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 | + |
| 88 | + |
| 89 | +**优化后:** |
| 90 | + |
| 91 | + |
| 92 | + |
| 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 | + |
| 202 | + |
| 203 | +**优化后:** |
| 204 | + |
| 205 | + |
| 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 | + |
| 223 | +- 欢迎关注我个人公众号,交个朋友,一起学习哈~ |
| 224 | + |
| 225 | + |
| 226 | + |
0 commit comments