记一次es查询优化

ELK算是现在比较火的一个技术栈了,这段时间接了一个需求,需要在每天亿级别的数据中进行查询,其中包含了聚合等操作,一阵狂码虽然需求实现了,但是性能比较差,查询一次可能需要5-6秒的时间,最为有追求的程序员,这样的效率有些不能接受,于是想了一些办法优化,下面记录一下优化的过程。

需求背景

需求1:我司预发环境的埋点数据和线上环境的埋点数据在同一个es索引中,每天的数据量在亿级别,QA同学在进行埋点测试的时候希望能够根据某些条件自动的筛选出他想要的预发环境的数据(注意,这里可以使用kibana去进行查询,但是由于我司在埋点管理上有一个自己的平台,所以希望能够简化kibana的功能,只通过鼠标点击一下就查询出数据,并和文档进行对比产出校准结果)。

需求2:对于产品和运营同学,希望能够对某一个他们关心的埋点进行数据查询。(注意,对于离线数据,我司有一套自己的数据平台和离线报表系统,可以让相关同学非常方便的进行查看,但是对于实时数据,kibana的使用要求比较高,产品运营同学在没有经过学习的情况下自然很难熟练快捷的使用)。

常规实现:

其实这两个需求一点都不难,对于对es查询语法比较熟悉的同学来说,需求1就是进行几个terms匹配,需求2细分为pv和uv,pv就是总量,uv则进行id的聚合即可。

经过我的一顿操作之后,需求完成了,但是性能比较差,特别在具体的使用过程中,有下面这种场景:

查询数据的时候,并非所有的查询条件都是精确匹配的,对于某些数据,我们需要进行模糊查询,而某些场景下,模糊查询的次数很多,并且条件也很多,在面对亿级别的数据的时候,会造成性能的影响,特别是查询次数较多的情况下,会影响整个集群的性能。

优化

出现了以上的问题之后,我思考了一下解决方案,优化查询语句应该是治标不治本的操作,因为数据量就是这么多,查询条件也比较苛刻,再怎么优化语句也不会有太好的效果。于是我决定从源头出发——既然查询层面解决不了问题,那就是数据收集层面进行分流。

在说具体的优化之前,我先说一下我司在实时数据处理这个技术栈上并没有使用logstash,而是用了flink。

对于需求1,其实我只要将预发数据和线上数据进行隔离,写入到不同于线上数据的另一个es索引中就ok了。预发数据一天也就那么一点,再怎么模糊查询都不会影响性能。这种需求用flink非常好实现,具体的大家可以去学习一下UDF任务。

对于需求2,查询pv没有问题,就是一个简单的hits,而es天生对于count查询就支持的比较好。对于uv的查询,之前是在查询的时候通过聚合的方式操作,而现在则是和需求1一样,通过flink的AggregateFunction在数据流入的时候就进行筛选,对于某一种数据,同一天内同一个id只输出一次,而在完成处理之后,同样的新建一个es索引,将数据存入其中即可。

思考

对于上面的这两个优化,我得出的一个思考就是:

对于量级比较大的数据查询来说,与其在查询上做优化,不如在收集数据的时候就进行清洗。