从具体场景落地接口优化方案

48 阅读6分钟

前言:所实现接口的功能是对文件进行不同维度的检查校验。

1、策略模式的使用

需求涉及到多个检查的实现,后续会增加新的维度检查,于是在接口设计之初就使用了策略模式,方便后期的维护和拓展。

实现思路

  • 定义策略接口:创建一个 Strategy 接口
  • 定义策略工厂类:方法中包括Map<String, Strategy>,包括getStrategy获取策略实现类、register注册类方法
  • 具体策略实现:为每个支付方式创建一个实现类,使用 @Component 注解将其注册为 Spring Bean,实现InitializingBeanafterPropertiesSet,注册策略工厂类中
  • 调用具体策略:从策略工厂代码中

参考代码

  1. 定义策略接口
public interface Strategy {
    void apply(DTO dto);
}
  1. 定义策略工厂类
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);
        }
    }
}
  1. 具体策略实现类
@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工具来找出此类代码。
参考命令

  1. 使用 trace 命令跟踪方法调用 假设 getOrderDetailsById 方法属于 OrderService 类,可以使用以下命令跟踪调用情况:
trace com.example.OrderService getOrderDetailsById

这个命令会追踪 getOrderDetailsById 方法的每一次调用,显示调用的时间和堆栈信息。

  1. 观察方法调用频率和堆栈信息

通过 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、 索引失效
  1. 不适当的查询条件

    • 使用了不等于(<>)、IS NULL 或 IS NOT NULL 等条件,这些条件通常会导致索引失效。
    • 使用了函数或表达式对索引列进行操作,例如 WHERE YEAR(date_column) = 2023
  2. 数据类型不匹配

    • 查询条件中的数据类型与索引列的数据类型不一致,例如使用字符串查询数字类型的列。
  3. 使用了模糊查询

    • 在 LIKE 查询中,如果通配符 % 位于前面,例如 LIKE '%value%',则会导致索引失效。
  4. 索引选择性差

    • 如果索引的选择性不高(即返回的行数较多),查询优化器可能会选择全表扫描而不是使用索引。
  5. 组合索引的使用不当

    • 在组合索引中,如果查询条件没有按照索引的顺序使用,可能会导致索引失效。例如,对于 (a, b) 的组合索引,查询条件应当遵循 a 和 b 的顺序。
  6. 使用了 OR 条件

    • 在使用 OR 连接的条件中,某些情况下可能导致索引失效,尤其是当 OR 连接的字段没有索引时。
  7. 临时表或子查询

    • 在某些复杂查询中,使用临时表或子查询可能导致索引失效。
3、缓存预热
  • 对于项目中的基础配置的分类路径进行缓存,做缓存预热的数据有以下几个特点:修改频率低、数据要求实时性不高(保证最终一致性)
4、异步处理:引入线程池
  • 将接口请求处理逻辑封装为任务,并通过线程池提交这些任务。
  • 每一个任务返回key,前端通过轮询接口,查看任务执行情况。
5、数据冗余

适当考虑数据冗余,此时会减少部分的查询语句,但需注意导致的问题。

因为本需求是分析在基础配置下的那一刻的检查,所以此后的判断只针对于那一刻的配置,后续的查询只针对于当前表,因为表中已存储了所有已需要的信息.

6、数据压缩

当传输数据比较大的时候,可以考虑对返回数据进行压缩。
下面介绍一些压缩算法:
接口返回的数据压缩可以有效减少数据传输的大小,提高网络传输效率。以下是几种常见的方法和技术来实现数据压缩:

  1. 使用 Gzip 压缩

    • 在服务器端启用 Gzip 压缩,可以自动压缩 HTTP 响应数据。大多数现代 Web 服务器(如 Nginx、Apache)都支持 Gzip 压缩。
    • 在响应头中添加 Content-Encoding: gzip,客户端在接收到数据后会自动解压。
  2. 使用 Brotli 压缩

    • Brotli 是一种较新的压缩算法,通常比 Gzip 提供更好的压缩率。许多现代浏览器和服务器也支持 Brotli。
    • 在响应头中添加 Content-Encoding: br
  3. JSON 数据的压缩

    • 对于 JSON 数据,可以使用 JSON 压缩库(如 json-minify)去除空格和换行符,从而减小数据体积。
    • 也可以考虑使用更高效的序列化格式,如 MessagePack 或 Protocol Buffers。
  4. 选择合适的数据格式

    • 根据需求选择合适的数据格式,例如使用二进制格式(如 Avro、Thrift)而不是文本格式(如 JSON),可以有效减少数据大小。

本文通过具体的文件检查场景,深入探讨如何有效减少接口响应时间,分析影响接口性能的关键问题,并提出针对性的优化策略,以提升系统的整体效率和用户体验。