接口性能优化的技巧

news/2024/11/25 11:38:50/

一. 索引

索引优化的成本是最小的,可以通过线上日志或者监控报告,查到某个接口用到的某条sql语句的耗时。

1.1 没加索引

sql语句中where条件的关键字段,或者order by后面的排序字段,忘了加索引。

1.2 索引没生效

可以通过explain执行计划查看索引的使用情况

常见的索引失效原因:不满足最左前缀原则,范围索引列没有放最后,使用了select * ,索引列上有计算,索引列上使用了函数,字符类型没加引号,用is null和is not null没注意字段是否允许为空,like查询左边有%,使用or关键字时没有注意等等。

1.3 选错索引

明明是同一条sql,只是入参不同而已,有的时候走的索引a,有的时候走的是索引b?必要时可以使用force index来强制查询sql走某个索引。

二. sql优化

如果优化索引后,也没啥效果,就需要试着优化sql语句,相比较java代码来说,这种方式的成本要小得多。

sql优化技巧:

2.1 避免使用select * ,它不会走覆盖索引,会出现大量回表操作,从而导致查询sql的性能很低。在我们日常开发时,

应只查需要用到的列,多余的列无需查出来。

2.2 用union all 代替union ,我们都知道sql语句使用union关键字后,可以获取排重后的数据,而如果使用union all关键字,可以获取所有的数据,包含重复的数据。排重的过程需要遍历,排序和比较,它更耗时,更消耗cpu资源。除非业务场景需要,否则尽量使用union all。

2.3 小表驱动大表,in 适用于左边大表,右边小表,exists适用于左边小表,右边大表。

2.4 批量操作,在代码中重复性调用数据库,每次远程请求建立连接都是会消耗一定性能的,对于一些批量插入的业务场景,可以将多条sql合并为一条执行。

2.5 多用limit,

2.6 in中的值太多,尽量分批查询

2.7 增量查询,

2.8 高效的分页,将limit的开始位置使用where条件替换

2.9 用连接查询代替子查询,子查询会创建临时表,查询结束后再删除,有额外的性能消耗,可以使用连接查询优化

2.10 join的表不应该太多,尽量不超过3个

2.11 提升group by 的效率,先用where条件圈定范围,在使用group by

三. 远程调用

3.1 对于多个调用的场景可以使用线程池异步执行,结果的获取可以通过CompleteFuture去实现

3.2 数据异构,将不相关的数据全部排除,需要用的数据全部通过某个任务整合到一起,这样查询时只需要调用一次即可(适用于离线数据,如果对数据实时性要求较高,则有数据一致性的问题)

四. 异步处理

通过梳理业务逻辑,核心逻辑可以同步执行,同步写库,非核心逻辑,可以异步执行,异步写库。

通常异步主要有两种:多线程和mq。

五. 避免大事务

大事务可能引发的问题:死锁,回滚时间长,并发情况下数据库连接池被占满,锁等待,接口超时,数据库主从延迟等等...

优化方式:少用@Transactional注解,将查询方法放在事务外,事务中避免远程调用,事务中避免一次性处理太多数据,有些功能可以非事务执行,有些功能可以异步处理(调用类中的私有方法会使事务失效,可以通过代理调用)。

六. 锁粒度

在某些业务场景下, 为了防止多个线程并发修改某个临界资源,造成数据异常,我们会选择 加锁

6.1 synchronized

java 中提供了synchronized关键字给我们的代码加锁,通常是在方法上 和 在代码块上 加锁。但是在加锁的过程中,需要注意锁的范围,也就是锁粒度,尽量只加在会产生数据竞争的地方

6.2 redis分布式锁的八大坑

非原子操作:setNx 与 expire 命令分开执行,非原子操作

忘了释放锁:在try-cathc-finally 中增加释放锁的操作

释放了别人的锁:假设线程A和线程B都使用了lockKey加锁,线程A加锁成功了,但是由于业务逻辑执行耗时较长,锁超时释放了,这时线程B加上了锁,线程A执行结束后将lockKey释放,这就是一个典型的案例。所以在加锁的过程中,需要多设置一个requestId,自己只能释放自己加的锁,不允许释放别人加的锁。

