Elasticsearch-基础及使用
Elasticsearch 基础及使用
Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。
单个 Elastic 实例称为一个节点(node),一组节点构成一个集群(cluster)
Elasticsearch 中文官网
https://www.elastic.co/cn/
全文搜索引擎 Elasticsearch 入门教程
https://www.ruanyifeng.com/blog/2017/08/elasticsearch.html
https://www.cnblogs.com/jajian/p/11223992.html
Elasticsearch 基础
倒排索引
普通索引是通过id,标题等标识建立索引,只有知道这些标识才能搜索到内容;倒排索引对内容进行分词,反向建立词项->标题->id的索引,可以根据内容搜索。
Document 文档,es 中的一条记录,等于 MySQL 中的行数据,被序列化成 JSON 并存储到 Elasticsearch 中。可以对文档执行增删查改操作。
Index 索引(名词),文档的集合,一个 索引 类似于传统关系数据库中的一个 数据库,是一个存储关系型文档的地方。 索引 (index) 的复数词为 indices 或 indexes。可以对索引执行创建、删除等操作。
索引(动词),索引一个文档 就是存储一个文档到一个 索引 (名词)中以便被检索和查询。类似于 SQL 语句中的 INSERT,除了文档已存在时,新文档会替换旧文档情况之外。
Node 节点,是一个 ElasticSearch 的运行实例,具有唯一标识。一台机器上可以有多个es节点
Cluster 集群,是一个或多个节点(node)的集合,这些节点将共同拥有完整的数据,由唯一的名称标识(elasticsearch.yml的cluster.name)。
Doc1: Java is the best programming language.
Doc2: PHP is the best programming language.
Doc3: Javascript is the best programming language.
主键索引
id | content |
---|---|
Doc1 | Java is the best programming language. |
Doc2 | PHP is the best programming language. |
Doc3 | Javascript is the best programming language. |
倒排索引
Term | Doc1 | Doc2 | Doc3 |
---|---|---|---|
Java | √ | ||
is | √ | √ | √ |
the | √ | √ | √ |
best | √ | √ | √ |
programming | √ | √ | √ |
language | √ | √ | √ |
PHP | √ | ||
Javascript | √ |
TF/IDF 相关性算法
Elasticsearch 在 ES5 版本之前默认使用经典的 TF/IDF 相关性算法,ES5 之后默认使用基于概率模型的 BM25(Best Match 25)相关性算法。
Elasticsearch 允许控制每个字段的相关性算法。
https://mp.weixin.qq.com/s/kcCfP8zyGyKiOQdCOvzs_Q
https://mp.weixin.qq.com/s/wlkFkaowW6DVY9cEJzZdXg
https://en.wikipedia.org/wiki/Okapi_BM25
Elasticsearch 被黑客攻击了
ES 被黑客攻击了,存博客文章的索引被删除了,创建了个叫 read-me 的索引让付款,我平常不看也没注意。后续还是要加上安全配置。
ES被黑客攻击了
Elasticsearch 8 新特性
1、简化安全配置
首次启动 Elasticsearch 时,会自动进行以下安全配置:
为传输层和 HTTP 层生成 TLS 证书和密钥。
TLS 配置设置被写入 elasticsearch.yml。
为 elastic 用户生成密码。
为 Kibana 生成一个注册令牌。
Elasticsearch 安全功能从 6.8.0 和 7.1.0 版本开始免费开放
https://www.elastic.co/cn/blog/security-for-elasticsearch-is-now-free
2、新的 REST API
Elasticsearch 8 开始废弃 High-level REST API 和 Low-level REST API,统一使用新的 Java API Client
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/rest-api-compatibility.html
3、强化向量搜索
Elasticsearch 7.0 版本引入高维向量字段类型,7.3/7.4 版本引入向量相似度函数支持。
在 Elasticsearch 8.0 版中,对自然语言处理 (NLP) 模型的原生支持直接引入了 Elasticsearch,让矢量搜索功能更容易实现。此外,Elasticsearch 8.0 版还包含了对近似最近邻 (ANN) 搜索的原生支持,因此可以快速且大规模地比较基于矢量的查询与基于矢量的文档语料库。
4、倒排索引内部数据结构优化
Elasticsearch 8 基于 Lucene 9.0 的第一个版本。
优化了 keyword 字段类型、 match_only_text 字段类型以及在较小程度上的 text字段类型的数据结构。
该更新使得转化为 message 字段索引大小(映射为match_only_text)减少了 14.4%,磁盘占用空间总体减少了 3.5%。
SpringBoot 集成 Elasticsearch
Spring Boot 中集成 Elasticsearch 的方式:
1、使用官方的 REST Client,又分为 Low Level Rest Api/High Leve Rest Api
2、使用 Spring Data Elasticsearch, Spring Data 提供的一套方案,低版本里面使用了 TransportClient ,这个 client 已被 es 宣布废弃。但是升级到 Spring Data Elasticsearch 3.2.6 以上版本后,添加了对 High Leve Rest Client 的支持,也可以使用的。
Spring Data Elasticsearch
https://spring.io/projects/spring-data-elasticsearch
REST/Transport
Elasticsearch(ES) 有两种连接方式:transport、rest。
REST API ,端口 9200,使用基于 HTTP 的 RESTful API 进行访问。es 官方建议使用这种方式。
Transport 连接, 端口 9300,基于 TCP 连接的访问方式,TransportClient 客户端 在 7.0 版本中不建议使用,在 8.X 的版本中废弃。
Low Level Rest Api/High Leve Rest Api
ES 官方提供了两个 JAVA REST client 版本: Low Level Rest Api(低级 Rest Api)和 High Leve Rest Api(高级 Rest Api)。
所谓低级 Api 并不是功能比较弱,而是指 Api 离底层实现比较近。官方提供的低级 Api 是对原始的 Rest Api 的第一层封装。只是把 Http 调用的细节封装起来。程序还是要自己组装查询的条件字符串、解析返回的结果 json 字符串等。同时也要处理 http 协议的 各种方法、协议头等内容。兼容所有ES版本。
高级 api 是在低级 api 上的进一步封装,不用在意接口的方法,协议头,也不用人工组合调用的参数字符串,同时对返回的 json 字符串有一定的解析。使用上更方便一些。但是高级 api 并没有实现所有低级 api 实现的功能。所以如果遇到这种情况,还需要利用低级 api 来实现自己功能。使用的版本需要保持和 ES 服务端的版本一致,否则会有版本问题。
High Leve Rest Api
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html
Low Level Rest Api
https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html
Java RestHighLevelClient(ES 7及以下)
添加依赖:
<!-- elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.6.2</version>
</dependency>
<!-- elasticsearch java rest high level client -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency>
初始化Client
指定 ES 集群地址和端口创建 RestHighLevelClient
实例
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"),
new HttpHost("localhost", 9201, "http")));
high-level client 内部使用 low-level client 来进行具体的请求操作,而 low-level client 内部维护了一个连接池,所以当使用结束后,为了节省资源可以通过
client.close();
关闭连接。
Java REST Client [7.6] » Java High Level REST Client » Getting started » Initialization
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-getting-started-initialization.html
SpringBoot RestHighLevelClient 自动配置
SpringBoot 中有 RestHighLevelClient 的自动配置:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestHighLevelClient.class)
@ConditionalOnMissingBean(RestClient.class)
@EnableConfigurationProperties(ElasticsearchRestClientProperties.class)
@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class,
RestClientSnifferConfiguration.class })
public class ElasticsearchRestClientAutoConfiguration {
}
只要 classpath 中有 RestHighLevelClient 类且没有手动暴露 RestHighLevelClient Bean 就会自动给配置一个 RestHighLevelClient 实例,注入即可使用。
es 地址配置:
spring:
elasticsearch:
rest:
uris: http://10.234.161.158:8200
RestHighLevelClient 配置 es 账号密码
@Bean
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
restClientBuilder.setHttpClientConfigCallback(requestConfig -> {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("es-user-name", "es-pswd"));
requestConfig.setDefaultCredentialsProvider(credentialsProvider);
return requestConfig;
});
return new RestHighLevelClient(restClientBuilder);
}
RestHighLevelClient 报错 Connection reset
问题:
长时间未操作后,首次使用 RestHighLevelClient 请求 es 报错如下,再次请求成功
Caused by: java.io.IOException: Connection reset
at org.elasticsearch.client.RestClient.extractAndWrapCause(RestClient.java:885)
at org.elasticsearch.client.RestClient.performRequest(RestClient.java:283)
Caused by: java.net.SocketException: Connection reset
at java.base/sun.nio.ch.SocketChannelImpl.throwConnectionReset(SocketChannelImpl.java:394)
原因:
Es 的 RestHighLevelClient 的 KeepAlive(最小空闲时间)默认值是-1(在 org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy
中),长连接,表示连接永不过期,可循环重复使用。
虽然客户端保持了长链接,然而 Linux 服务器 TCP 的 Keepalive 却有着自己的超时时间:
# sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_probes net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75
若超过这个时间,且中间客户端没有操作,系统就会断开并清除TCP连接,而此时 es 客户端还依然认为自己持有的连接是有效的,发送请求就会报上面错误。
解决:
自定义 RestHighLevelClient 配置,会覆盖 RestHighLevelClientConfiguration 中 RestHighLevelClient
@Bean
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
restClientBuilder.setHttpClientConfigCallback(requestConfig -> {
requestConfig.setKeepAliveStrategy((response, context) -> TimeUnit.MINUTES.toMillis(3));
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("es-user-name", "es-pswd"));
requestConfig.setDefaultCredentialsProvider(credentialsProvider);
return requestConfig;
});
return new RestHighLevelClient(restClientBuilder);
}
解决Elasticsearch Connection reset by peer异常
https://blog.csdn.net/lzxlfly/article/details/128234591
Enable TCP keepalives by default in Java REST clients
https://github.com/elastic/elasticsearch/issues/65213
ElasticSearch: RestHighLevelClient is getting timeout exception if client API is idle for some time
https://discuss.elastic.co/t/elasticsearch-resthighlevelclient-is-getting-timeout-exception-if-client-api-is-idle-for-some-time/255442/6
RestHighLevelClient 操作
判断索引是否存在
// 判断索引是否存在
@Test
public void testIndexExist() {
GetIndexRequest request = new GetIndexRequest("article");
request.local(false);
request.humanReadable(true);
request.includeDefaults(false);
try {
boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists ? "索引存在" : "索引不存在");
} catch (IOException e) {
e.printStackTrace();
}
}
Java REST Client [7.6] » Java High Level REST Client » Index APIs » Index Exists API
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-indices-exists.html#java-rest-high-indices-exists
更新 nested 字段内容
索引 mapping 结构如下,nested_field_a 是 nested 字段,内部是对象数组,对象有 sub_field1 和 sub_field2 两个字段:
{
"properties": {
"field_a": {
"type": "keyword"
},
"field_b": {
"type": "keyword"
},
"nested_field_a": {
"type": "nested",
"properties": {
"sub_field1": {
"type": "keyword"
},
"sub_field2": {
"type": "keyword"
}
}
}
}
}
java 更新代码:
通过 XContentBuilder 对象构建 nested 数据,会完全覆盖原 nested_field_a 字段内容
也可以同时更新 field_a 这种非 nested 字段
public void updateNested() { try (XContentBuilder builder = XContentFactory.jsonBuilder()) { builder.startObject(); builder.field("field_a", "value1"); builder.field("field_b", "value2"); builder.startArray("quality_issues"); builder.startObject(); builder.field("sub_field1", "value1"); builder.field("sub_field2", "value2"); builder.endObject(); builder.startObject(); builder.field("sub_field1", "value3"); builder.field("sub_field2", "value4"); builder.endObject(); builder.endArray(); builder.endObject(); UpdateRequest updateRequest = new UpdateRequest("index1", "1").doc(builder); UpdateResponse resp = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT); log.info("update response {}", resp.getResult()); } catch (Exception e) { log.error("update failed", e); } }
Bulk Request 批量操作文档
可以单个操作文档,也可以使用 Bulk Request API 批量操作,一般我们直接使用批量操作就可以了。
Java REST Client [7.6] » Java High Level REST Client » Document APIs » Bulk API
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-document-bulk.html
IndexRequest
用于插入文档DeleteRequest
用于删除文档UpdateRequest
用于根据 ID 更新文档
Java RestHighLevelClient 操作文档实例
public class ElaticsearchRestHighLevelTest {
private RestHighLevelClient restHighLevelClient;
// 索引
private static final String INDEX_ARTICLE = "article";
@Before
public void initClient() {
restHighLevelClient = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http")));
}
// 批量添加文档
@Test
public void testBulkRequestAddDocument() {
BulkRequest bulkRequest = new BulkRequest();
Article article1 = Article.builder().pathname("/article/Interview-13-Algorithm/").title("面试准备13-算法").
content("面试准备之算法").build();
Article article2 = Article.builder().pathname("/article//article/Elasticsearch/").title("Elasticsearch使用笔记").
content("全文搜索引擎 Elasticsearch 入门教程").build();
Article article3 = Article.builder().pathname("/article/Linux-Commands/").title("Linux-常用命令").
content("top命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。").build();
bulkRequest.add(new IndexRequest(INDEX_ARTICLE).id(article1.getPathname()).source(JSONUtils.writeValue(article1), XContentType.JSON));
bulkRequest.add(new IndexRequest(INDEX_ARTICLE).id(article2.getPathname()).source(JSONUtils.writeValue(article2), XContentType.JSON));
bulkRequest.add(new IndexRequest(INDEX_ARTICLE).id(article3.getPathname()).source(JSONUtils.writeValue(article3), XContentType.JSON));
try {
BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(JSONUtils.writePrettyValue(bulkResponse));
} catch (IOException e) {
e.printStackTrace();
}
}
// 批量删除文档
@Test
public void testBulkRequestRemoveDocument() {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.add(new DeleteRequest(INDEX_ARTICLE, "u__ojXEBrN9oq5tgGcul"));
bulkRequest.add(new DeleteRequest(INDEX_ARTICLE, "vP-iknEBrN9oq5tgActC"));
try {
BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(JSONUtils.writePrettyValue(bulkResponse));
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testPhraseQuery() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("content", "Elasticsearch"));
executeQuery(searchSourceBuilder);
}
@Test
public void testStringQuery() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.queryStringQuery("top"));
executeQuery(searchSourceBuilder);
}
@Test
public void testTermQuery() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("title", "Elasticsearch使用笔记"));
executeQuery(searchSourceBuilder);
}
// 执行查询
public void executeQuery(SearchSourceBuilder searchSourceBuilder) {
// content 内容太大,不返回
searchSourceBuilder.fetchSource(null, ARTICLE_FIELD_CONTENT);
searchSourceBuilder.from(0);
searchSourceBuilder.size(10);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 高亮参数
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<span style=\"color:red\">");
highlightBuilder.postTags("</span>");
highlightBuilder.field(ARTICLE_FIELD_CONTENT);
searchSourceBuilder.highlighter(highlightBuilder);
// 搜索请求
SearchRequest searchRequest = new SearchRequest(INDEX_ARTICLE);
searchRequest.source(searchSourceBuilder);
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("查询执行时间:" + searchResponse.getTook().getMillis());
SearchHits searchHits = searchResponse.getHits();
System.out.println("结果个数:" + searchHits.getTotalHits().value);
System.out.println("结果:");
int i = 1;
for (SearchHit hit : searchHits) {
System.out.println(String.format("-------------------------- 匹配结果 %d ----------------------", i++));
System.out.println("id: " + hit.getId());
Map<String, Object> sourceMap = hit.getSourceAsMap();
System.out.println("title: " + sourceMap.get(FRONT_MATTER_TITLE));
System.out.println("postlink: " + sourceMap.get(FRONT_MATTER_POSTLINK));
System.out.println("content: " + sourceMap.get(ARTICLE_FIELD_CONTENT));
// 高亮
for (Map.Entry<String, HighlightField> entry : hit.getHighlightFields().entrySet()) {
System.out.println(
String.format("高亮字段 %s 匹配到 %d 条", entry.getKey(), entry.getValue().fragments().length));
for (Text text : entry.getValue().fragments()) {
System.out.println(String.format("高亮字段 %s, 匹配内容 %s", ARTICLE_FIELD_CONTENT, text));
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@After
public void close() {
try {
restHighLevelClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java REST Client [7.6] » Java Low Level REST Client
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low.html
Java REST Client [7.6] » Java High Level REST Client » Document APIs
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-supported-apis.html
Elastic Search Java Api
https://www.jianshu.com/p/bf21cb2bd79c
springboot整合elasticsearch(基于es7.2和官方high level client)
https://www.tapme.top/blog/detail/2019-07-29-14-59/
springboot集成ES实现磁盘文件全文检索
https://www.jianshu.com/p/7e3c6a82671b
Java 中查看 BulkRequest 的多行json内容
Java 中打断点看 BulkRequest 对应的多行 json 请求体内容:
org.elasticsearch.client.RequestConverters
cn.hutool.core.io.IoUtil
BulkRequest bulkRequest = new BulkRequest();
String bulkJsonl = IoUtil.read(RequestConverters.bulk(bulkRequest).getEntity().getContent()).toString()
Java 中查看 DeleteByQueryRequest 的json内容
DeleteByQueryRequest request = new DeleteByQueryRequest("index");
request.setQuery(new TermQueryBuilder("field", "value"));
XContentBuilder builder = XContentFactory.jsonBuilder();
deleteRequest.toXContent(builder, ToXContent.EMPTY_PARAMS);
String requestBody = Strings.toString(builder);
Java API Client(ES 8.0)
Elasticsearch 8 开始废弃 High-level REST API 和 Low-level REST API,统一使用新的 Java API Client
API 兼容性,实测可继续使用 High-level REST API 操作 Elasticsearch 8 中的文档
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/rest-api-compatibility.html
1、创建连接
Elastic Docs ›Elasticsearch Java API Client [8.6] ›Getting started
Connecting
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/connecting.html
2、批量索引文档
Elastic Docs ›Elasticsearch Java API Client [8.6] ›Using the Java API Client
Bulk: indexing multiple documentsedit
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/indexing-bulk.html
3、检索
Elastic Docs ›Elasticsearch Java API Client [8.6] ›Using the Java API Client
Searching for documents
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/searching.html
Elasticsearch 8.x Java API Client 操作文档完整实例
@Slf4j
public class ElasticsearchClientTest extends BaseElasticsearchTest {
private static ElasticsearchClient elasticsearchClient;
// 初始化 ElasticsearchClient
@BeforeAll
public static void initClient() {
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
elasticsearchClient = new ElasticsearchClient(transport);
}
// 批量添加文档测试
@Test
public void testBulkRequestAddDocument() {
bulkRequestAddDocument(buildArticleList());
}
// 批量添加文档
@Override
public void bulkRequestAddDocument(List<Article> articleList) {
if (CollectionUtils.isEmpty(articleList)) {
return;
}
try {
BulkRequest.Builder br = new BulkRequest.Builder();
articleList.forEach(article -> br.operations(
op -> op.index(idx -> idx
.index(INDEX_ARTICLE)
.id(article.getPathname())
.document(article))));
BulkResponse result = elasticsearchClient.bulk(br.build());
if (result.errors()) {
log.error("批量操作异常:");
for (BulkResponseItem item : result.items()) {
if (item.error() != null) {
log.error(item.error().reason());
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 根据ID查询文档
@Test
public void testReadById() {
try {
GetResponse<Article> response = elasticsearchClient.get(g -> g
.index(INDEX_ARTICLE)
.id("/article/Interview-15-Algorithm/"),
Article.class
);
if (response.found()) {
Article article = response.source();
log.info("Article: {}", JsonMappers.Normal.toJson(article));
} else {
log.info("Article not found");
}
} catch (IOException e) {
log.error("es get error", e);
}
}
// 检索
@Test
public void testSearch() throws Exception {
SearchResponse<Article> response = elasticsearchClient.search(searchRequestBuilder -> searchRequestBuilder
.index(INDEX_ARTICLE)
.query(queryBuilder -> queryBuilder
.match(matchQueryBuilder -> matchQueryBuilder
.field(ARTICLE_FIELD_CONTENT)
.query("Elasticsearch"))
)
.source(sourceConfigBuilder -> sourceConfigBuilder
.filter(sourceFilterBuilder -> sourceFilterBuilder
.excludes(ARTICLE_FIELD_CONTENT))) // content 内容太大,不返回
.highlight(highlightBuilder -> highlightBuilder
.fields(ARTICLE_FIELD_CONTENT, highlightFieldBuilder -> highlightFieldBuilder
.preTags("<span style=\"color:red\">")
.postTags("</span>")))
.from(0)
.size(3)
,
Article.class
);
log.info("检索匹配到 {} 条记录,耗时 {} ms", response.hits().total().value(), response.took());
int i = 1;
for (Hit<Article> hit : response.hits().hits()) {
log.info("------------------------------ 第 {} 条结果 ----------------------------", i++);
Article article = hit.source();
log.info("检索结果文档ID {}", hit.id());
log.info("检索结果完整文档: {}", JsonMappers.Normal.toJson(article));
hit.highlight().forEach((k, v) -> {
log.info("高亮字段 {} 匹配到 {} 条", k, v.size());
v.forEach(value -> log.info("高亮字段 {}, 匹配内容 {}", k, v));
});
}
}
}
Elasticsearch 8.x Java API Client 设置用户名密码(BasicAuth)
Basic authentication
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/_basic_authentication.html
public static void initClient() {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("user", "test-user-password"));
RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9200))
.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
.setDefaultCredentialsProvider(credentialsProvider));
ElasticsearchTransport transport = new RestClientTransport(builder.build(), new JacksonJsonpMapper());
elasticsearchClient = new ElasticsearchClient(transport);
}
NoClassDefFoundError: jakarta/json/JsonException
初始化 ElasticsearchTransport 报错
java.lang.NoClassDefFoundError: jakarta/json/JsonException
java.lang.NoClassDefFoundError: jakarta/json/JsonException
https://github.com/elastic/elasticsearch-java/issues/79
Elastic Docs ›Elasticsearch Java API Client [8.6] ›Getting started
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/installation.html
解决:
添加 jakarta.json-api 依赖
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
Elasticsearch 压测
压测点
1、单 doc 最多多少字段写入最优(不出问题)?写入TPS(0错误)?
2、单个 doc 查询最多支持多少字段数?查询QPS(0错误)?
3、多 index 之间 join 查询性能如何?
0-10 字段
20-50
50 及以上
4、多 index 分页排序性能?
1-3 字段
3-5
查询:1字段,5字段、10字段
排序:不排序,或者1字段 id 排序
分页:每页 20 数据,测 200 - 220 分页场景
5、单 index 记录数大于300万时,分页性能?解决方案?
6、多 index 联合查询底层实现逻辑?
7、数据初始化导入工具?做成通用的, table.col1 -> index.doc.field
8、故障处理?如何快速重建索引?
Elasticsearch 压测方案对比
一、es 官方的压测工具 rally
elastic 官方开源的一款基于 python3 实现的针对 es 的压测工具
优势:1、专用的es压测工具,有较为详细的文档。2、定义了一整套数据集(track)、操作(operation)、任务(challenge,一系列操作的组合)标准,包含压测过程管理、存储、对比工具。3、自带数据集。
劣势:1、需要一定的学习成本,对于简单的单次测试显得大材小用。
二、jmeter 压测 es http api
优势:1、有使用经验,上手容易。2、测试报告易懂
劣势:1、需要自己准备数据集。2、压测轮次较多时不方便管理,不方便对比结果
Rally 官方ES压测工具
esrally 是 elastic 官方开源的一款基于 python3 实现的针对 es 的压测工具
自动创建、压测和销毁 es 集群
可分 es 版本管理压测数据和方案
完善的压测数据展示,支持不同压测之间的数据对比分析,也可以将数据存储到指定的es中进行二次分析
支持收集 JVM 详细信息,比如内存、GC等数据来定位性能问题
环境:
Python 3.4+ 和 pip3
JDK 8
git 1.9+
概念
track 赛道,压测用的数据和测试策略,使用 git 存储
elastic / rally-tracks 自带数据集
https://github.com/elastic/rally-tracks
operations 具体的操作,操作类型 create-index, search, raw-request 原始请求
challenges 通过组合 operations 定义一系列 task,再组合成一个压测的流程
car 赛车,指 es 实例
race 比赛,指一次压测
官方压测结果
https://elasticsearch-benchmarks.elastic.co/
elastic / rally
https://github.com/elastic/rally
文档
https://esrally.readthedocs.io/en/stable/index.html#
Announcing Rally: Our benchmarking tool for Elasticsearch
https://www.elastic.co/cn/blog/announcing-rally-benchmarking-for-elasticsearch
Elasticsearch 压测方案之 esrally 简介
https://segmentfault.com/a/1190000011174694
JMeter压测 ES HTTP 接口
Elasticsearch 6.8 默认配置下,使用 JMeter 1000 线程持续 5 分钟压测,写入doc,有大约 15% 的 429/Too Many Requests es_rejected_execution_exception 错误,需要调高 Elasticsearch 的 bulk 线程池(6.8 中是 write 线程池)队列大小(不建议),或者增加结点。
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: