后端API性能优化方法堪称优雅!
连接性能优化对于喜欢后台开发的同学来说肯定很熟悉。有些公司在性能评估中加入了接口性能。今天我就和大家聊聊开发中常用的用户界面优化方法。
索引
有问题,首先想一下SQL是有索引还是有索引。
我们可以通过explain命令查看索引使用情况
explain select * from usero where userId like '%234';
SQL优化方法
索引创建原理参考:MySQL索引
深度分页问题不跳过,但SQL不跳过offset+N行,然后返回切换前的偏移行并返回N行。当排量特别大时,效率很低。控制返回的页面总数或对超过特定阈值的页面重写 SQL。
标签存储方式
select id,name FROM user where id > 100000 limit 10;
延迟关联方式
延迟关联方式是将条件移到主键的索引树上,然后归约回数组。优化后的SQL如下:
select a.id,a.name,a.balance FROM user a INNER JOIN (SELECT b.id FROM user b WHERE b.create_time > '2022-05-19' limit 100000, 10) AS c on a.id= c.id;
先通过二级索引树查询符合条件的主键IDcreate_time
,然后通过主键ID与原表join,这样主键索引就是后面直接用。与此同时,表上的回报也减少了。
使用缓存
缓存是以空间换时间的思想。缓存待检查的数据。如果需要,直接检查缓存而不是检查它。数据库或计算过程。
缓存一般有两种:一种是本地缓存,即JVM缓存,另一种是Memcached/Redis类的集中式缓存。
缓存的使用还可以实现预取的想法,提前计算任何必要的信息并将其存储在缓存中。如果需要的话,去缓存中检索它。可以通过计划任务定期更新缓存。
使用缓存需要考虑几个点:
- 缓存的高可用性问题。如果缓存宕机了,数据库会溢出吗?
- 缓存入侵。虽然缓存没有宕机,但是有大量的对某些key的查询,而这些key没有被缓存,导致短时间内大量的请求压垮了数据库。
- 缓存故障。指热点键。高并发重点要达到这一点。当key失效时,持久高并发会破坏缓存,直接请求数据库。
- 大量键盘快捷键已经过时。当某些键无效时,短时间内会出现大量请求写入,导致数据库过载,这种情况称为缓存雪崩。
- 缓存和数据库一致性问题。
池化思想
池化思想的解决方案是避免创建重复可重用的对象或连接,避免不必要的损失。毕竟,创造和毁灭也需要时间。我们的共享线程池和数据库连接池的连接思想。
我们在编写代码时可以利用这个思想来优化用户界面的性能。任何不阻塞主流程的业务逻辑都可以是异步的并在后台完成。
异步处理
异步思维:对于结果不需要的长时间运行的逻辑,可以考虑放在异步执行,这样可以减少UI的时间消耗。
示例: 更多用户成功注册后,还可以异步处理短信和电子邮件通知 异步实现,可以使用池、异步条目、异步调用或消息队列在链中实现。 。 串行是指当前执行逻辑必须等到上一个执行逻辑完成后才能执行。并行是指两个执行逻辑同时执行。 如果是同步调用,则总耗时为T=T1+T2+T3;如果是异步调用,则总共花费的时间为T=Max(T1,T2,T3)。 串联条件:三个调用之间没有连接关系,可以并行。如果需要获取第一个调用的结果,然后根据结果调用第二个和第三个接口,则无法进行异步调用。 数据库的批量使用主要是为了同时存储多个数据。 优化前: 优化后: 大事件是 因此,要优化用户界面,我们需要避免大事件问题。我们可以通过以下解决方案来避免大型事件: 锁通常是高并发场景下保护共享资源的一种方式,但如果锁定精度太粗,会严重影响前端的性能。 有关锁定精度的信息:表示锁定的范围有多大。无论是jvm锁、redis分布式锁还是数据库锁,只需要锁定关键资源即可。如果不涉及共享资源,则不需要加锁。 错误做法 正确做法: 优化程序逻辑和程序代码可以节省时间。例如程序创建了太多不必要的对象或程序逻辑混乱、反复检查数据库或实现的逻辑算法不是最有效的甚至产生无限循环等等。 在复杂的逻辑条件下,改变顺序有时可以使程序更加高效。 有时候我们自己写一个无限循环,比如下面的代码: 如果条件很复杂,如果判断不正确或者缺少一些逻辑判断,在某些场景下可能会发生这种情况。无限循环问题。 随着系统发展到一定阶段,用户并发量较高,数据库请求较多,需要大量的数据库连接,同时也造成磁盘IO性能瓶颈。 此外,随着用户数量的增加,产生的数据也越来越多。由于数据量很大,SQL语句查询数据,即使使用索引,也需要花费大量的时间。 目前需要对数据库进行分表。 共享数据库和表格主要有两个方向: 在水平方向(即数据方向),子库和子表的作用其实是不同的,不能混为一谈。 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);
})
串行转并行
批量处理
//for循环单笔入库
for(User user:userList){
insert(user);
}
batchInsert(userList);
避免大事件
远程精度锁定太粗
//非共享资源
private void notShare(){
}
//共享资源
private void share(){
}
private int wrong(){
synchronized (this) {
share();
notShare();
}
}
//非共享资源
private void notShare(){
}
//共享资源
private void share(){
}
private int right(){
notShare();
synchronized (this) {
share();
}
}
优化程序结构
while(true) {
if(condition) {
break;
}
System.out.println("do samething");
}
分库与分表
垂直
和水平
。 子库
:其目的是为了解决数据库连接资源不足和磁盘IO性能瓶颈的问题。 表分区
:这样解决了单表数据量太大,SQL语句查询数据时,即使使用索引也非常耗时的问题。另外,还可以解决CPU资源消耗的问题。 分库分表
:可以解决数据库连接资源不足、磁盘IO性能瓶颈、数据检索耗时、CPU资源消耗等问题。 总结
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。