一、Elastic Search 基础
1.1 什么是ElasticSearch
Elasticsearch 是位于 Elastic Stack 核心的分布式搜索和分析引擎, 是一个基于 Apache Lucene (TM),分布式的使用 REST 接口的搜索引擎,Elasticsearch 不仅仅是 Lucene 和全文搜索引擎,它还提供:
- 分布式的实时文件存储,每个字段都被索引并可被搜索
- 实时分析的分布式搜索引擎
- 可以扩展到上百台服务器,处理 PB 级结构化或非结构化数据
1.2 基本概念
- 索引index: 类似于数据库的表
- 类型type:类似于表的逻辑类型,用于区分不同的索引类型
- 文档document:类似于数据库的记录行,是我们搜索的核心目标,以Json数据格式存在,是可以被索引的一个基础数据单位
- 字段fields:类似于数据库的列,是文档的一个个不同的属性
- 映射mapping:相当于表结构的定义,如有哪些字段、是什么数据类型等
- 近实时NRT:near real time,当创建了新的文档或索引的时候,如果用户请求搜索,可以以接近实时的时间返回搜索结果。
- 节点node:即分布式架构下的一个服务器节点
- shard replica:数据分片与备份的概念
1.3 倒排索引
一般索引是以key寻找value,倒排索引则相反,提取value中的各个属性值(关键字),由关键字为依据去返回包含这些关键字的记录的key,与常规索引相反,因此称为倒排索引。
二、Elastic Search 安装与配置
环境:MacOS Monterey
版本:为了方便开发的其他部分选用了6.8.6版本
2.1 安装ES
官网链接。直接到官网下载对应系统和版本的包,注意ES的版本比较敏感,需要对应好后端的Spring Boot的版本进行下载。
启动前需要进入config目录下,修改elasticsearch.yml文件中的部分设置,首先是集群名和节点名:
# ---------------------------------- Cluster -----------------------------------
#
# Use a descriptive name for your cluster:
#
cluster.name: xxx-cluster
#
# ------------------------------------ Node ------------------------------------
#
# Use a descriptive name for the node:
#
node.name: xxx-node
然后是日志和数据的地址:
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
path.data: /Users/lr/Workspace/software/es/data
#
# Path to log files:
#
path.logs: /Users/lr/Workspace/software/es/data/log
#
最后是IP和端口:
# Set the bind address to a specific IP (IPv4 or IPv6):
#
network.host: 0.0.0.0
#
# Set a custom port for HTTP:
#
# http.port: 9200 端口默认是9200
主要是这三部分,如果有额外需要再添加。
2.2 启动ES
通过bin目录下的可执行文件elasticsearch
即可启动服务,控制台可以看到日志输出,添加参数-d
后台启动。
在MacOS下启动遇到了一些与jvm相关的问题,需要到config目录下修改jvm.options
文件
首先是与GC相关的,不知道为什么原始配置不能启动,网上求解无果后尝试修改了GC,绕开了报错:
## G1GC Configuration
# NOTE: G1 GC is only supported on JDK version 10 or later
# to use G1GC, uncomment the next two lines and update the version on the
# following three lines to your version of the JDK
# 10-13:-XX:-UseConcMarkSweepGC
# 10-13:-XX:-UseCMSInitiatingOccupancyOnly
14-:-XX:+UseG1GC
14-:-XX:G1ReservePercent=25
14-:-XX:InitiatingHeapOccupancyPercent=30
14-:-XX:+UseG1GC 这个部分原来没记错的话应该是-XX:+UseConcMarkSweepGC
,但是原始的配置没法启动。
另一个是在该配置文件末尾:
# temporary workaround for C2 bug with JDK 10 on hardware with AVX-512
10-:-XX:UseAVX=2
启动时提示是未知的配置项,注释掉之后能正常启动
2.3 Chrome 插件监控ES
使用的插件名为Multi ElasticSearch Head
,如果ES正确启动,打开该插件可以看到这样的画面:
上面有包括host、ip和版本的信息,右上角有集群的健康情况。通过插件还可以直接添加和删除索引。
2.4 分析器的使用
ES内置了五种分词器:
- standard:默认的分词方式,单词会被独立出来,全部转换为小写
- simple:按照非字母分词,全部转换为小写
- whitespace:按照空格分词,大小写保持
- stop:去除无意义的单词,如the, a, an等
- keyword:不做分词,把整个文本作为一个独立的关键词
standard和simple的区别在于,standard会按照空格、标点等进行拆分,而simple则会在一切非字母的字符处做拆分,如
don't
在standard下是一个整体,而在simple中会从'
处拆分为两个部分。
内置的五种分词器均不支持中文分词,因此这里需要安装ik分词器,安装过程简单此处不详细叙述,下载对应版本的ik,解压到es的插件目录并重启es即可。
2.5 向ES发送分析请求
其中analyzer指定了使用的分词器,text则是分析的内容。发送请求得到的结果如下:
{
"tokens": [
{
"token": "今天",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "的",
"start_offset": 2,
"end_offset": 3,
"type": "CN_CHAR",
"position": 1
},
{
"token": "风儿",
"start_offset": 3,
"end_offset": 5,
"type": "CN_WORD",
"position": 2
},
{
"token": "甚是",
"start_offset": 5,
"end_offset": 7,
"type": "CN_WORD",
"position": 3
},
{
"token": "喧嚣",
"start_offset": 7,
"end_offset": 9,
"type": "CN_WORD",
"position": 4
}
]
}
三、Spring Boot整合ES
3.1 依赖
pom.xml
增添如下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
这里我还尝试了以下HighLevelRestClient
,再添加以下依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
注意HighLevelRestClient的版本与ES保持一致。
3.2 配置
application.yml
文件添加如下配置,这些配置在后续版本中是弃用的,但是6.8.6版本仍然可以使用:
data:
elasticsearch:
cluster-name: laros-elasticsearch # es版本6.8.6可以继续使用该形式
cluster-nodes: 'localhost:9300'
这里注意ES与http的交互接口是9200,但是默认与Java交互的接口是9300(来自某篇被遗忘的博客),之前曾因为配置写了9200而无法连接。
3.3 Spring Boot配置类
如果要使用RestHighLevelClient
,则需要编写如下配置类:
@Configuration
public class ESConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
return new RestHighLevelClient(
RestClient.builder(new HttpHost("127.0.0.1", 9200, "http"))
);
}
}
3.4 发送请求建立索引
这里跟随教程使用了ElasticsearchTemplate
并自己又尝试用了RestHighLevelClient
。先简单做个尝试,后续再深入研究。
首先是需要一个索引对应的module类:
@Document(indexName = "EsIdx", type = "_doc") // 7.x版本后不再支持type属性
public class EsIdx {
@Id
Long indexId;
@Field
private String indexName;
@Field
private Integer counts;
@Field
private Float price;
@Field
private String infos;
记得添加getter和setter,以及构造器等。然后写一个Controller把两个Bean注入进去,部分注解省略了
public class HelloController {
private final ElasticsearchTemplate esTemp;
private final RestHighLevelClient esClient;
}
首先是使用RestHighLevelClient
,操作过程如下:
@RequestMapping("/createIndex")
public Object createIndex(){
EsIdx idx = new EsIdx(1001L, "测试索引", 10, 20.9f, "这是一条测试索引");
String jsonIdx = JsonUtils.objectToJson(idx);
IndexRequest req = new IndexRequest("stu", "EsIdx", "1");
req.source(jsonIdx, XContentType.JSON);
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.add(req);
try {
esClient.bulk(bulkRequest, RequestOptions.DEFAULT);
return JsonResultObject.ok();![在这里插入图片描述](https://img-blog.csdnimg.cn/523c1688c35742ed8f62c9b2f4950d21.png#pic_center)
} catch (IOException e) {
e.printStackTrace();
return JsonResultObject.errorMsg(e.toString());
}
}
这里使用时遇见过一个错误:IndexRequest的source方法是个有重载的方法,使用JSON数据格式时要有第二个参数来指定类型,否则会报错。
第二种:
@RequestMapping("/tempCreateIndex")
public Object tempCreateIndex(){
esTemp.createIndex(EsIdx.class);
return JsonResultObject.ok();
}
看起来会简单不少,但是也少了很多可以控制的东西,实际学习过程中发现,这里createIndex会报错,提示索引名要全小写,一开始猜测是与module类上的注解有关,但是实际修改测试后发现并不是。因为时间比较紧张这里先挖个坑,暂时使用第一种方式继续学习,后续有时间再回头研究两者的区别和正确的使用方法。
3.5 测试
发送请求到对应的接口,返回200说明成功
然后到Chrome的插件查看是不是有新数据进来了