一、背景

有一个功能,提供两个接口,一个是A服务查询列表某天的数据,一个是B服务查询列表中单个对象某天的数据。

需要实现的效果是,

(1)调用A服务得到今天的数据

(2)然后再次调用A服务查询昨天的数据,

(3)然后循环A服务获取的数据依次调用B服务,查询每个对象的其他属性,然后获取某个中间结果。

(4)然后根据上面的中间结果和第依次调用A服务的结果组装最后结果(postLogic)。

 

整体的活动图,大致如下:

一个业务场景的性能优化方案:并发+缓存_时间复杂度

假如服务A调用时间为800ms,服务B调用getData的平均时间为300ms(假设10次), 则在执行postLogic前耗时约为800ms *2 + 300ms*10=4.6s。 

二、方案

如果服务A的两次请求和服务B的一次请求,服务提供方可以包装成一次,当然效率会更高,但是无法提供。

那么,肿么办?

服务A的两次是可以异步并发请求的,而服务B依赖于服务A的第一次请求结果,因此如果服务A两次异步并发请求,则理想条件下耗时为800ms。

服务B的10次也可以异步并发请求,则服务器B的耗时理想状态下为300ms。

异步方案使用线程池执行Callable任务,返回值为Future对象。(带返回值的异步任务)

则postLogic之前总耗时被优化为800ms+300ms = 1.1s。

然后可以再优化,对结果进行缓存,如果缓存有数据直接返回,如果没有查询并计算后再缓存。

可以使用Redis,设置缓存失效时间。(典型的空间换时间)

这样不仅第一次请求耗时尽可能缩短,而且第二次以后请求超快(10-50ms)。

 

三、常见思路

我们要想想耗时的常见因素,主要是

  1. IO
  2. 网络
  3. 服务器性能
  4. 资源的创建和释放:线程的创建和销毁、连接(数据库连接、网络连接)的创建和销毁
  5. 转换:字符到字节转换等
  6. 算法的时间复杂度高(如多层for循环,而且数据量很大)
  7. 数据库查询条件复杂没命中索引等

因此我们思考的角度是

  1. 将串行变为并行或并发
  2. 同步操作变异步操作
  3. 多个请求合并成一个请求
  4. 用空间换时间
  5. 算法时间复杂度的优化
  6. 提高机器性能(CUP/内存/宽带/磁盘等)
  7. 利用各种池,如数据库连接池、缓存连接池等
  8. 数据库索引优化