暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

ElasticSearch

是汐江呀 2021-02-12
412

1 ES的重要概念

1.1 ES的特点

1.2 ES和MySQL的区别

1.3 ES数据存储和搜索原理

1.4 倒排索引

1.5 ES核心概念

1.6 ik分词器

1.7 数据类型

1.7.1 简单数据类型

1.7.2 复杂数据类型

2 ESClient对象创建

3 操作索引

3.1 新建索引

3.2 删除索引

3.3 查询索引

3.4 判断索引

3.5 索引操作总结

4 操作文档

4.1 添加/修改文档

4.2 查询文档

4.3 删除文档

4.4 文档操作总结

5 数据批量操作

5.1 Bulk批量操作

5.2 批量导入

6 ES查询

6.1 matchAllQuery-- 查询所有

6.2 词条查询

6.2.1 termQuery

6.2.2 matchQuery

6.3 模糊查询

6.4 范围查询

6.5 queryString查询 -- 多字段查询

6.6 布尔查询-- 多条件查询

6.7 聚合查询

6.8 高亮查询

6.9 重建索引及别名

7 ES集群

7.1 集群的概念

7.2 集群的特点及分布式架构

7.3 Code访问集群

7.4 分片配置

7.5 路由原理

7.6 脑裂

7.6.1 脑裂出现的原因

7.6.2 脑裂的解决方案

1 ES的重要概念

1.1 ES的特点

 1.ElasticSearch是一个基于Lucene的搜索服务器,是一个分布式、高扩展、高实时的搜索与数据分析引擎,基于RESTful web接口
2.Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎
3.应用场景
3.1 搜索:海量数据的查询
3.2 日志数据分析
3.3 实时数据分析

1.2 ES和MySQL的区别

 • MySQL有事务性,而ElasticSearch没有事务性,所以删除的数据是无法恢复的。
• ElasticSearch没有物理外键这个特性,,如果数据强一致性要求比较高,还是建议慎用
• ElasticSearch和MySql分工不同,MySQL负责存储数据,ElasticSearch负责搜索数据。

1.3 ES数据存储和搜索原理

1.4 倒排索引

 1.倒排索引就是将各个文档中的内容进行分词,形成词条,然后记录词条和数据的唯一标识(id)的对应关系,形成的产物
2.通俗来讲,正向索引就是通过key找value,而倒排索引是通过value找key

1.5 ES核心概念

 • 索引(index)
