订单ES查询性能优化

分享到:

文章目录

背景

由于系统中的订单量大,一些查询语句需要级联多张表来查询,单纯靠数据库的索引已经无法满足查询速度与用户界面响应速度的要求,因此在5年前引入了ES来加快查询速度。但是,原先的方案中ES存放的是全量的订单数据,并且是存放在同一个数据库索引中,随着业务的发展与订单量的累积,ES查询的速度已经越来越慢。通过Grafana监控数据,可以看到单个索引的数据量已达到1.5TB,主要的性能指标越来越差。ES的CPU使用率不时地大于80%,甚至100%,导致极端情况下ES查询耗时十几秒。对于用户的直观感受就是,在界面上面查询数据,需要耗时很久才能看到数据。

解决方案

系统总是慢慢演变的,某个时间点的解决方案都是基于当前的一些情况,满足近3年内的需求就足够了。考虑到成本问题,尽可能在不增加硬件投入的情况下,找到节省时间的优化方案。

在原先的方案中,索引中有1.5TB的数据量。从1.5TB的数据集里面查找数据,会很耗时间。由于系统中大部分的操作都是根据公司来区分的,所以如果把ES里面的数据,按照公司来拆分成不同的索引,某个公司查询订单的时候,仅仅查询它自己公司的索引。拆分成若干个索引之后,最大的一个索引不到3GB,小的一些索引就100多MB。由于单个公司索引的数据量很小,查询速度自然就比原先快了。

系统中也存在一些查询,需要跨公司来查数据,但是这些查询有个特点,它们仅仅需要查最近一定时间范围内的数据,比如半年之内。对于这些数据,可以再专门建一个ES索引来存放,同时有个后台的Job,定期删除过期的数据。这样,就可以控制该索引的总数据量在一定的范围之内,不会因为数据量多大的原因导致查询变慢。

当然,在写代码实现的时候还需要考虑很多具体的问题,比如:

  • 修改原先ES数据实时同步方案,支持根据公司ID写入到不同的索引
  • 修改原先ES数据全量加载方案,支持根据公司ID写入到不同的索引
  • 修改原先ES数据查询方案,支持根据公司ID从不同的索引读取数据
  • 修改ES查询相关设置参数的实现逻辑,比如某个公司是否开启ES,开启ES的走ES查询,不开启ES的走数据库查询。
  • 需要考虑到将来的扩展性,如何更合理的接口
  • 原先的java ES库的某些操作不支持显式指定index名称,需要继承该库中的一些类并重写方法
  • 在过渡阶段,让系统支持新旧两种的查询方式,不需要重启服务,仅通过修改配置就可以实现无缝切换。这样的话,如果由于新方案中,代码有bug,可以直接通过修改配置切换到旧的方式,bug修复后再切换成新的。等到新方案的上线一定的时间,稳定之后,再移除旧方案的代码

优化前后的性能对比

通过Grafana的监控数据,可以很明显地看出优化的效果。

切换新旧方案的瞬间

切换新旧方案的瞬间,可以看到查询耗时断崖式减少

平峰时期CPU使用率对比

优化前平均40%左右

优化后平均5%

高峰时期CPU使用率对比

优化前平均82%,最高98%

优化后平均15%

平峰时期查询耗时对比

优化前平均3.2s

优化后9ms

高峰时期查询耗时对比

优化前平均7.8s,最高13.24s

优化后平均32ms