-
前言
由于需要兼容服务器上部署5.4.3版本的ElasticSearch,因此ElasticSearch的版本也必须要5.X,同时SpringBoot也要升级到2.X。
重点来了,暂时不想升级SpringBoot的版本,所以只能不使用spring-boot-starter-data-elasticsearch的jar包来集成ElasticSearch。改用TransportClient来集成!
-
jar包
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>5.4.3</version> <exclusions> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>5.4.3</version> </dependency>
记录一个大坑: Caused by: java.lang.ClassNotFoundException: org.elasticsearch.plugins.NetworkPlugin
切记一定要把包放到ClassPath根路径下,不然他扫不到NetworkPlugin!!!
-
配置
## 集群名(默认是elasticsearch) elasticsearch.cluster-name=elasticsearch ## 集群节点地址列表,用逗号分隔 elasticsearch.cluster-nodes=ip:9300 ## 是否嗅探集群状态(默认false) elasticsearch.sniff=true ## 是否忽略集群名认证 elasticsearch.ignore-cluster-name=true ## 等待Ping命令返回结果的时间(默认5秒) elasticsearch.ping-timeout=5 ## 节点之间互相ping、检测的时间间隔 elasticsearch.nodes-sampler-interval=5
启动时初始化客户端:
-
配置注意点
client.transport.sniff这个配置需要额外关注。当打开这个配置的时候,客户端会去嗅探(sniff)整个集群的状态。此时如果集群中有其他的机器加入进来,客户端会自动发现这个加入集群的机器,不用手动设置就可以自动连接到客户端。
此时注意集群必须在同一个网段,他默认5秒会自动检测一次,加入新节点,去掉坏节点。
-
配置可能出错点
当sniff开关打开的时候,如果报了这个错误:None of the configured nodes are available: [{#transport#-1}{VTss4h8SRsCpP6EW5PoLrQ}
这时候就要检查下网络,如果你的客户端的网络是内网,然后又经过外网去访问了内网的ES,这时候就会报这个错误!!!
请看官文所说:
官方文档: https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.4/transport-client.html
- ES操作
-
增加索引
// 新增 IndexResponse response = client.prepareIndex(indexName, indexType, id) .setSource(entity, XContentType.JSON).execute().actionGet(); // 校验 if (!response.status().equals(RestStatus.CREATED)) { // ... }
注意: 如果字段为Null,Es字段栏并不会增加该字段。这里可以将value为Null的过滤成空字符串即可!
-
根据Id删除索引
// 删除 DeleteResponse response = client.prepareDelete(indexName, indexType, id).get(); if (!response.status().equals(RestStatus.OK)) { // ... }
-
根据Id查询索引
// 查询 GetResponse response = client.prepareGet(indexName, indexType, id) .setOperationThreaded(false) .get(); if (StringUtils.hasText(response.getSourceAsString())) { // ... }
注意: setOperationThreaded(false) ,先来看官方文档
他的意思是默认是true。当true的时候他将用其他的线程去查询,当false的时候他会用该调用线程去查询。但是不管是哪种方法,这个API都是用异步实现的。
-
更新索引
UpdateRequest updateRequest = new UpdateRequest(); updateRequest.index(indexName); updateRequest.type(indexType); updateRequest.id(id); // 加版本号控制 updateRequest.version(data.getVersion()); updateRequest.doc(entity, XContentType.JSON); UpdateResponse response = client.update(updateRequest).get(); if (!response.status().equals(RestStatus.OK)) { // .... }
每一次更新数据,这一行数据的version就会加1;如果每次更新相同的数据,版本并不会增加!
-
批量新增
// 构建 BulkRequestBuilder bulkRequest = client.prepareBulk(); for (String entity : list) { bulkRequest.add(client.prepareIndex(indexName, indexType, JSON.parseObject(entity).get("id").toString()) .setSource(entity, XContentType.JSON)); } BulkResponse bulkResponse = bulkRequest.get(); // 判断 if (bulkResponse.hasFailures()) { // .... }
由于BulkRequest会将数据保存内存中,所以批量插入的数据量不能过大,一般是在1000到5000条之间。大小一般在5到15MB之间。具体提数值要在自己项目的环境中不断尝试,每个环境都会不一样!
bulk引起的异常:https://www.jianshu.com/p/d4f7a6d58008 可以看下这篇文章,对于优化非常有帮助!
- 批量处理器
官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.6/java-docs-bulk-processor.html
上述参数解释
BulkActions:一次性提交的请求数,默认1000
BulkSize:达到多少文件大小后提交请求,默认5MB
FlushInterval:每多少时间提交一次请求,默认不设置。当打开这个配置的时候,BulkActions和BulkSize都要靠边
ConcurrentRequests:支持用多少并发来请求,默认1
BackoffPolicy:重试策略,默认重试8次,启动延迟时间50ms,总等待时间5.1s
同时注意的是ConcurrentRequests若为0的时候,他接受请求和发出请求是同步的。当不为0的时候,接受和发出请求是异步的!
在使用过程中总结出来推荐使用单例,对服务资源状态占用较佳。同时注意当使用成单例时,不要使用完对其进行close操作!
7.总条数查询
// 查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 构建
buildQueryBuilder(boolQueryBuilder, json);
SearchHits searchHits = client.prepareSearch(indexName).setTypes(indexType)
.setQuery(boolQueryBuilder)
.setSize(0)
.get().getHits();
只要将查询的条数设置为0,他默认会将搜索类型search_type改为count,他会跳过fetch阶段,对只需要获取查询总条数的场景非常实用。能用极低的内存,不用查询出真实数据来得知数据总条数。
-
分页查询
// 查询 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); // 构建 buildQueryBuilder(boolQueryBuilder, json); int formSize = (pageDTO.getPageNum() - 1) * pageDTO.getPageSize(); SearchRequestBuilder searchRequestBuilder = client.prepareSearch(indexName).setTypes(indexType) .setQuery(boolQueryBuilder) .setFrom(formSize) .setSize(pageDTO.getPageSize()); // 结果 SearchHits searchHits = searchRequestBuilder.get().getHits(); if (searchHits.getTotalHits() == 0) { .... }
特别注意的是这里虽然是分页查询,没有全查。但是ES的机制就是会将所有的数据都查出来,然后再按传进来的分信息页来切割数据返还给你。所以换句话来说,如果总数据有10条,每页1条,不管你取第一页还是最后一页,都是要将这10条数据查出来。
同时单次查询最大的数据量取决于ES的Index.max_result_window的这个配置。默认是10000.也就是说如果单次查询超过了这个数,就会报错。这就是暴露ES的一个问题就是他对深度分页搜索支持性很低,越深度的分页查询很依赖max_result_window参数的上限。但是又不能无限加大这个值,这个值越大ES在查询大量数据所占内存就会越大。所以我自己也找了3种解决方案。
- 滚动查询
官网地址:https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.6/java-search-scrolling.html
// 第一次查询
SearchResponse scrollResp = searchRequestBuilder
.setScroll(new TimeValue(60000)))
.setSize(10000).get();
// 开始滚动查询
List<String> resultList = new ArrayList<>();
do {
for (SearchHit hit : scrollResp.getHits().getHits()) {
resultList.add(hit.getSourceAsString());
}
// 继续滚动
scrollResp = client.prepareSearchScroll(scrollResp.getScrollId())
.setScroll(new TimeValue(60000)
.execute().actionGet();
} while(scrollResp.getHits().getHits().length != 0);
滚动查询不太适合实时查询,比较适合后台大批量的查询或者导出使用。
他相当于先查了一次,es会将所有满足条件的数据缓存起来,保存一个视图快照,然后接下去我们只要拿着第一次查询的ScrollId为凭证,不断的去es查询接下来的数据,直到所有数据查完。由于我们是从快照取数据,因此就算我们在滚动取数据的过程中有新的数据增加和更新也不会对我们此次操作有影响。
注意的是虽然我们可以用滚动查询查询出所有的数据,但是单次取得数据量还是和max_result_window配置是挂钩的哦!
- 滚动查询升级版
上面的滚动查询已经速度不错了,但是如果使用scroll-scan的话,查询速度会更快!!但是缺点就是这种查询ES不能进行排序,只能查出来之后我们自己进行排序。
scroll-scan未完待续。。。
若有错误,恳请指正,万分感谢!!!