接口性能提升方法
接口性能提升方法
1.索引
1.1 没加索引
sql语句中where条件的关键字段,或者order by后面的排序字段,忘了加索引,这个问题在项目中很常见。
项目刚开始的时候,由于表中的数据量小,加不加索引sql查询性能差别不大。
目前在mysql中如果想要修改索引,只能先删除索引,再重新添加新的。
1.2 索引没生效
可以使用explain命令,查看mysql的执行计划,它会显示索引的使用情况。
1.3 选错索引
必要时可以使用force index来强制查询sql走某个索引。
2. sql优化
3. 远程调用
3.1 并行调用
3.2 数据异构
4. 重复调用
4.1 循环查数据库
这里有个需要注意的地方是:id集合的大小要做限制,最好一次不要请求太多的数据。要根据实际情况而定,建议控制每次请求的记录条数在500以内。
4.2 死循环
还有一种隐藏的比较深的死循环,是由于代码写的不太严谨导致的。如果用正常数据,可能测不出问题,但一旦出现异常数据,就会立即出现死循环。
4.3 无限递归
建议写递归方法时,设定一个递归的深度,比如:分类最大等级有4级,则深度可以设置为4。然后在递归方法中做判断,如果深度大于4时,则自动返回,这样就能避免无限循环的情况。
5. 异步处理
在这里有个原则就是:核心逻辑可以同步执行,同步写库。非核心逻辑,可以异步执行,异步写库。
通常异步主要有两种:多线程 和 mq。
5.1 线程池
但使用线程池有个小问题就是:如果服务器重启了,或者是需要被执行的功能出现异常了,无法重试,会丢数据。
5.2 mq
因为发送mq消息速度是很快的,我们只需关注业务操作的代码即可。
6. 避免大事务
我们该如何优化大事务呢?
少用@Transactional注解
将查询(select)方法放到事务外
事务中避免远程调用
事务中避免一次性处理太多数据
有些功能可以非事务执行
有些功能可以异步处理
7. 锁粒度
为了解决并发场景下,多个线程同时修改数据,造成数据不一致的情况。通常情况下,我们会:加锁。
7.1 synchronized
在java中提供了synchronized关键字给我们的代码加锁。
通常有两种写法:在方法上加锁 和 在代码块上加锁。
同时它也带来了新的问题:synchronized只能保证一个节点加锁是有效的,但如果有多个节点如何加锁呢?
答:这就需要使用:分布式锁了。目前主流的分布式锁包括:redis分布式锁、zookeeper分布式锁 和 数据库分布式锁。
由于zookeeper分布式锁的性能不太好,真实业务场景用的不多,这里先不讲。
7.2 redis分布式锁
7.3 数据库分布式锁
mysql数据库中主要有三种锁:
表锁:加锁快,不会出现死锁。但锁定粒度大,发生锁冲突的概率最高,并发度最低。
行锁:加锁慢,会出现死锁。但锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
间隙锁:开销和加锁时间界于表锁和行锁之间。它会出现死锁,锁定粒度界于表锁和行锁之间,并发度一般。
优先使用行锁,其次使用间隙锁,再其次使用表锁。
8.分页处理
8.1 同步调用
google的guava工具中的Lists.partition方法,用它来做分页简直太好用了,不然要写一大堆分页的代码。
8.2 异步调用
使用CompletableFuture类,多个线程异步调用远程接口,最后汇总结果统一返回。
9.加缓存
9.1 redis缓存 redis和memcached
9.2 二级缓存
使用二级缓存,即基于内存的缓存。
除了自己手写的内存缓存之后,目前使用比较多的内存缓存框架有:guava、Ehcache、caffine等。
10. 分库分表
当系统发展到一定的阶段,用户并发量大,会有大量的数据库请求,需要占用大量的数据库连接,同时会带来磁盘IO的性能瓶颈问题。
随着用户数量越来越多,产生的数据也越来越多,一张表有可能存不下。由于数据量太大,sql语句查询数据时,即使走了索引也会非常耗时。
需要做分库分表
如果有用户请求过来的时候,先根据用户id路由到其中一个用户库,然后再定位到某张表。
路由的算法挺多的:
根据id取模,比如:id=7,有4张表,则7%4=3,模为3,路由到用户表3。
给id指定一个区间范围,比如:id的值是0-10万,则数据存在用户表0,id的值是10-20万,则数据存在用户表1。
一致性hash算法
分库分表主要有两个方向:垂直(即业务方向)和水平(即数据方向)。
分库:是为了解决数据库连接资源不足问题,和磁盘IO的性能瓶颈问题。
分表:是为了解决单表数据量太大,sql语句查询数据时,即使走了索引也非常耗时问题。此外还可以解决消耗cpu资源问题。
分库分表:可以解决 数据库连接资源不足、磁盘IO的性能瓶颈、检索数据耗时 和 消耗cpu资源等问题。
如果在有些业务场景中,用户并发量很大,但是需要保存的数据量很少,这时可以只分库,不分表。
如果在有些业务场景中,用户并发量不大,但是需要保存的数量很多,这时可以只分表,不分库。
如果在有些业务场景中,用户并发量大,并且需要保存的数量也很多时,可以分库分表。
11. 辅助功能
11.1 开启慢查询日志
开启慢查询日志需要重点关注三个参数:
slow_query_log 慢查询开关
slow_query_log_file 慢查询日志存放的路径
long_query_time 超过多少秒才会记录日志
set global slow_query_log='ON';
set global slow_query_log_file='/usr/local/mysql/data/slow.log';
set global long_query_time=2;
也可以直接修改配置文件my.cnf
[mysqld]
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 2
11.2 加监控
目前业界使用比较多的开源监控系统是:Prometheus。
可以访问Prometheus的官网:https://prometheus.io/
11.3 链路跟踪
用分布式链路跟踪系统:skywalking。
在skywalking中可以通过traceId(全局唯一的id),串联一个接口请求的完整链路。可以看到整个接口的耗时,调用的远程服务的耗时,访问数据库或者redis的耗时等等,功能非常强大。
可以访问skywalking的官网:https://skywalking.apache.org/