ElasticSearch 8.x 教程
ElasticSearch 8.x 教程
ElasticSearch 概述
-
Lucene 是什么?
- Lucene 是一套信息检索工具包,jar 包,不包含搜索引擎系统
- 包含:索引结构,读写索引的工作,排序、搜索规则工具类
-
ElasticSearch (es) 是什么?
-
一个开源的高扩展的分布式的全文搜索引擎
-
可以近乎实时存储、检索数据
-
使用 java 开发并使用 Lucene 作为核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 REST-ful 的 API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单
-
Solor 和 ElasticSearch 的区别
- Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能
- Solr 支持更多格式的数据,比 JSON、XML、CSV,而 Elasticsearch 仅支持 json 文件格式
- Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要 kibana 友好支撑
- Solr 查询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用
- ES 建立索引快(即查询慢),即实时性查询快,用于 facebook 新浪等搜索
- Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用
- Solr 比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而 Elasticsearch 相对开发维护者较少,更新太快,学习使用成本较高
ELK 安装
ELK 是 Elasticsearch、Logstash、Kibana 三大开源框架首字母大写简称。市面上也被成为 Elastic Stack。
- Elasticsearch 是一个基于 Lucene、分布式、通过 Restful 方式进行交互的近实时搜索平台框架。
- Logstash 是 ELK 的中央数据流引擎,用于从不同目标(文件 / 数据存储 / MQ)收集的不同格式数据,经过过滤后支持输出到不同的目的地(文件 / MQ / redis / elasticsearch / kafka 等)。
- Kibana 是一个针对 Elasticsearch 的开源分析及可视化平台,用来搜索、查看交互存储在 es 索引中的数据,提供实时分析的功能。它操作简单,基于浏览器的用户界面可以快速创建仪表板 dashboard,实时显示查询动态。
市面上很多开发只要一提到 ELK 能够一致说出它是一个日志分析架构技术栈总称,但实际上 ELK 不仅仅适用于日志分析,它还可以支持其他任何数据分析和收集的场景。
windows 下安装 Elasticsearch
- 最新版本下载地址:Download Elasticsearch | Elastic
- 熟悉目录:
bin:启动文件
config:配置文件
log4j2:日志配置文件
jvm.options:java虚拟机相关的配置
elasticsearch.yml:elasticsearch的配置文件!默认9200端口!跨域!
lib:相关jar包
logs:日志
modules:功能模块
plugins:插件!
- 启动:进入解压后的目录,运行下面的命令:
./bin/elasticsearch
# 如果这时报错 "max virtual memory areas vm.max *map*count [65530] is too low"
sudo sysctl -w vm.max_map_count=262144
- 访问
localhost:9200
curl localhost:9200
默认情况下,Elastic 只允许本机访问,如果需要远程访问,可以修改 Elastic 安装目录的
config/elasticsearch.yml文件,去掉network.host的注释,将它的值改成0.0.0.0,然后重新启动 Elastic。线上服务不要这样设置,要设成具体的 IP。
Docker-Compose 安装 ELK
- 创建
docker-compose.yaml文件:注意 ES 和 Kibana 的版本要一致
version: "3.9"
services:
elasticsearch:
image: elasticsearch:8.7.1
container_name: elasticsearch
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- xpack.security.enabled=false
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- target: 9200
published: 9200
networks:
- elastic
kibana:
image: kibana:8.7.1
ports:
- target: 5601
published: 5601
depends_on:
- elasticsearch
networks:
- elastic
volumes:
es_data:
driver: local
networks:
elastic:
name: elastic
driver: bridge
-
在该文件所在目录,执行:
docker-compose up -
访问
http://localhost:9200/,返回:
6ab0dc860d1",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "YE5ZOkzbQoejhluBtOT_eQ",
"version" : {
"number" : "8.7.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "f229ed3f893a515d590d0f39b05f68913e2d9b53",
"build_date" : "2023-04-27T04:33:42.127815583Z",
"build_snapshot" : false,
"lucene_version" : "9.5.0",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
-
访问
http://localhost:5601/app/home#/得到 Elastic 的可视化界面 -
如果容器意外关闭的话:
-
服务器版:可以使用
docker logs -f 容器id查看日志 -
桌面版:点击
view details进入 详情页
-
-
如果不再使用这些容器,可以删除:
docker-compose down -
重启 windows 前请务必先关掉容器
ES 的核心概念
集群,节点,索引,类型,文档,分片,映射是什么?
基本概念
Node 与 Cluster
Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。
单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
Index
Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。
所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。它是单个数据库的同义词。每个 Index (即数据库)的名字必须是小写。
下面的命令可以查看当前节点的所有 Index:
curl -X GET 'http://localhost:9200/_cat/indices?v'
Document
Index 里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。
Document 使用 JSON 格式表示,下面是一个例子。
{
"user": "张三",
"title": "工程师",
"desc": "数据库管理"
}
同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。
关系行数据库和 Elasticsearch 对比
Elasticsearch 面向文档:
| Relational DB | ElasticSearch |
|---|---|
| 数据库 (database) | 索引 (indices) |
| 表 (tables) | types(弃用) |
| 行 (rows) | documents |
| 字段 (columns) | fields |
Elasticsearch (集群) 中可以包含多个索引 (数据库),每个索引中可以包含多个类型 (表),每个类型下又包含多个文档 (行),每个文档中又包含多个字段 (列)。
物理设计
ES 在后台把每个索引划分为多个分片,每个分片可以在集群中的不同服务器间迁移。一个人就是一个集群,默认的集群名 elasticsearch。
节点和分片如何工作
一个集群至少有一个节点,而一个节点就是一个 es 进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有 5 个分片(primary shard)构成,每一个主分片会有一个副本(replica shard)

上图是一个有 3 个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个 Lucene 索引,一个包含倒排索引的文件目录,倒排索引的结构使得 es 在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。
什么是倒排索引?
比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构:

如果我们需要搜索含有 python 标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章 ID 即可。
逻辑设计
一个索引类型中,包含多个文档,比如说文档 1、文档 2。当我们索引一篇文档时,可以通过这样的一个顺序找到它:索引 \(\to\) 类型 \(\to\) 文档 ID,通过这个组合我们就能索引到某个具体的文档。
注意:ID 不必是整数,实际上它是个字符串。
- 文档:es 是面向文档的,索引和搜索数据的最小单位是文档,文档有如下属性:
- 自我包含:一篇文档同时包含字段和对应的值,也就是同时包含
key:value - 可以是层次型的:个文档中包含自文档,就是一个 json 对象
- 灵活的结构:文档不依赖于预先定义的模式。在关系数据库中,要提前定义字段才能用;在 es 中,字段是非常灵活的,可以动态添加一个新的字段
- 自我包含:一篇文档同时包含字段和对应的值,也就是同时包含
- 类型:类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如 name 映射为字符串类型。
- 索引:也就是数据库,索引是映射类型的容器,es 的索引是一个非常大的文档集合,索引存储了映射类型的字段和其他设置,然后将其存储在各个分片上
在 es 中安装插件
以 elasticsearch-analysis-ik 分词器为例:
安装
方法一:直接进入容器下载
# 1. 进入运行中的容器
docker exec -it 容器id bash
# 2. 安装插件
bin/elasticsearch-plugin install https://github.com/ElisaMin/elasticsearch-analysis-ik/releases/download/8.7.1-17/elasticsearch-analysis-ik-8.7.1.zip
# 3. 查看插件是否安装成功
bin/elasticsearch-plugin list
# 3. 退出容器
exit
# 4. 重启容器
docker restart 容器id
方法二:本地下载
# 1. 下载插件安装包,然后将其拷贝到容器中
docker cp D:\Software\Elasticsearch\Docker\plugins\elasticsearch-analysis-ik-8.7.1.zip 容器id:/tmp/
# 2. 进入运行中的容器
docker exec -it 容器id bash
# 3. 安装插件
bin/elasticsearch-plugin install file:///tmp/elasticsearch-analysis-ik-8.7.1.zip
使用
- 进入 [Console - Dev Tools - Elastic]
- 在控制台输入:其中
ik_smart为最少切分;ik_max_word为最细粒度划分
GET _analyze
{
"analyzer": "ik_smart",
"text": "北京邮电大学"
}
GET _analyze
{
"analyzer": "ik_max_word",
"text": "北京邮电大学"
}
restful 风格说明
restful 风格是一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
基本 rest 说明:
| method | url 地址 | 描述 |
|---|---|---|
| PUT | localhost:9200 / 索引名称 / 类型名称 / 文档 id | 创建文档(指定文档 id) |
| POST | localhost:9200 / 索引名称 / 类型名称 | 创建文档(随机文档 id) |
| POST | localhost:9200 / 索引名称 / 类型名称 / 文档 id/_update | 修改文档 |
| DELETE | localhost:9200 / 索引名称 / 类型名称 / 文档 id | 删除文档 |
| GET | localhost:9200 / 索引名称 / 类型名称 / 文档 id | 查询文档通过文档 id |
| POST | localhost:9200 / 索引名称 / 类型名称 /_search | 查询所有数据 |
关于索引的基本操作
- 创建一个索引
# ElasticSearch 的 7.x.x 版本
PUT /索引名/类型/文档id
{
请求体
}
# ElasticSearch 的 8.x.x 版本,类型已经弃用,默认写_doc
PUT /索引名/_doc/文档id
{
请求体
}
# 举个栗子
PUT /test1/_doc/1
{
"name": "locke",
"age": 3,
"birthday": "1997-04-06"
}
Kibana 返回:
{
"_index": "test1",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
官网类型文档地址
# 字符串类型
text、keyword
# 数值类型
long,integer,short,byte,double,float,half float,scaled float
# 日期类型
date
# te布尔值类型
boolean·
# 二进制类型
binary
- 创建具体的索引规则:
PUT test2/
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "long"
},
"birthday": {
"type": "date"
}
}
}
}
Kibana 返回:
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "test2"
}
- 获得索引规则
GET test2
如果自己的文档字段没有指定,那么 es 就会给我们默认配置字段类型!
- 扩展命令
GET _cat/health # 查看健康值
GET _cat/indices?v # 查看所有东西的版本信息
- 修改:POST
POST test1/_update/1
{
"doc": {
"name": "lockegogo"
}
}
- 删除:DELETE,根据请求来判断是删除索引还是删除文档
DELETE test1
关于文档的基本操作
基本操作
# 增
PUT /test/_doc/1
{
"name": "locke",
"age": 26,
"desc": "喜欢大熊猫花花",
"tags": ["圆滚滚","可爱","三角形"]
}
PUT /test/_doc/2
{
"name": "feifei",
"age": 28,
"desc": "喜欢大熊猫福宝",
"tags": ["活泼","调皮","花园杀手"]
}
# 查
GET /test/_doc/1
GET /test/_doc/2
# 改
POST /test/_update/1
{
"doc": {
"name": "locke"
}
}
简单操作:条件查询
如果有多条记录,匹配度越高,分数越高:
# 支持模糊查询
GET /test/_search?q=name:locke
复杂操作
- 排序 & 分页
# "_source": 过滤字段查询
# "sort": 排序
# "from": 分页查询,从第几条数据开始查询
# "size": 返回多少数据
GET test/_search
{
"query": {
"match": {
"name": "feifei"
}
},
"_source": ["name", "desc"],
"sort": [
{
"age": {
"order": "desc"
}
}
],
"from": 0,
"size": 1
}
- 布尔值查询
- must = and:既符合 A 也符合 B
- should = or:符合 A 或符合 B
- must_not = not:不符合 A 或不符合 B
GET test/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "feifei"
}
},
{
"match": {
"age": "29"
}
}
]
}
}
}
- 过滤查询:
filter
GET test/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "feifei"
}
}
],
"filter": [
{
"range": {
"age": {
"gte": 10,
"lte": 30
}
}
}
]
}
}
}
- 匹配多个条件:多个条件使用空格隔开,只要满足一个条件就可以被查出,可以通过分值进行基本判断
# 匹配多个条件
GET test/_search
{
"query": {
"match": {
"tags": "活泼 花园杀手 三角形"
}
}
}
- 精确查询:term 查询是直接通过倒排索引指定的词条进行精确查找
- term:直接查询精确的,两种情况:text or keyword
- match:会使用分词器解析,先分析文档,然后在通过分析的文档进行查询
# 1. 创建数据
PUT testdb
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"desc": {
"type": "keyword"
}
}
}
}
PUT testdb/_doc/1
{
"name": "java name",
"desc": "java desc"
}
PUT testdb/_doc/2
{
"name": "java name",
"desc": "java desc2"
}
# 2. 分析数据,查看结果
# 2.1 没有被拆分
GET _analyze
{
"analyzer": "keyword",
"text": "java name"
}
# 2.2 被拆分了
GET _analyze
{
"analyzer": "standard",
"text": "java name"
}
# 3. 查看数据:keyword 类型的字段不会被分词器解析
# 3.1 两条记录都可以查到
GET testdb/_search
{
"query": {
"match": {
"name": "java"
}
}
}
# 3.2 只能查到一条,因为 keyword 不可拆分
GET testdb/_search
{
"query": {
"match": {
"desc": "java desc"
}
}
}
# term
GET testdb/_search
{
"query": {
"term": {
"desc": "java desc"
}
}
}
- 高亮查询
# "pre_tags" + "post_tags": 自定义高亮条件
GET testdb/_search
{
"query": {
"match": {
"desc": "java desc"
}
},
"highlight": {
"pre_tags": "<p class='key' style='color:red'>",
"post_tags": "</p>",
"fields": {
"desc": {}
}
}
}
集成 SpringBoot
Installation
- 找文档
- 找到原生的依赖
<project>
<dependencies>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.7.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
</dependencies>
</project>
If you get ClassNotFoundException: jakarta.json.spi.JsonProvider:add
<project>
<dependencies>
...
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>
Connecting
Java API 客户端围绕三个主要组件进行了结构化设计:
-
API 客户端类:它们提供了强类型的数据结构和用于 Elasticsearch API 的方法。由于 Elasticsearch API 较大,它被组织成命名空间,每个命名空间都有自己的客户端类。Elasticsearch 核心功能在 ElasticsearchClient 类中实现。
-
JSON 对象映射器:它将您的应用程序类映射到 JSON,并与 API 客户端无缝集成。
-
传输层实现:这是处理所有 HTTP 请求的地方。
下面的代码片段创建并将这三个组件连接在一起:
// Create the low-level client
RestClient restClient = RestClient.builder(
new HttpHost("localhost", 9200)).build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
// And create the API client
ElasticsearchClient client = new ElasticsearchClient(transport);
尝试连接:
SearchResponse<Product> search = client.search(s -> s
.index("products")
.query(q -> q
.term(t -> t
.field("name")
.value(v -> v.stringValue("bicycle"))
)),
Product.class);
for (Hit<Product> hit: search.hits().hits()) {
processProduct(hit.source());
}
实战:京东
参考资料
-
安装并运行 Elasticsearch | Elasticsearch: 权威指南 | Elastic
-
【狂神说 Java】ElasticSearch7.6.x 最新完整教程通俗易懂
-
【狂神说 Java】ElasticSearch7.6.x
-
全文搜索引擎 Elasticsearch 入门教程 - 阮一峰的网络日志 (ruanyifeng.com)