Code前端首页关于Code前端联系我们

Java接口全连接优化:如何减少接口RT时间

terry 2年前 (2023-09-25) 阅读数 63 #后端开发

以前的应用程序很多而且复杂,所以最近对公司的应用程序进行了优化和重新设计,要求所有接口的RT时间低于xxx的值。

1. 监控

那么问题来了~现在所有的应用程序都是免费的,监控工具几乎不存在。不可能一次只根据一个接口的日志来收集日志。那么如何知道哪些接口rt较长,需要优化呢?羊毛面料。所以我们采取的第一步是监控。

监控工具:指定。

选择精确点时,请考虑以下几个因素:

1。侵入应用程序0的代码,这当然是程序员最担心的。没有人愿意在自己的应用程序中争夺额外的功能,如果这会影响原来的功能。这笔交易根本不值得。 。

2。应用依赖和调用关系一目了然。所有进程都通过traceId链接,以便于故障排除。这也是非常重要的。在分布式系统中,调用关系需求过于复杂。如果没有traceId,你将很难找到该请求的高层调用关系。

3。连接各种监控表,包括RT图,快速找到需要优化的环节。

pinpoint 的源代码和教程可以在 https://github.com/naver/pinpoint 找到。

我们可以粗略的看一下下图所示的微调页面Java接口全链路优化:如何降低接口RT时长

从图中我们可以得出结论:调用连接一栏显示该请求实际上执行了两次SQL。

运行时间一栏显示第一次耗时19ms,第二次耗时722ms。

traceId 是该请求的唯一标识符。如果我们需要在应用程序中记录这个值,我们也可以通过相应的API来检索它。

当然,这个例子只是一个非常简单的请求。还可以监控dubbo调用、http请求、Redis、db等操作。所以,只要应用程序配置好,请求执行调用的细节就一目了然,一切尽在我们的掌握之中。控制而不是让它发展。

2.优化

既然我们找到了目标,那么我们下一步当然就是解决它并优化它。我在优化过程中总结了一些非常常见、实用的优化经验,特别是对于初学者来说,大家可以参考一下。因为对于刚开始编程的人来说,这些都是习惯,都是正常逻辑,但并不代表就是最优的。

1。 SQL操作循环

这种情况对于新手来说很容易做到,比如创建交易订单。我们的主订单和子订单的关系如下: Java接口全链路优化:如何降低接口RT时长

那么创建一个订单需要生成1个主订单和N个子订单。目前很多人都是这样写的(代码只代表执行过程):

insert(主单);
for(子单:子单列表){
    insert(子单);
}
复制代码

于是就有了循环SQL操作,因为SQL操作比较长,循环会大大增加整个界面的RT时长。在这种情况下,您应该一次插入所有子订单,而不是一一处理。优化后的代码如下:

insert(主单);
batchInsert(子单列表);
复制代码

Mybatits支持Label循环,所以只需更改sqlMap文件中的SQL即可。此外,还可以进行批量更新。如需进行批量操作,需要在数据库连接中添加参数allowMultiQueries=true

2。数据库索引

当然索引是必须创建的,不然你得弄清楚什么时候。它只是一个构造,但我们仍然需要正确使用它。 SQL是否使用索引可以通过explain命令检测。 EXPLAIN SELECT * FROMarticle WHERE id=10;Java接口全链路优化:如何降低接口RT时长

键和行反映了使用的索引和预期扫描的行数。还有一些需要注意的地方:

在查询语句中优化ORDER BY时,尽可能使用现有索引来避免实际的排序计算,可以大大提高ORDER BY操作的性能。在一些查询优化过程中,调整索引字段的顺序甚至添加索引字段以避免实际的排序操作是值得的 因为 MySQL 中按实现有两种类型的顺序:

1)。一是直接通过有序索引来检索有序数据,这样就可以将符合客户端要求的有序数据返回给客户端,而无需进行排序操作; Java接口全链路优化:如何降低接口RT时长

2)。首先是MySQL排序算法对存储引擎中返回的数据进行排序,然后将排序后的数据返回给客户端,seq加入索引,可以看到第一张图片是通过define_id和直接进入索引。第二个图像无法进入索引,需要使用 filesort.group by 进行额外的排序操作。其实也是先进行order by操作,然后再进行分组,所以Group by是一种类似的优化方法。 DISTINCT 应​​尽可能少用。 DISTINCT实际上执行group by操作,然后取出每组的第一条记录。

Java 应用程序类型必须与数据库索引列类型匹配。例如java类型是long,数据库类型是varchar。如果这样查询的话,不会使用索引,但是不会报错。 下图是两种方法的对比:Java接口全链路优化:如何降低接口RT时长Java接口全链路优化:如何降低接口RT时长

3.不应该使用计数

计数也是我们经常使用的东西,比如领取优惠券的数量、统计活动参加人数等。这些有时与业务密切相关。比如限时抢购只能卖多少件等。在这种情况下,我们不应该用计数来计数。我们来分析一下为什么不使用计数:

1)。 count会查询所有符合条件的记录。如果表很大,这可能会导致整个表被扫描。每个人都知道后果。 2)。如果使用计数来控制,则必须在业务逻辑运行时对其进行锁定,否则计数的结果将为空,这也会阻塞该活动的所有请求。

在这种场景下,我们通常会在需要控制的记录中添加一个计数字段,例如检查num=10的最大集合值。那么,在业务逻辑中,对整数的控制可以转换为SQL, update xxx set num=num-1 where id=xx and num>0;这是一个原子操作,保证无超领。根据返回结果判断执行是否成功(返回结果为受影响的行数)。

4。锁具

单机锁和分体锁。事实上,能避免的情况下就不应该使用锁,因为加锁意味着只能串行进行,并且并发数会减少到1,这肯定会影响性能。

类似库存扣减的场景,请参见第3条。避免在数据库中使用原子操作。
如果是更新操作,可以使用乐观锁来避免长期阻塞。
如果需要使用锁,无论是单机锁还是分布式锁,我们都需要评估锁的范围,是单个用户的用户ID还是所有用户的用户ID。单个用户可以使用它,因为它是单个用户并发。速率极低,但如果所有用户的操作都被锁定,则应仔细评估。此操作将导致所有用户阻止类似操作。

5。足够的冗余

在分布式系统中,冗余应该是很常见的情况。目前,我们不应该遵循数据库范式标准,因为它是根据数据库范式设计的,所有字段都不冗余,但这给我们的查询带来了很多问题。我们在验证订单时可能需要查询相关询问、验证订单信息、验证产品信息、验证付款信息等。有很多疑问。想想我们接口的RT能不能快,qps能不能高。产品名称等冗余背景信息可以减少对实际模块的查询,这是一个很好的方法。

6.Redis缓存

其实我们系统中通常都会用到缓存,所以这部分没有太多的优化。我们来总结一些经验。缓存刷新策略选择:无效刷新或定时刷新。因为观察到很多接口的RT总是定期变慢。这是因为当缓存失效时,需要从db等模块组装数据,然后推送到缓存中。这段时间并不是所有的请求都能经过缓存,而且当流量较大时也可能成为流量的致命因素。这种情况下,例如首页的推荐产品、推荐帖子等,访问次数多且相同的,可以定期刷新。 严禁使用Keys*命令行:Redis是单线程的,执行该命令会阻塞所有进一步的请求,影响整个系统的运行。

7。搜索引擎

看到这里你可能会感到困惑。搜索引擎如何优化RT?它肯定比DB慢,但在某些场景下可以比DB更快。例如,在社区论坛中,需要对文章进行过滤和排序。总共有十几个字段,可以随意组合。此时DB很无奈,因为条件太多,各种排序操作和相关操作,数据库无法建立索引。将A表和B表的字段组合起来,推入搜索引擎,根据查询条件直接搜索,就可以整理出完整的信息。这样既可以保证rt,又可以防止DB因复杂查询而超载。

作者:胜利同学
链接:https://juejin.im/post/5ba3396ee51d450e43694547
来源:掘金
版权归作者所有。商业转载请联系作者获得许可。非商业转载请注明来源。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门