大量失败请求:可以通过自旋锁解决

锁重入问题:使用可重入锁

锁竞争问题:细化锁粒度,使用读写锁,分段锁

锁超时问题:使用TimeTask实现自动续期功能(看门狗机制)自动续期的功能是获取锁之后开启一个定时任务,每隔十秒判断一下锁是否存在,如果存在则刷新过期时间,如果续期三次,也就是三十秒后。业务方法还是没有执行完,就不再续期了。

主从复制的问题:使用RedissonRedLock。

6.3 数据库分布式锁

mysql数据库主要有三种锁,

表锁:加锁快,不会出现死锁,但锁粒度大,发生锁冲突的概率最高,并发度最低。

行锁:加锁慢,会出现死锁,但锁粒度最小,发生锁冲突的概率最低,并发度也最高。

间隙锁:开销和加锁时间介于表锁和行锁之间,会出现死锁,锁粒度介于表锁和行锁之间,并发度一般。

数据库锁的优化方向是:优先使用行锁,其次使用间隙锁,再使用表锁。

七. 分页处理

将一次获取所有的数据请求,改成分多次获取,每次只获取一部分用户的数据,最后进行合并和汇总。

八. 加缓存

九. 分库分表

十. 辅助功能


http://www.ppmy.cn/news/1549811.html

相关文章

Java 查询最大最小值 详解

在 Java 中,查询最大值和最小值是常见需求。以下将详细介绍 最大值和最小值的查询方法,包括适用于数组、集合、以及更复杂的数据结构的解决方案。 1. 使用 Math 类 Java 提供了 Math.max 和 Math.min 方法,可用于直接比较两个值。 适用场景…

VELO SkyOW+坐垫,一起Cityride温暖你的上海之旅

随着冬季的到来,上海的街头巷尾弥漫着一种独特的浪漫气息,当金黄的落叶从空中飘落,铺满路边,只是路过就仿佛骑进了一幅世界名画。无论是沿着外滩漫游,还是穿行在浦东的高楼间,骑行的方式总能让你充分体验到…

@EnableConfigurationProperties @ConfigurationProperties

EnableConfigurationProperties && ConfigurationProperties的使用时机 今天在写properties时想到了这个问题,为什么有时候我需要写EnableConfigurationProperties有时候又不需要呢?下面就详细讲讲。 Data Component ConfigurationProperties(pr…

关于图论建模的一份介绍

图论是离散数学的一部分,主要研究点与线所组成的形状。不过它们并非与几何中的图形类似,而是一种抽象化的产物,因此,我们可以通过这种方式来将一些生活、生产等中的问题进行建模,然后用数学规划等方式解决。所以&#…

docker学习笔记跟常用命令总结

Docker简介 Docker是一个用于构建运行传送应用程序的平台 镜像 将应用所需的函数库、依赖、配置等与应用一起打包得到的就是镜 镜像结构 镜像管理命令 命令说明docker pull拉取镜像docker push推送镜像docker images查看本地镜像docker rmi删除本地镜像docker image prune…

LLMops产品介绍

文章目录 字节跳动的扣子优点低代码开发丰富的插件与能力扩展强大的记忆与数据交互能力应用场景广泛 不足模型选择相对受限定制化程度受限输出效果有待提高应用部署范围有限市场认知度和用户基础不足 开悟大模型运营管理系统(LLMOPS)优点全生命周期管理降…

初识线程池

目录 初识线程池为什么要引入线程池标准库中的线程池代码演示创建线程池的时候,设置多少个线程合适?? 初识线程池 池 是一个非常重要的概念 你可能听说过 池化技术 常量池、数据库连接池、线程池、进程池、内存池… 为什么要引入线程池 最开…

IDEA优雅debug

目录 引言一、断点分类🎄1.1 行断点1.2 方法断点1.3 属性断点1.4 异常断点1.5 条件断点1.6 源断点1.7 多线程断点1.8 Stream断点 二、调试动作✨三、Debug高级技巧🎉3.1 watch3.2 设置变量3.3 异常抛出3.4 监控JVM堆大小3.5 数组过滤和筛选 引言 使用ID…