Java道经第3卷 - 第6阶 - ElasticSearch(一)
传送门:JB3-6-ElasticSearch(一)
传送门:JB3-6-ElasticSearch(二)
心法:本章使用 Maven 父子结构项目进行练习
练习项目结构如下:
|_ v3-6-ssm-elasticsearch
|_ 13601 elasticsearch-elk
|_ 13602 elasticsearch-template
武技:搭建练习项目结构
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>3.2.5version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
<scope>providedscope>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>${hutool-all.version}version>
dependency>
dependencies>
心法:ElasticSearch 简称 ES,是基于lucene核心并由Java开发的一款开源的检索引擎,支持全文检索,分布式检索,数据统计分析,内容纠错和提示等功能,可扩展到上百台服务器,可处理 PB 级数据,且检索延迟通常在 1 秒以内,常用于日志搜索,数据聚合,数据监控,报表统计分析等业务。
ES 搜索引擎相关概念如下:
node
:就是 1 个启动的 ES 服务,节点名默认随机生成,用于存储,索引和搜索数据。cluster
:由 1 或 N 个 ES 节点构成,默认名 elasticsearch,集群间共享数据,均可操作数据。index
:类似数据库,一个 ES 节点包含 N 个索引库,库名建议全小写。type
:类似数据库表,一个 ES 索引库包含 N 个类型,默认名 _doc,ES7+ 已弃用。document
:类似数据库表中的一行记录,本质是一条 JSON 格式的数据。field
:类似数据库表中的列,是构成 ES 文档的基本单元。mapping
:用于配置索引,如字段约束,数据规则等。shard
:每个 ES 索引库可以拆为 N 个分片,每个分片都是一个功能完整的索引,最多能存储 Integer.maxValue - 128
个文档:
replica
:每个 ES 分片可以配置 1 或 N 个副本:
心法:Elasticsearch Inverted Index ES倒排索引是 ES 核心特性之一,用于高效地存储和查询文档数据,提高数据搜索效率。
正排索引对比倒排索引:
倒排索引原理:
{ "id": 1, "text": "hi, i am lilei" },
{ "id": 2, "text": "hi, i am hanmeimei" },
{ "id": 3, "text": "how are you" },
{ "id": 4, "text": "fine, thank you, and you" },
{ "id": 5, "text": "i am fine too" },
文档ID | terms词条 | terms词条 | terms词条 | terms词条 | terms词条 |
---|---|---|---|---|---|
1 | hi | i | am | lilei | |
2 | hi | i | am | hanmeimei | |
3 | how | are | you | ||
4 | fine | thank | you | and | you |
5 | i | am | fine | too |
(出现某单词的文档ID; 单词在该文档中出现的次数; 单词在文档中的位置)
:单词 | 单词出现频率 | 倒排列表 |
---|---|---|
hi | 2 | (1; 1; <1>), (2; 1; <1>) |
i | 3 | (1; 1; <2>), (2; 1; <2>), (5; 1; <1>) |
am | 3 | (1; 1; <3>), (2; 1; <3>), (5; 1; <2>) |
lilei | 1 | (1; 1; <4>) |
hanmeimei | 1 | (2; 1; <4>) |
how | 1 | (3; 1; <1>) |
are | 1 | (3; 1; <2>) |
you | 3 | (3; 1; <3>), (4; 2; ❤️, 5>) |
fine | 1 | (5; 1; <3>) |
too | 1 | (5; 1; <4>) |
武技:在 Docker 中搭建单机 ElasticSearch 容器
# 创建ElasticSearch相关目录
mkdir -p /opt/elasticsearch/single/data;
mkdir -p /opt/elasticsearch/single/plugins;
chmod -R 777 /opt/elasticsearch;
# 拉取镜像(二选一)
docker pull elasticsearch:8.4.0;
docker pull registry.cn-hangzhou.aliyuncs.com/joezhou/elasticsearch:8.4.0;
# 创建并运行临时的容器: 该容器仅用于拷贝配置文件,用完即删
docker run -d --name tmp \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
registry.cn-hangzhou.aliyuncs.com/joezhou/elasticsearch:8.4.0;
# 拷贝配置目录到虚拟机
docker cp tmp:/usr/share/elasticsearch/config /opt/elasticsearch/single/conf
# 停止并删除临时容器,为后续真正的容器让出端口
docker rm -f tmp;
# 创建并运行真正的ES容器
# 参数 `-e "discovery.type=single-node"`: 使用单机模式
# 参数 `-e ES_JAVA_OPTS="-Xms512m -Xmx512m"`: 设置JVM参数,小于512M无法启动
docker run -itd --name elasticsearch --network my-net -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-v /opt/elasticsearch/single/conf:/usr/share/elasticsearch/config \
-v /opt/elasticsearch/single/data:/usr/share/elasticsearch/data \
-v /opt/elasticsearch/single/plugins:/usr/share/elasticsearch/plugins \
registry.cn-hangzhou.aliyuncs.com/joezhou/elasticsearch:8.4.0;
# 查看ES容器
docker ps --format "{{.ID}}\t{{.Names}}\t{{.Ports}}"
docker logs elasticsearch --tail 30
# 进入容器的内部: 查看ES版本
docker exec -it elasticsearch bash
elasticsearch -version
# 永久开放9200端口和9300端口
firewall-cmd --add-port=9200/tcp --permanent
firewall-cmd --add-port=9300/tcp --permanent
firewall-cmd --reload
/opt/elasticsearch/single/conf/elasticsearch.yml
配置文件的内容,修改如下:cluster.name: "docker-cluster"
network.host: 0.0.0.0
#----------------------- BEGIN SECURITY AUTO CONFIGURATION -----------------------
# (修改)关闭 X-Pack 安全模式
xpack.security.enabled: false
# (修改)禁用初始化生成密码功能
xpack.security.enrollment.enabled: false
# (修改)禁用客户端如 Kibana、Logstash、filebeat)通过加密方式访问
xpack.security.http.ssl.enabled: false
# (修改)禁用 Elasticsearch 节点间传输层通信通过加密方式访问
xpack.security.transport.ssl.enabled: false
#----------------------- END SECURITY AUTO CONFIGURATION -------------------------
docker restart elasticsearch;
{
"name": "607ab45ce69e",
"cluster_name": "docker-cluster",
"cluster_uuid": "Zs3rlwEbQd-AagLF9KYobg",
"version": {
"number": "8.4.0",
"build_flavor": "default",
"build_type": "docker",
"build_hash": "f56126089ca4db89b631901ad7cce0a8e10e2fe5",
"build_date": "2022-08-19T19:23:42.954591481Z",
"build_snapshot": false,
"lucene_version": "9.3.0",
"minimum_wire_compatibility_version": "7.17.0",
"minimum_index_compatibility_version": "7.0.0"
},
"tagline": "You Know, for Search"
}
武技:在 Docker 中搭建 ElasticSearch 集群容器
/etc/sysctl.conf
文件,在其文件末尾添加虚拟内存大小配置:# 在 /etc/sysctl.conf 文件末尾添加虚拟内存大小配置
echo 'vm.max_map_count=262144' >> /etc/sysctl.conf;
cat /etc/sysctl.conf;
# 刷新配置
sysctl -p
# 创建2个ES节点的相关目录
mkdir -p /opt/elasticsearch/cluster/node9201/data;
mkdir -p /opt/elasticsearch/cluster/node9201/plugins;
mkdir -p /opt/elasticsearch/cluster/node9202/data;
mkdir -p /opt/elasticsearch/cluster/node9202/plugins;
chmod -R 777 /opt/elasticsearch/cluster;
# 将单机ES目录的 `conf` 目录分别拷贝到两个ES节点目录中
cp -r /opt/elasticsearch/single/conf/ /opt/elasticsearch/cluster/node9201/;
cp -r /opt/elasticsearch/single/conf/ /opt/elasticsearch/cluster/node9202/;
/opt/elasticsearch/cluster/node9201/conf/elasticsearch.yml
配置(追加):# ES节点的端口号,默认9200
http.port: 9201
# 节点名,必须保证两个ES节点的名称不一致
node.name: node9201
# ES内部通信端口
transport.port: 9301
# 本机IP地址
network.publish_host: 192.168.40.77
# 集群节点列表
discovery.seed_hosts: ["192.168.40.77:9301", "192.168.40.77:9302"]
# 初始主节点
cluster.initial_master_nodes: ["node9201", "node9202"]
# 允许被跨域访问,默认false
http.cors.enabled: true
# 允许所有域名对我进行访问,值为RE表达式
http.cors.allow-origin: "*"
/opt/elasticsearch/cluster/node9202/conf/elasticsearch.yml
配置(追加):# ES节点的端口号,默认9200
http.port: 9202
# 节点名,必须保证两个ES节点的名称不一致
node.name: node9202
# 本机IP地址
network.publish_host: 192.168.40.77
# ES内部通信端口
transport.port: 9302
# 集群节点列表
discovery.seed_hosts: ["192.168.40.77:9301", "192.168.40.77:9302"]
# 初始主节点
cluster.initial_master_nodes: ["node9201", "node9202"]
# 允许被跨域访问,默认false
http.cors.enabled: true
# 允许所有域名对我进行访问,值为RE表达式
http.cors.allow-origin: "*"
# 创建并运行9201节点容器
docker run -d --name elasticsearch9201 --network my-net -p 9201:9201 -p 9301:9301 \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-v /opt/elasticsearch/cluster/node9201/conf:/usr/share/elasticsearch/config \
-v /opt/elasticsearch/cluster/node9201/data:/usr/share/elasticsearch/data \
-v /opt/elasticsearch/cluster/node9201/plugins:/usr/share/elasticsearch/plugins \
registry.cn-hangzhou.aliyuncs.com/joezhou/elasticsearch:8.4.0;
# 创建并运行9202节点容器
docker run -d --name elasticsearch9202 --network my-net -p 9202:9202 -p 9302:9302 \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-v /opt/elasticsearch/cluster/node9202/conf:/usr/share/elasticsearch/config \
-v /opt/elasticsearch/cluster/node9202/data:/usr/share/elasticsearch/data \
-v /opt/elasticsearch/cluster/node9202/plugins:/usr/share/elasticsearch/plugins \
registry.cn-hangzhou.aliyuncs.com/joezhou/elasticsearch:8.4.0;
# 查看运行的容器
docker ps --format "{{.ID}}\t{{.Names}}\t{{.Ports}}"
docker logs elasticsearch9201 --tail 30
docker logs elasticsearch9202 --tail 30
# 永久开放9201/9202/9301/9302端口
firewall-cmd --add-port=9201/tcp --permanent
firewall-cmd --add-port=9202/tcp --permanent
firewall-cmd --add-port=9301/tcp --permanent
firewall-cmd --add-port=9302/tcp --permanent
firewall-cmd --reload
心法:Kibana 是一个开源的分析与可视化平台,可以使用图形化的界面展示和操作存储在 Elasticsearch 中的数据。
武技:在 Docker 中搭建 单机的 Kibana 容器
# 创建Kibana相关目录
mkdir -p /opt/kibana/conf;
chmod -R 777 /opt/kibana;
/opt/kibana/conf/kibana.yml
(自行创建):# 配置服务名
server.name: kibana
# 配置服务地址,允许远程访问
server.host: "0.0.0.0"
# 配置ES服务端地址
elasticsearch.hosts: ["http://192.168.40.77:9200"]
# 配置连接ES服务端的超时毫秒数
elasticsearch.requestTimeout: 100000
# 配置Kibana界面使用中文
i18n.locale: "zh-CN"
# 拉取镜像(二选一)
docker pull kibana:8.4.0;
docker pull registry.cn-hangzhou.aliyuncs.com/joezhou/kibana:8.4.0;
# 创建并运行Kibana容器
# 参数 `-e ELASTICSEARCH_URL=http://192.168.40.77:9200`: 指定ES服务端地址。
docker run -itd --name kibana --network my-net -p 5601:5601 \
-e ELASTICSEARCH_URL=http://192.168.40.77:9200 \
-v /opt/kibana/conf:/usr/share/kibana/config \
registry.cn-hangzhou.aliyuncs.com/joezhou/kibana:8.4.0;
# 查看运行的容器
docker ps --format "{{.ID}}\t{{.Names}}\t{{.Ports}}"
docker logs kibana --tail 30
# 进入Kibana容器的内部: 查看Kibana版本
docker exec -it kibana bash
kibana --version
# 永久开放5601端口
firewall-cmd --add-port=5601/tcp --permanent
firewall-cmd --reload
开发工具
页面,输入如下代码进行测试:# 不配置 `analyzer` 则仍然使用默认的逐字拆分法
POST _analyze
{
"text": "my name is 亚洲舞王-赵四"
}
心法:ElasticSearch 自带的标准分词器仅对英文分词合理,对中文采取逐字拆分法,此时建议安装中文的 IK 分词器插件已解决中文分词问题(IK 分词器对英文单词忽略大小写)。
IK 分词器分词方式:
ik_smart
:结果集词条数量较少,适用于检索场景。ik_max_word
:结果集词条数量较多,适用于存储场景。武技:为 Docker 中的 ES 容器添加 IK 分词器插件
# 创建IK分词器插件目录,将IK分词器插件的zip包拷贝进来
mkdir -p /opt/elasticsearch/single/plugins/ik;
# 解压ZIP包
cd /opt/elasticsearch/single/plugins/ik;
unzip elasticsearch-analysis-ik-8.4.0.zip;
# 删除ZIP包(可选)
rm -f elasticsearch-analysis-ik-8.4.0.zip;
# 重启ES服务使插件生效
docker restart elasticsearch
# 查看ES中的所有插件
docker exec -it elasticsearch bash
elasticsearch-plugin list
# 使用IK最小拆分法
POST _analyze
{
"text": "我是一个黄皮肤的中国人",
"analyzer": "ik_smart"
}
# 使用IK最大拆分法
POST _analyze
{
"text": "我是一个黄皮肤的中国人",
"analyzer": "ik_max_word"
}
武技:在 Kibana 开发工具中测试创建 ES 索引相关的常用 REST 命令。
# 创建指定索引,该命令不支持POST
PUT user
# 重复创建报错
PUT user
武技:在 Kibana 开发工具中测试查看 ES 索引相关的常用 REST 命令。
# 查看指定索引
GET user
# 查看当前ES节点中全部索引
GET _cat/indices?v
武技:在 Kibana 开发工具中测试删除 ES 索引相关的常用 REST 命令。
# 删除指定索引
DELETE user
# 索引不存在报错
DELETE user
心法:创建文档时,文档类型
type
和文档id
需要同时指定。
武技:在 Kibana 开发工具中测试创建 ES 文档相关的常用 REST 命令。
# 向指定索引中添加1号文档数据,body需要另起一行
POST user/_doc/1
{ "name": "赵四", "age": 18 }
# 向指定索引中添加1号文档数据:重复添加视为修改
POST user/_doc/1
{ "name": "赵四", "age": 58 }
武技:在 Kibana 开发工具中测试查看 ES 文档相关的常用 REST 命令。
# 查看指定索引中的1号文档数据,不存在返回 "found" : false
GET user/_doc/1
武技:在 Kibana 开发工具中测试删除 ES 文档相关的常用 REST 命令。
# 删除指定索引中的2号文档数据,不存在返回 "result" : "not_found"
DELETE user/_doc/2
心法:ES的检索使用
_search
关键字,用于检索指定索引中的文档,默认只展示 10 条文档。
文档检索结果集格式如下:
{
"took" : 137, // 检索耗时(单位毫秒)
"timed_out" : false, // 本次检索是否超时
"_shards" : { // 分片情况
"total" : 1, // 总计检索了少分片
"successful" : 1, // 成功检索了少分片
"skipped" : 0, // 跳过了多少分片
"failed" : 0 // 失败了多少分片
},
"hits" : { // 命中情况
"total" : { // 总计数量
"value" : 6, // 命中数量
"relation" : "eq" // 命中关系
},
"max_score" : 1.0, // 最高命中匹配分数
"hits" : [ // 命中数据列表
{
"_index" : "user", // 库
"_type" : "_doc", // 表
"_id" : "1", // 主键
"_score" : 1.0, // 得分
"_source" : { // 源数据
"name" : "赵四",
"age" : 18,
"gender" : "male"
}
},
{ .. }, { .. }, { .. }, { .. }, { .. } // 其余5条数据
]
}
}
武技:在 Kibana 开发工具中测试 ES 检索相关的常用 REST 命令。
POST user/_doc/1
{"name": "赵四", "age": 18, "gender": "male"}
POST user/_doc/2
{"name": "刘能", "age": 19, "gender": "male"}
POST user/_doc/3
{"name": "广坤", "age": 35, "gender": "male"}
POST user/_doc/4
{"name": "刘英", "age": 44, "gender": "female"}
POST user/_doc/5
{"name": "王云", "age": 65, "gender": "female"}
POST user/_doc/6
{"name": "大拿", "age": 18, "gender": "male"}
武技:在 Kibana 开发工具中测试创建 ES 全文检索相关的常用 REST 命令。
# 检索指定索引中的全部文档
GET user/_search
武技:在 Kibana 开发工具中测试创建 ES 分页检索相关的常用 REST 命令。
# 检索分页: 从第0条文档开始(默认0),检索并显示3条文档(默认10)
GET user/_search
{ "from": 0, "size": 3 }
心法:ES 排序查询时,默认使用
_score
降序,若使用其他字段排序,则默认的_score
排序会失效,若想保留该效果则需要手动设置。
排序要求:ES排序查询时,尽量只使用数字进行排序,若非要对字符串排序,用 属性名.keyword
替代。
武技:在 Kibana 开发工具中测试创建 ES 检索排序相关的常用 REST 命令。
# 按年龄升序,年龄相同时按姓名降序
GET user/_search
{
"sort": [
{ "age": "asc" },
{ "name.keyword": "desc" }
]
}
武技:在 Kibana 开发工具中测试创建 ES 条件检索相关的常用 REST 命令。
# 查询name属性中包含 `赵` 或 `刘` 的全部文档
GET user/_search
{
"query": {
"match": { "name": "赵刘" }
}
}
# 查询name属性值中包含 `赵四` 短语的全部文档
GET user/_search
{
"query": {
"match_phrase": { "name": "赵四" }
}
}
# 查询_id属性值为1的文档
GET user/_search
{
"query": {
"bool": {
"must": [
{ "match": { "_id": 1 } }
]
}
}
}
# 查询_id属性值不为1的全部文档
GET user/_search
{
"query": {
"bool": {
"must_not": [
{ "match": { "_id": 1 } }
]
}
}
}
# 查询_id属性值在3到7之间(包括两端值)的全部文档
GET user/_search
{
"query": {
"bool": {
"filter": [
{ "range": { "age": { "gte": 30, "lte": 70 } } }
]
}
}
}
心法:聚合筛选结果的命名是自定义的,如
sumResult/minResult
等,默认会展示部分数据,如不需要,可以额外设置size: 0
来屏蔽数据。
武技:在 Kibana 开发工具中测试创建 ES 聚合检索相关的常用 REST 命令。
# 查询全部文档中的age属性值的总和/最小值/最大值/平均值/多少个/多少种/全部聚合信息
GET user/_search
{
"aggs": {
"sumResult": { "sum": { "field": "age" } },
"minResult": { "min": { "field": "age" } },
"maxResult": { "max": { "field": "age" } },
"avgResult": { "avg": { "field": "age" } },
"countResult": { "value_count": { "field": "age" } },
"typeResult": { "cardinality": { "field": "age" } },
"statsResult": { "stats": { "field": "age" } }
},
"size": 0
}
# 按gender分组,查询每组的age的总和/最大值/最小值/平均值/总数量
# 支持在 `terms` 同级再次用 `aggs` 聚合
GET user/_search
{
"aggs": {
"result": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"statsResult": { "stats": { "field": "age" } }
}
}
},
"size": 0
}
Java道经第3卷 - 第6阶 - ElasticSearch(一)
传送门:JB3-6-ElasticSearch(一)
传送门:JB3-6-ElasticSearch(二)