ES使用优化方案

246 阅读4分钟
Filesystem cache

filesystem cache使我们操作系统的文件缓存机制。es就是依赖这种文件缓存机制但是例如mysql这种就是自己实现的存储引擎机制。所以当我们遇到操作系统内存分配的时候其实要视情况而定,es的机器自然是给文件缓存多些,mysql的话就直接给mysql即可。 回到es,es的数据存储到磁盘上,我们在进行查询的时候,会从磁盘读取数据后缓存到内存里,也就是我们的filesystem cache。如果我们读取数据的时候直接走磁盘速度可能会到秒级,但是如果我们走内存,毫秒级就可以查到数据。可以想到redis为啥快,因为内存操作。所以我们要尽可能找到平衡点,提高文件存储内存。

减少非搜索字段存储

我们有时候一般会这样搞,mysql里面存了什么,我们就往es里面继续存什么。那么可能我们实际搜索的字段只是其中某一个字段,例如content内容。其他的title啊,digest啊,都是不会被查询到的,那么这种就不要存进来。查的时候可以减少一点内存的占用,提高我们整体的有效数据存储。

数据预热

拿大家平时在京东上购物来说,就比如可口可乐吧,大家搜索的会比较多,非常可乐可能搜索的就会少一些。由于我们的内存缓存数据是具有时效性的,所以大家可以针对热点数据进行提前预刷。通过热数据探测啊,记录搜索次数等等啊,动态的去查询一次数据,保证每次尽量命中内存,这样可以极大的提高效率

冷热分离

我们也可以通过不同的索引来存储不同的数据,把我们业务产品认为搜索可能性极大的数据单独在一个索引,搜索可能性极小的走另一个索引,而不是为了方便直接存在一个里面,做聚合,这样可以保证我们的热数据大量保存在内存中,不被冷数据占用。这样也可以极大提高我们的速度。

分页优化

es的分页是聚合产生的。如果说我们每页10条数据,我查询第100页的数据,es的执行逻辑是会把每个shard上存储的前1000条数据都查到一个协调节点上,如果你有个5个shard,那么就有5000条数据,接着协调节点对这5000条数据进行一些合并、处理,再获取到最终第100页的10条数据。那么随着页的越来越深,就会导致效率极慢。所以我们从产品设计上要避免深分页问题。我们一般是使用scroll api来进行解决scroll会生成数据的快照,每次进行翻页进行数据的滚动,比如你在京东上买货,你会发现他一般是触底加载的,b站也同样如此。

scroll Api 使用示例

Elasticsearch中的Scroll API是一种查询API,它主要用于处理大量数据的查询。在传统的查询方式中,一次查询只能返回一定数量的结果,当我们需要查询的数据量很大时,必须不断地发送多次请求来获取所有的数据。而使用Scroll API就可以将所有的满足条件的数据都缓存在内存中,当我们需要分批次处理这些数据时,可以通过滚动游标的方式,逐步地获取所有的查询结果,避免了因为数据量太大而无法一次性获取的问题。

以下是使用Java查询示例:

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.search.SearchScrollResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import java.io.IOException;

public class ESQueryDemo {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ESUtils.getClient();

        //创建search请求
        SearchRequest searchRequest = new SearchRequest("index_name");
        //设置scroll参数
        Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
        searchRequest.scroll(scroll);

        //为search请求设置查询条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery("field_name", "value"));
        sourceBuilder.size(1000);
        searchRequest.source(sourceBuilder);

        //发送search请求获取查询上下文
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        String scrollId = searchResponse.getScrollId();

        //处理查询结果
        do {
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                //处理查询结果
            }
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
            scrollRequest.scroll(scroll);
            searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
        } while (searchResponse.getHits().getHits().length > 0);

        //清除查询上下文
         ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
         clearScrollRequest.addScrollId(scrollId);
         ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);

        client.close();
    }
}

这段代码使用Scroll API对名称为“index_name”的索引中的名称为“field_name”且值为“value”的数据进行了查询,设置了每次查询返回的最大结果集大小为1000,然后使用scroll游标的方式获取了所有的查询结果,并对每条结果进行了处理。如果查询结束后需要清除查询上下文,可以通过ClearScroll API来完成。这样,我们就可以使用ES的滚动游标API来查询大量数据,并分批次进行处理了。