ElasticSearch存储数据的地方,可以理解成关系型数据库中的数据库概念。
• 映射(mapping)
mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构。
• 文档(document
Elasticsearch中的最小数据单元,常以json格式显示。一个document相当于关系型数据库中的一行数据。
• 倒排索引(反向索引)
• 倒排索引就是将文档中的内容进行分词形成词条,然后记录词条和数据的唯一标识(id)的对
应关系形成的产物
• 通俗来讲,正向索引就是通过key找value,而倒排索引是通过value找key
• 类型(type
一种type就像一类表。如用户表、角色表等。在Elasticsearch7.X默认type为_doc
- ES 5.x中一个index可以有多种type
- ES 6.x中一个index只能有一种type
- ES 7.x以后,将逐步移除type这个概念,现在的操作已经不再使用,默认_doc

1.6 ik分词器

 • 分词器(Analyzer):将一段文本,按照一定逻辑,分析成多个词语的一种工具,ElasticSearch 内置分词器对中文很不友好,处理方式为:一个字一个词
• IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包,是一个基于Maven构建的项目,具有60万字/秒的高速处理能力,支持用户词典扩展定义
 IK提供两种分词模式:智能模式和细粒度模式
   • 智能:对应es的IK插件的ik_smart
   • 细粒度:对应es的IK插件的ik_max_word

1.7 数据类型

1.7.1 简单数据类型

1.7.2 复杂数据类型

2 ESClient对象创建

 • 在SpringBoot中定义类,将连接信息通过yaml等配置文件的形式自动进行装配
@Component
@ConfigurationProperties(prefix = "elasticsearch")
@Data
public class elasticsearchGet {

private String host;
private Integer port;

@Bean
public RestHighLevelClient client(){
return new RestHighLevelClient(RestClient.builder(
new HttpHost(
host,
port,
"http"
)
));
}
}
• application.yaml
# es
elasticsearch:
host: 192.168.153.131
port: 9200

3 操作索引

3.1 新建索引

 //  添加索引
CreateIndexRequest createRequest = new CreateIndexRequest("river3");
 // 添加映射
String source = "{\n" +
" \"properties\" : {\n" +
" \"age\" : {\n" +
" \"type\" : \"integer\"\n" +
" },\n" +
" \"name\" : {\n" +
" \"type\" : \"keyword\"\n" +
" }\n" +
" }\n" +
" }";
  createRequest.mapping(source, XContentType.JSON);
CreateIndexResponse response = client.indices().create(createRequest, RequestOptions.DEFAULT);
// 判断索引是否创建成功
System.out.println(response.isAcknowledged());    

3.2 删除索引

IndicesClient indices = client.indices();
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("river");
AcknowledgedResponse response = indices.delete(deleteIndexRequest, RequestOptions.DEFAULT);
boolean acknowledged = response.isAcknowledged();
System.out.println(acknowledged);

3.3 查询索引

IndicesClient indices = client.indices();
GetIndexRequest getIndexRequest = new GetIndexRequest("person");
GetIndexResponse response = indices.get(getIndexRequest, RequestOptions.DEFAULT);
Map<String, MappingMetaData> map = response.getMappings();
for (String str : map.keySet()) {
System.out.println(str);
System.out.println(map.get(str).sourceAsMap());
}

3.4 判断索引

IndicesClient indices = client.indices();
GetIndexRequest getIndexRequest = new GetIndexRequest("river3");
boolean exists = indices.exists(getIndexRequest, RequestOptions.DEFAULT);
System.out.println(exists);

3.5 索引操作总结

• 索引的操作核心方法在于indices,通过该方法获取一个索引客户端对象,然后通过该对象来进行一系列相关操作
• 创建 -- create() -- createRequest
• 查询 -- get() -- GetIndexRequest
• 删除 -- delete() -- DeleteIndexRequest
• 判断 -- exists() -- GetIndexRequest

4 操作文档

4.1 添加/修改文档

// id存在则修改,id不存在则添加
String json = JSON.toJSONString(new User(4525,"二1黑1"));
IndexRequest indexRequest = new IndexRequest("river3").id("3").source(json, XContentType.JSON);
IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(response.getPrimaryTerm());

4.2 查询文档

GetRequest request = new GetRequest("river3","1");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
System.out.println(response.getSourceAsString();

4.3 删除文档

DeleteRequest deleteRequest = new DeleteRequest("river3","3");
DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(response.toString());

4.4 文档操作总结

• 文档的操作直接通过client客户端对象来进行
• 添加/修改使用index方法 -- IndexRequest
• 查询使用get方法 -- GetRequest
• 删除使用delete方法 -- DeleteRequest

5 数据批量操作

5.1 Bulk批量操作

• Bulk批量操作是将文档的增删改查一些列操作,通过一次请求全都做完,从而减少网络的传输次数
• 核心在于client中的bulk()方法,通过向BulkRequest中add操作对象实现批量操作
BulkRequest bulkRequest = new BulkRequest();
/*
* 1.删除6号记录
* 2.添加7号记录
* 3.修改2号记录 名称为2号
* */
// 1.删除6号记录
DeleteRequest deleteRequest = new DeleteRequest("person", "6");
bulkRequest.add(deleteRequest);
// 2.添加7号记录
Map<String, String> map = new HashMap<>();
map.put("age", "123");
map.put("name", "世界");
IndexRequest indexRequest = new IndexRequest("person").id("7").source(map);
bulkRequest.add(indexRequest);
// 3.修改2号记录 名称为2号
Map<String, String> map1 = new HashMap<>();
map1.put("name", "2号");
UpdateRequest updateRequest = new UpdateRequest("person", "2").doc(JSON.toJSONString(map1), XContentType.JSON);
bulkRequest.add(updateRequest);
BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.status());

5.2 批量导入

• 思路:从mysql中查询出数据循环遍历对象将数据添加到批量操作bulk中
// 1.从mysql中查询所有数据
List<Goods> all = goodsDao.findAll();
// 2.bulk导入
BulkRequest bulkRequest = new BulkRequest();
for (Goods goods : all) {
// 将获取到的spec数据转换为一个map集合
Map map = JSON.parseObject(goods.getSpecStr(), Map.class);
goods.setSpec(map);
// 将每个goods对象中的数据添加到批量导入脚本中
bulkRequest.add(new IndexRequest("goods").id(goods.getId() + "").source(JSON.toJSONString(goods), XContentType.JSON));
}
// 进行批量导入
BulkResponse responses = client.bulk(bulkRequest, RequestOptions.DEFAULT);
// 查看导入状态信息
System.out.println(responses.status());

6 ES查询

6.1 matchAllQuery-- 查询所有

matchAll查询:查询所有文档
// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件
searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);
// 将查询出的数据封装到一个list集合中去
List<Goods> goods = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
Goods good = JSON.parseObject(hit.getSourceAsString(), Goods.class);
goods.add(good);
}
System.out.println(goods);

6.2  词条查询

6.2.1 termQuery

// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件
searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.termQuery("title","诺基亚")).from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);
// 将查询出的数据封装到一个list集合中去
List<Goods> goods = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
Goods good = JSON.parseObject(hit.getSourceAsString(), Goods.class);
goods.add(good);
}
System.out.println(goods);

6.2.2 matchQuery

// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件
searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.matchQuery("title","华为手机")).from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);
// 将查询出的数据封装到一个list集合中去
List<Goods> goods = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
Goods good = JSON.parseObject(hit.getSourceAsString(), Goods.class);
goods.add(good);
}
System.out.println(goods);

6.3 模糊查询

