前言:所实现接口的功能是对文件进行不同维度的检查校验。
1、策略模式的使用
需求涉及到多个检查的实现,后续会增加新的维度检查,于是在接口设计之初就使用了策略模式,方便后期的维护和拓展。
实现思路:
- 定义策略接口:创建一个
Strategy接口 - 定义策略工厂类:方法中包括
Map<String, Strategy>,包括getStrategy获取策略实现类、register注册类方法 - 具体策略实现:为每个支付方式创建一个实现类,使用
@Component注解将其注册为 Spring Bean,实现InitializingBean中afterPropertiesSet,注册策略工厂类中 - 调用具体策略:从策略工厂代码中
参考代码:
- 定义策略接口
public interface Strategy {
void apply(DTO dto);
}
- 定义策略工厂类
public class StrategyFactory {
private static final Map<String, Strategy> STRATEGY_MAP = new ConcurrentHashMap();
public static Strategy getStrategy(CostReviewRuleTypeEnum ruleType) {
return STRATEGY_MAP.get(ruleType);
}
public static void register(String type, Strategy strategy) {
if (!STRATEGY_MAP.containsKey(type)) {
STRATEGY_MAP.put(ruleType, strategy);
}
}
}
- 具体策略实现类
@Component
public class StrategyImpl implements Strategy, InitializingBean{
@Override
public void apply(DTO dto) {
System.out.println("具体策略实现类");
}
@Override
public void afterPropertiesSet() throws Exception {
StrategyFactory.register(typeEnum//枚举, this);
}
}
2、SQL优化分析
2.1、 批量操作
批量查询和更新操作在性能、维护和代码简洁性方面都具有显著优势,尤其是在处理大量数据或频繁调用数据库或外部服务的场景中。
当业务逻辑比较复杂,找出需要批量查询的代码,需要对业务有很深的理解以及大量的代码阅读,为此,我们可以借助Arthas工具来找出此类代码。
参考命令:
- 使用
trace命令跟踪方法调用 假设getOrderDetailsById方法属于OrderService类,可以使用以下命令跟踪调用情况:
trace com.example.OrderService getOrderDetailsById
这个命令会追踪 getOrderDetailsById 方法的每一次调用,显示调用的时间和堆栈信息。
- 观察方法调用频率和堆栈信息
通过 trace 命令输出的信息,我们可以看到该方法被调用的频率、调用的耗时、调用的堆栈。例如,可能的输出如下:
`---ts=2023-10-08 15:34:55;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@18b4aac2
`---[0.234753 ms] com.example.OrderService.getOrderDetailsById
`---[0.245398 ms] com.example.OrderService.getOrderDetailsById
`---[0.237688 ms] com.example.OrderService.getOrderDetailsById
如果看到该方法在短时间内多次被调用,且耗时较多,就表明可能存在重复的单条查询。观察堆栈信息,可以确定调用的上下文代码块。
2.2、 慢sql优化
分析步骤:
1、拿到一条sql,首先分析有没有命中索引,如果命中索引后仍然慢,考虑有没有进行回表操作
2、只查询需要的字段
3、当查询多个表中的字段时,是否可以考虑字段冗余(此处会造成数据不一致的问题)
2.3、 索引失效
-
不适当的查询条件:
- 使用了不等于(
<>)、IS NULL或IS NOT NULL等条件,这些条件通常会导致索引失效。 - 使用了函数或表达式对索引列进行操作,例如
WHERE YEAR(date_column) = 2023。
- 使用了不等于(
-
数据类型不匹配:
- 查询条件中的数据类型与索引列的数据类型不一致,例如使用字符串查询数字类型的列。
-
使用了模糊查询:
- 在 LIKE 查询中,如果通配符
%位于前面,例如LIKE '%value%',则会导致索引失效。
- 在 LIKE 查询中,如果通配符
-
索引选择性差:
- 如果索引的选择性不高(即返回的行数较多),查询优化器可能会选择全表扫描而不是使用索引。
-
组合索引的使用不当:
- 在组合索引中,如果查询条件没有按照索引的顺序使用,可能会导致索引失效。例如,对于
(a, b)的组合索引,查询条件应当遵循a和b的顺序。
- 在组合索引中,如果查询条件没有按照索引的顺序使用,可能会导致索引失效。例如,对于
-
使用了 OR 条件:
- 在使用
OR连接的条件中,某些情况下可能导致索引失效,尤其是当OR连接的字段没有索引时。
- 在使用
-
临时表或子查询:
- 在某些复杂查询中,使用临时表或子查询可能导致索引失效。
3、缓存预热
- 对于项目中的基础配置的分类路径进行缓存,做缓存预热的数据有以下几个特点:修改频率低、数据要求实时性不高(保证最终一致性)
4、异步处理:引入线程池
- 将接口请求处理逻辑封装为任务,并通过线程池提交这些任务。
- 每一个任务返回key,前端通过轮询接口,查看任务执行情况。
5、数据冗余
适当考虑数据冗余,此时会减少部分的查询语句,但需注意导致的问题。
因为本需求是分析在基础配置下的那一刻的检查,所以此后的判断只针对于那一刻的配置,后续的查询只针对于当前表,因为表中已存储了所有已需要的信息.
6、数据压缩
当传输数据比较大的时候,可以考虑对返回数据进行压缩。
下面介绍一些压缩算法:
接口返回的数据压缩可以有效减少数据传输的大小,提高网络传输效率。以下是几种常见的方法和技术来实现数据压缩:
-
使用 Gzip 压缩:
- 在服务器端启用 Gzip 压缩,可以自动压缩 HTTP 响应数据。大多数现代 Web 服务器(如 Nginx、Apache)都支持 Gzip 压缩。
- 在响应头中添加
Content-Encoding: gzip,客户端在接收到数据后会自动解压。
-
使用 Brotli 压缩:
- Brotli 是一种较新的压缩算法,通常比 Gzip 提供更好的压缩率。许多现代浏览器和服务器也支持 Brotli。
- 在响应头中添加
Content-Encoding: br。
-
JSON 数据的压缩:
- 对于 JSON 数据,可以使用 JSON 压缩库(如
json-minify)去除空格和换行符,从而减小数据体积。 - 也可以考虑使用更高效的序列化格式,如 MessagePack 或 Protocol Buffers。
- 对于 JSON 数据,可以使用 JSON 压缩库(如
-
选择合适的数据格式:
- 根据需求选择合适的数据格式,例如使用二进制格式(如 Avro、Thrift)而不是文本格式(如 JSON),可以有效减少数据大小。
本文通过具体的文件检查场景,深入探讨如何有效减少接口响应时间,分析影响接口性能的关键问题,并提出针对性的优化策略,以提升系统的整体效率和用户体验。