在订单系统当中使用Elasticsearch

/ 0评 / 14

在订单系统当中使用Elasticsearch

背景

单表亿级别的数据仅仅凭借MySQL的引擎依赖索引去查询已经无法满足当前的需求,面对日益激增的订单数据,而订单又作为状态变更十分敏感的业务产品,对及时性和准确性有着非常高的要求。而业务稳定性的评价,订单的快速准确查询是非常重要的一个指标。

搜索中间件调研

对于Java而已,阿帕奇已经有非常成熟的搜索类库LucLuene。说起这个我们已经经历过Solr的实践(用作搜索POI和城市景点,老东家老旅游公司了,怎么会没有几个全文搜索解决方案?),也深知其在搜索领域的强大,但是当时我们的Solr是自建的,一般用在了低频更新的地点数据上面,这里就先简单介绍下比较熟知的两款中间件。

Solr

官网:https://lucene.apache.org/solr/

Apache Solr是一个基于名为Lucene的Java库构建的开源搜索平台。它以用户友好的方式提供Apache Lucene的搜索功能。作为一个行业参与者近十年,它是一个成熟的产品,拥有强大而广泛的用户社区。它提供分布式索引,复制,负载平衡查询以及自动故障转移和恢复。如果它被正确部署然后管理得好,它就能够成为一个高度可靠,可扩展且容错的搜索引擎。

看了下人的社区氛围真就酸了,由于开源早口碑好,积淀了不少的用户,如果要选择其作为落地的方案的话其实也是比较可靠的.

ElasticSearch

官网:https://www.elastic.co/cn/

Elasticsearch是一个开源(Apache 2许可证),是一个基于Apache Lucene库构建的RESTful搜索引擎。
Elasticsearch是在Solr之后几年推出的。它提供了一个分布式,多租户能力的全文搜索引擎,具有HTTP Web界面(REST)和无架构JSON文档。Elasticsearch的官方客户端库提供Java,Groovy,PHP,Ruby,Perl,Python,.NET和Javascript。
分布式搜索引擎包括可以划分为分片的索引,并且每个分片可以具有多个副本。每个Elasticsearch节点都可以有一个或多个分片,其引擎也可以充当协调器,将操作委派给正确的分片。
Elasticsearch可通过近实时搜索进行扩展。其主要功能之一是多租户。

对于之前加个字段看命令学了一下午的铁笨比我而言,solr其实在配置(经典sechmal.xml rebuild)和运维上都是要难于Es的(Http一把梭,不行就直接PUT),而按场景来划分的话也说了几遍了,不适合热频更新的索引。

关于两者的对比这里有详细的数据:http://solr-vs-elasticsearch.com/

选取ElasticSearch

订单更新频繁、检索多依赖于索引字段,就这一条就够我们抛弃Solr上Es了。但是不同于当时的自建,我们这次的方式是买集群...可能同步方案和更新策略上的考虑比较欠缺,严重依赖阿里云的同步,但是这样一来我们有更多的空间去关注下游的查询,对于数据更新这件事我们无需花更多精力在上面。

当然在阿里云的落地就略显无脑,一路点点点,然后掏钱就好了(真的是这样,只需要考虑需要建哪些索引,同步属性是怎么样,剩下的阿里云统统帮你搞定[阿里云打钱])

基本使用

ES的文档就有点离谱了,由于Es 的版本较多且部分语法在不同版本当中都不太一致,所以使用的时候可能要注意一下。而且文档都是Http请求的Demo文档,Es没有给出一些Java API的使用说明,而这些API其实有点类似JSON的直译总体上比较好理解,只要能够分清什么是Hits、什么是索引、什么是桶、什么是聚合就很快能上手,当然如果以前手撸过Mango 的查询的话可能比较更好处理,因为API的分类比较类似。

这里先附上他们的文档链接:https://www.elastic.co/guide/en/elasticsearch/reference/index.html

这里我检举一下SpringBoot项目的引入流程(仅限版本6.7.0,ES版本不同可能会有一些异样,请注意版本):
1. 引入依赖

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>6.7.0</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>6.7.0</version>
</dependency>
  1. 配置连接
@Bean(destroyMethod = "close") // 这个close是调用RestHighLevelClient中的close
@Scope("singleton")
public RestHighLevelClient createInstance() {
    log.info("!!!! es !!!!!" + host + " username " + username);

    try {
        String[] hosts = host.split(",");
        HttpHost[] httpHosts = new HttpHost[hosts.length];
        for (int i = 0; i < httpHosts.length; i++) {
            String h = hosts[i];
            httpHosts[i] = new HttpHost(h.split(":")[0], Integer.parseInt(h.split(":")[1]), "http");
        }
        RestClientBuilder build =
            RestClient.builder(httpHosts).setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
                @Override
                public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
                    return requestConfigBuilder;
                }
            });

        if (!StringUtils.isEmpty(username)) {
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
            build.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                @Override
                public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                    httpAsyncClientBuilder.setMaxConnTotal(maxConnTotal); //最大连接数 默认30
                    httpAsyncClientBuilder.setMaxConnPerRoute(maxConnPerRoute);// 最大路由连接数 默认10
                    httpAsyncClientBuilder.disableAuthCaching();
                    return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                }
            });
        } else {
            build.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                @Override
                public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                    httpAsyncClientBuilder.setMaxConnTotal(maxConnTotal); //最大连接数 默认30;
                    httpAsyncClientBuilder.setMaxConnPerRoute(maxConnPerRoute);// 最大路由连接数 默认10
                    return httpAsyncClientBuilder;
                }
            });
            restHighLevelClient = new RestHighLevelClient(RestClient.builder(httpHosts));
        }
        restHighLevelClient = new RestHighLevelClient(build);
    } catch (Exception e) {
        log.error("", e);
        return null;
    }
    return restHighLevelClient;
}
  1. 通过Rest发起查询/变更请求
public class ElasticsearchTemplate<T, M> {
    @Autowired
    RestHighLevelClient client;


    public Response request(Request request) throws Exception {
        Response response = client.getLowLevelClient().performRequest(request);
        return response;
    }

    public SearchResponse search(SearchRequest searchRequest) throws IOException {
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        return searchResponse;
    }
}

当然这个依赖下封装了很多的工具类和包装类,其查询的语法总体上比较直译,但是包比较多,单独拉一起说一下各种查询语法。

当然 希望下期快一点。

参考文章

全文搜索引擎 ElasticSearch 还是 Solr?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注