• WildCard -- 通配符方式
• Regexp -- 正则式方式
• Prefix -- 前缀匹配方式
// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件
// 1.通配符
//searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.wildcardQuery("title","华*")).from(0).size(20));
// 2.正则式
//searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.regexpQuery("title","\\w+.*")).from(0).size(20));
// 3.前缀
searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.prefixQuery("title","new8")).from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);
// 将查询出的数据封装到一个list集合中去
List<Goods> goods = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
Goods good = JSON.parseObject(hit.getSourceAsString(), Goods.class);
goods.add(good);
}
System.out.println(goods);

6.4 范围查询

 // 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件 -- 根据范围进行查询
searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.rangeQuery("price").gte(2000).lte(3000))
// 根据价格进行排序 -- 降序排列
.sort("price", SortOrder.DESC)
.from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);

6.5 queryString查询 -- 多字段查询

// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件 -- 根据一个查询内容去多个字段中进行查找
searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.queryStringQuery("华为手机").field("title").field("brandName").field("categoryName")).from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);

6.6 布尔查询 -- 多条件查询

* must -- 必须包含,计算得分
* must_not -- 必须不包含
* should -- 可以包含也可以不包含
* filter -- 必须包含,不计算得分
// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件 -- 根据多个查询内容进行查找
searchRequest.source(new SearchSourceBuilder().query(QueryBuilders.boolQuery()
// 第一个查询条件 -- 不分词进行查询
.must(QueryBuilders.matchQuery("title","华为手机"))
// 第二个查询条件 -- 根据价格进行查询
.filter(QueryBuilders.rangeQuery("price").gte(2000).lte(3000)))
// 设置当前查询页数及每页查询条数
.from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);

6.7 聚合查询

* 指标聚合 -- 相当于mysql中聚合函数
* 桶聚合 -- 相当于mysql中分组
// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件
searchRequest.source(new SearchSourceBuilder()
.query(QueryBuilders.termQuery("title","手机"))
// 聚合查询条件 -- 桶聚合
.aggregation(AggregationBuilders.terms("goods_brand").field("brandName"))
// 聚合查询条件 -- 指标聚合
.aggregation(AggregationBuilders.max("max_price").field("price"))
.from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);
// 获取聚合结果
Map<String, Aggregation> map = response.getAggregations().asMap();
System.out.println(map);
// 强转为Terms类型获取桶聚合结果
Terms terms = (Terms) map.get("goods_brand");
List<? extends Terms.Bucket> buckets = terms.getBuckets();
// 获取价格最大值
Max maxPrice = response.getAggregations().get("max_price");
System.out.println("最高价为:"+maxPrice.value());
// 遍历集合获取所有的分类数据
List brands = new ArrayList();
for (Terms.Bucket bucket : buckets) {
brands.add(bucket.getKey());
}
System.out.println(brands);

6.8 高亮查询

高亮三要素:
• 高亮字段
• 前缀
• 后缀

// 指定查询哪个索引
SearchRequest searchRequest = new SearchRequest("goods");
// 指定查询条件
searchRequest.source(new SearchSourceBuilder()
.query(QueryBuilders.termQuery("title","手机"))
// 指定高亮区域 -- 所有的手机都会被包裹在一个font标签当中
.highlighter(new HighlightBuilder().field("title").preTags("<font color=\"red\">").postTags("</font>"))
.from(0).size(20));
// 进行查询获得查询响应信息
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的总记录数
System.out.println("总记录数:" + response.getHits().getTotalHits().value);
// 获取聚合结果
SearchHits hits = response.getHits();


List<Goods> goodsList = new ArrayList<>();
for (SearchHit hit : hits) {
// 获取所有设置高亮的字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
// 获取其中名称为title的高亮字段
HighlightField highlightField = highlightFields.get("title");
// 获取文本对象
Text[] fragments = highlightField.fragments();
// 将设置高亮之后的title重新进行替换
Goods goods = JSON.parseObject(hit.getSourceAsString(), Goods.class);
goods.setTitle(fragments[0].toString());
goodsList.add(goods);
}
System.out.println(goodsList);

6.9 重建索引及别名

* 重建索引 -- ES中无法对字段类型和结构进行修改,只能添加字段无法改变字段,当需要更改时需要重新创建一个索引将原索引中数据进行迁移
* 索引别名 -- 为了避免JAVA代码因为重建索引的影响,需要对新建索引创建一个别名
* 1.删除原索引
* 2.给新建索引设置一个别名,别名名称和原索引保持一致,从而确保JAVA代码不修改即可完成业务功能变更

7 ES集群

7.1 集群的概念

7.2 集群的特点及分布式架构

• ElasticSearch 天然支持分布式
• ElasticSearch 的设计隐藏了分布式本身的复杂性

7.3 Code访问集群

• 配置多个HttpHost对象
@Bean
public RestHighLevelClient client(){
return new RestHighLevelClient(RestClient.builder(
new HttpHost(
host1,
port1,
"http"
),
new HttpHost(
host2,
port2,
"http"
),
new HttpHost(
host3,
port3,
"http"
),
new HttpHost(
host4,
port4,
"http"
)
));
}

7.4 分片配置

7.5 路由原理

7.6 脑裂

7.6.1 脑裂出现的原因

7.6.2 脑裂的解决方案

文章转载自是汐江呀,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论