赞
踩
Mysql数据库 | Elastic Search |
---|---|
Database | 7.X版本前有Type,对比数据库中的表,新版取消了 |
Table | Index |
Row | Document |
Column | Field |
ES默认为一个索引创建1个主分片和1个副本,在创建索引的时候使用settings属性指定,每个分片必须有零到多个副本
注意:索引一旦创建成功,主分片primary shard数量不可以变(只能重建索引),副本数量可以改变
正排索引 (Forward Index )
文档1:标题 "Apple iPhone 12",内容 "The latest iPhone model"
文档2:标题 "Samsung Galaxy S21",内容 "Powerful Android smartphone"
文档3:标题 "Microsoft Surface Laptop",内容 "Thin and lightweight laptop"
DocumentID | Title | Content
-----------------------------------------------------------
1 | Apple iPhone 12 | The latest iPhone model
2 | Samsung Galaxy S21 | Powerful Android smartphone
3 | Microsoft Surface Laptop | Thin and lightweight laptop
倒排索引(Inverted Index)
根据关键词构建的索引结构,记录了每个关键词出现在哪些文档或数据记录中,适用于全文搜索和关键词检索的场景
它将文档或数据记录划分成关键词的集合,并记录每个关键词所出现的位置和相关联的文档或数据记录的信息
案例一
例子假设 使用以下文档构建倒排索引
文档1:标题 "Apple iPhone 12",内容 "The latest iPhone model"
文档2:标题 "Samsung Galaxy S21",内容 "Powerful Android smartphone"
文档3:标题 "Microsoft Surface Laptop",内容 "Thin and lightweight laptop Apple"
倒排索引的结构示意图如下:
Term | Documents
-----------------------------------------
Apple | 1,3
iPhone | 1
12 | 1
latest | 1
Samsung | 2
Galaxy | 2
S21 | 2
Powerful | 2
Android | 2
smartphone| 2
Microsoft | 3
Surface | 3
Laptop | 3
Thin | 3
lightweight| 3
案例二
假设我们有以下三个文档
文档1:我喜欢吃苹果
文档2:我喜欢吃橙子和苹果
文档3:我喜欢吃香蕉
倒排索引的结构示意图如下
关键词 "我":文档1、文档2、文档3
关键词 "喜欢":文档1、文档2、文档3
关键词 "吃":文档1、文档2、文档3
关键词 "苹果":文档1、文档2
关键词 "橙子":文档2
关键词 "香蕉":文档3
通过倒排索引,可以快速地找到包含特定关键词的文档或数据记录
倒排索引记录了每个词语出现在哪些文档中,通过这种方式,我们可以非常快速地得知每个关键词分别出现在哪些文档中。
当我们需要搜索包含特定关键词的文档时,可以直接查找倒排索引,找到包含该关键词的文档
总结
正排索引和倒排索引的结构和用途不同
正排索引用于快速访问和提取文档的内容
倒排索引用于快速定位和检索包含特定词语的文档
说明
jdk17 windows:
链接:https://pan.baidu.com/s/16q0JMY-1o-nU_fd6-jXqQA?pwd=pnr7
提取码:pnr7
jdk17 linux:
链接:https://pan.baidu.com/s/1rHzHpHbjP6RalCYYCM9EYw?pwd=sar2
提取码:sar2
vim /etc/profile
在末尾添加(JAVAHOME写自己的路径)
JAVA_HOME=/usr/local/software/elk_test/jdk17
CLASSPATH=$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH
下载地址
链接:https://pan.baidu.com/s/1oRTlwZeCXDzPY3r1LontKA?pwd=6sff
提取码:6sff
上传安装包和解压
tar -zxvf elasticsearch-8.4.1-linux-x86_64.tar.gz
新建一个用户,安全考虑,elasticsearch默认不允许以root账号运行
创建用户:useradd es_user
设置密码:passwd es_user
修改目录权限
chgrp -R es_user /usr/local/software/elk_test/elasticsearch-8.4.1
chown -R es_user /usr/local/software/elk_test/elasticsearch-8.4.1
chmod -R 777 /usr/local/software/elk_test/elasticsearch-8.4.1
修改文件和进程最大打开数,需要root用户,如果系统本身有这个文件最大打开数和进程最大打开数配置,则不用
在文件内容最后添加后面两行(切记*不能省略)
vim /etc/security/limits.conf
- soft nofile 65536
- hard nofile 65536
修改虚拟内存空间,默认太小
在配置文件中改配置 最后一行上加上
vim /etc/sysctl.conf
vm.max_map_count=262144
退出保存,执行 sysctl -p(立即生效)
修改elasticsearch的JVM内存,机器内存不足,常规线上推荐16到24G内存
vim config/jvm.options
-Xms1g
-Xmx1g
修改 elasticsearch相关配置
vim config/elasticsearch.yml
cluster.name: my-application
node.name: node-1
path.data: /usr/local/software/elk_test/elasticsearch-8.4.1/data
path.logs: /usr/local/software/elk_test/elasticsearch-8.4.1/logs
network.host: 0.0.0.0
http.port: 9200
cluster.initial_master_nodes: ["node-1"]
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
ingest.geoip.downloader.enabled: false
配置说明
cluster.name: 指定Elasticsearch集群的名称。所有具有相同集群名称的节点将组成一个集群。
node.name: 指定节点的名称。每个节点在集群中应该具有唯一的名称。
path.data: 指定用于存储Elasticsearch索引数据的路径。
path.logs: 指定Elasticsearch日志文件的存储路径。
network.host: 指定节点监听的网络接口地址。0.0.0.0表示监听所有可用的网络接口,开启远程访问连接
http.port: 指定节点上的HTTP服务监听的端口号。默认情况下,Elasticsearch的HTTP端口是9200。
cluster.initial_master_nodes: 指定在启动集群时作为初始主节点的节点名称。
xpack.security.enabled: 指定是否启用Elasticsearch的安全特性。在这里它被禁用(false),意味着不使用安全功能。
xpack.security.enrollment.enabled: 指定是否启用Elasticsearch的安全认证和密钥管理特性。在这里它被禁用(false)。
ingest.geoip.downloader.enabled: 指定是否启用GeoIP数据库下载功能。在这里它被禁用(false)
以上命令均在root用户下执行
启动ElasticSearch
切换到es_user用户启动, 进入bin目录下启动, &为后台启动,再次提示es消息时 Ctrl + c 跳出
./elasticsearch &
常见命令,可以用postman访问(网络安全组记得开发端口)
#查看集群健康情况
http://120.78.85.91:9200/_cluster/health
#查看分片情况
http://120.78.85.91:9200/_cat/shards?v=true&pretty
#查看节点分布情况
http://120.78.85.91:9200/_cat/nodes?v=true&pretty
#查看索引列表
http://120.78.85.91:9200/_cat/indices?v=true&pretty
常见问题
磁盘空间需要85%以下,不然ES状态会不正常
不要用root用户安装
linux内存不够
linux文件句柄
没开启远程访问 或者 网络安全组没看开放端口
有9300 tcp端口,和http 9200端口,要区分
没权限访问,重新执行目录权限分配
chgrp -R es_user /usr/local/software/elk_test/elasticsearch-8.4.1
chown -R es_user /usr/local/software/elk_test/elasticsearch-8.4.1
chmod -R 777 /usr/local/software/elk_test/elasticsearch-8.4.1
Kibana
基于node.js开发,数据可视化和仪表盘工具,连接到Elasticsearch,通过简单易用的用户界面创建各种图表、图形和仪表盘
帮助用户快速探索和理解数据,并进行强大的数据分析和可视化
除了使用Kibana,也可以使用其他http请求工具,比如postman等进行测试接口和es语法
安装部署
安装包
链接:https://pan.baidu.com/s/1QyepsiMPgTYtFnUxKYLKcQ?pwd=o5ee
提取码:o5ee
上传安装包和解压
tar -zxvf kibana-8.4.1-linux-x86_64.tar.gz
配置用户权限
chgrp -R es_user /usr/local/software/elk_test/kibana-8.4.1
chown -R es_user /usr/local/software/elk_test/kibana-8.4.1
chmod -R 777 /usr/local/software/elk_test/kibana-8.4.1
修改配置文件
vim config/kibana.yml
server.port: 5601
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://112.74.167.42:9200"]
i18n.locale: "zh-CN" #汉化
#配置说明
server.port: 指定Kibana服务器监听的端口号,Kibana将在5601端口上监听HTTP请求。
server.host: 指定Kibana服务器绑定的网络接口地址, "0.0.0.0"表示监听所有可用的网络接口。
elasticsearch.hosts: 指定ES集群的主机地址,可以配置多个,Kibana将连接到位于"120.24.7.58"主机上、使用默认HTTP端口9200的es
i18n.locale: 指定Kibana界面的语言区域,"zh-CN"表示使用简体中文作为语言。
启动,切换到es_user 用户, 建议先不用守护进程方式启动,可以直接启动查看日志
./kibana &
验证请求
进入开发工具管理台:http://120.78.85.91:5601
#查看集群健康情况
GET /_cluster/health
#查看分片情况
GET /_cat/shards?v=true&pretty
#查看节点分布情况
GET /_cat/nodes?v=true&pretty
#查看索引列表
GET /_cat/indices?v=true&pretty
查看索引列表
GET /_cat/indices?v=true&pretty
查看分片情况
GET /_cat/shards?v=true&pretty
创建索引(Create Index):
PUT /<index_name>
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}
PUT /xdclass_shop
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1
}
}
当单节点部署es时,创建索引如果同时创建副本,此时es的状态的yellow,因为副本和主节点不能再同一台机器上
查看索引是否存在( 结果是200 和 404)
HEAD /<index_name>
HEAD /xdclass_shop
获取索引(Get Index)
GET /<index_name>
GET /xdclass_shop
更新索引设置(Update Index Settings):
PUT /<index_name>/_settings
{
"settings": {
"number_of_replicas": 2
}
}
PUT /xdclass_shop/_settings
{
"settings": {
"number_of_replicas": 2
}
}
删除索引(Delete Index):
DELETE /<index_name>
DELETE /xdclass_shop
文档document
文档的基本操作
GET /xdclass_shop/_doc/1
PUT /xdclass_shop/_doc/1
{
"id":5555,
"title":"小滴课堂短链平台大课",
"pv":144
}
POST /xdclass_shop/_doc
{
"id":123,
"title":"小滴课堂架构大课",
"pv":244
}
PUT /xdclass_shop/_doc/1
{
"id":999,
"title":"小滴课堂短链平台大课v2",
"pv":999,
"uv":55
}
POST /xdclass_shop/_doc/1
{
"id":999,
"title":"小滴课堂短链平台大课v3",
"pv":999,
"uv":559
}
GET /xdclass_shop/_search
字段解释
took字段表示该操作的耗时(单位为毫秒)。
timed_out字段表示是否超时。
hits字段表示搜到的记录,数组形式。
total:返回记录数,本例是1条。
max_score:最高的匹配程度,本例是1.0
DELETE /xdclass_shop/_doc/1
什么是Mapping
查看索引库的字段类型
GET /<index_name>/_mapping
GET /my_index/_mapping
Dynamic Mapping(动态映射)
用于在索引文档时自动检测和定义字段的数据类型
当我们向索引中添加新文档时,Elasticsearch会自动检测文档中的各个字段,并根据它们的值来尝试推断字段类型
常见的字段类型包括文本(text)、关键词(keyword)、日期(date)、数值(numeric)等
动态映射具备自动解析和创建字段的便利性,但在某些情况下,由于字段类型的不确定性,动态映射可能会带来一些问题
例如字段解析错误、字段类型不一致等,如果对字段类型有明确的要求,最好在索引创建前通过显式映射定义来指定字段类型
ElasticSearch常见的数据类型
在 ES 7.X后有两种字符串类型:Text 和 Keyword
Text类型:用于全文搜索的字符串类型,支持分词和索引建立
Keyword类型:用于精确匹配的字符串类型,不进行分词,适合用作过滤和聚合操作。
Numeric类型:包括整数类型(long、integer、short、byte)和浮点数类型(double、float)。
Date类型:用于存储日期和时间的类型。
Boolean类型:用于存储布尔值(true或false)的类型。
Binary类型:用于存储二进制数据的类型。
Array类型:用于存储数组或列表数据的类型。
Object类型:用于存储复杂结构数据的类型
指定索引库字段类型mapping
PUT /my_index
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text"
},
"price": {
"type": "float"
}
}
}
}
最高频使用的数据类型
text字段类型
text类型主要用于全文本搜索,适合存储需要进行全文本分词的文本内容,如文章、新闻等。
text字段会对文本内容进行分词处理,将文本拆分成独立的词项(tokens)进行索引
分词的结果会建立倒排索引,使搜索更加灵活和高效。
text字段在搜索时会根据分词结果进行匹配,并计算相关性得分,以便返回最佳匹配的结果。
keyword字段类型
keyword类型主要用于精确匹配和聚合操作,适合存储不需要分词的精确值,如ID、标签、关键字等。
keyword字段不会进行分词处理,而是将整个字段作为一个整体进行索引和搜索
这使得搜索只能从精确的值进行匹配,而不能根据词项对内容进行模糊检索。
keyword字段适合用于过滤和精确匹配,同时可以进行快速的基于精确值的聚合操作。
总结
在选择text字段类型和keyword字段类型时,需要根据具体的需求进行权衡和选择:
如果需要进行全文本检索,并且希望根据分词结果计算相关性得分,以获得最佳的匹配结果,则选择text字段类型。
如果需要进行精确匹配、排序或聚合操作,并且不需要对内容进行分词,则选择keyword字段类型。
案例实战
创建索引并插入文档
PUT /my_index
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"tags": {
"type": "keyword"
},
"publish_date": {
"type": "date"
},
"rating": {
"type": "float"
},
"is_published": {
"type": "boolean"
},
"author": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
}
}
},
"comments": {
"type": "nested",
"properties": {
"user": {
"type": "keyword"
},
"message": {
"type": "text"
}
}
}
}
}
}
POST /my_index/_doc/1
{
"title": "小滴课堂最近上线了新课 Elasticsearch Introduction",
"tags": ["search", "big data", "distributed system", "小滴课堂"],
"publish_date": "2025-01-01",
"rating": 4.5,
"is_published": true,
"author": {
"name": "John Doe",
"age": 30
},
"comments": [
{
"user": "Alice",
"message": "Great article!"
},
{
"user": "Bob",
"message": "Very informative."
}
]
}
查询匹配关键词的文档一
GET /my_index/_search
{
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
查询匹配关键词的文档二
# tags是password类型,不支持分词,根据data查不到数据
GET /my_index/_search
{
"query": {
"match": {
"tags": "data"
}
}
}
什么是搜索引擎的分词
假设我们有两个产品标题:
"Apple iPhone 12 Pro Max 256GB"
"Samsung Galaxy S21 Ultra 128GB"
使用默认的标准分词器(Standard Tokenizer),这些标题会被分割为以下令牌:
标题1:["Apple", "iPhone", "12", "Pro", "Max", "256GB"]
标题2:["Samsung", "Galaxy", "S21", "Ultra", "128GB"]
分词器根据标点符号和空格将标题拆分为独立的词语。当我们执行搜索时,可以将查询进行分词,并将其与标题中的令牌进行匹配。
例如
如果我们搜索"iPhone 12",使用默认的分词器,它会将查询分解为["iPhone", "12"],然后与令牌进行匹配。
对于标题1,令牌["iPhone", "12"]匹配,它与查询相符。 标题2中没有与查询相符的令牌
默认的Standard分词器的分词规则
如何查看ES分词存储效果?
使用analyze API 来对文本进行分词处理并查看分词结果,基本语法如下
GET /_analyze
{
"analyzer": "分词器名称",
"text": "待分析的文本"
}
案例
#字段是text类型
POST /my_index/_analyze
{
"field": "title",
"text": "This is some text to analyze"
}
#字段是text类型
POST /my_index/_analyze
{
"field": "title",
"text": "今天我在小滴课堂学习架构大课"
}
#字段是keyword类型
POST /my_index/_analyze
{
"field": "tags",
"text": "This is some text to analyze"
}
#字段是keyword类型
POST /my_index/_analyze
{
"field": "tags",
"text": ["This is","小滴课堂","Spring Boot" ]
}
每个分词结果对象包含
分词后的单词(token)
开始位置(start_offset)
结束位置(end_offset)
类型(type)
ALPHANUM是一种数据类型,表示一个字符串字段只包含字母和数字,并且不会进行任何其他的分词或处理
它会忽略字段中的任何非字母数字字符(如标点符号、空格等),只保留字母和数字字符
单词在原始文本中的位置(position)
IK中文分词器配置
#字段是text类型
POST /my_index/_analyze
{
"field": "title",
"text": "我今天去小滴课堂学习spring cloud项目大课"
}
#结果如下,中文每个字单独一个词
{
"tokens": [
{
"token": "我",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "今",
},
{
"token": "天",
},
......
{
"token": "spring",
"start_offset": 10,
"end_offset": 16,
"type": "<ALPHANUM>",
"position": 10
},
{
"token": "cloud",
"start_offset": 17,
"end_offset": 22,
"type": "<ALPHANUM>",
"position": 11
},
{
"token": "项",
"start_offset": 22,
"end_offset": 23,
"type": "<IDEOGRAPHIC>",
"position": 12
},
{
"token": "目",
"start_offset": 23,
"end_offset": 24,
"type": "<IDEOGRAPHIC>",
"position": 13
},
]
}
官网:https://github.com/medcl/elasticsearch-analysis-ik/releases
安装包:
链接:https://pan.baidu.com/s/1ZwyxlYGX7XszDKVNoZAx5Q?pwd=84jb
提取码:84jb
#关闭Elasticsearch节点
bin/elasticsearch -d
#检查进程是否已经停止
ps -ef | grep elasticsearch
ik_smart: 会做最粗粒度的拆分
ik_max_word(常用): 会将文本做最细粒度的拆分
案例实践
GET /_analyze
{
"text":"今天星期一,我今天去小滴课堂学习spring cloud项目大课",
"analyzer":"ik_smart"
}
GET /_analyze
{
"text":"今天星期一,我今天去小滴课堂学习spring cloud项目大课",
"analyzer":"ik_max_word"
}
GET /索引库名/_search
{
"query":{
"查询类型":{
}
}
PUT /xdclass_shop_v1
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "keyword"
},
"summary": {
"type": "text"
},
"price": {
"type": "float"
}
}
}
}
PUT /xdclass_shop_v1/_bulk
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "1", "title": "Spring Boot","summary":"this is a summary Spring Boot video", "price": 9.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "2", "title": "java","summary":"this is a summary java video", "price": 19.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "3", "title": "Spring Cloud","summary":"this is a summary Spring Cloud video", "price": 29.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "4", "title": "Spring_Boot", "summary":"this is a summary Spring_Boot video","price": 59.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "5", "title": "SpringBoot","summary":"this is a summary SpringBoot video", "price": 0.99 }
GET /xdclass_shop_v1/_search
{
"query": {
"match_all": {}
}
}
GET /xdclass_shop_v1/_search
{
"query": {
"match": {
"summary": "Spring"
}
}
}
#包括多个词(多个词之间是or的关系,匹配度越高排在前面)
GET /xdclass_shop_v1/_search
{
"query": {
"match": {
"summary": "Spring Java"
}
}
}
#keyword类型字段,ES不进行分词
GET /xdclass_shop_v1/_search
{
"query": {
"term": {
"title": {
"value": "Spring Boot"
}
}
}
}
GET /xdclass_shop_v1/_search
{
"_source":["price","title"],
"query": {
"term": {
"title": {
"value": "Spring Boot"
}
}
}
}
用于根据范围条件进行查询,例如指定价格在一定区间内的商品
范围符号
gte:大于等于
gt:大于
lte:小于等于
lt:小于
GET /xdclass_shop_v1/_search
{
"query": {
"range": {
"price": {
"gte": 5,
"lte": 100
}
}
}
}
可以使用 from 和 size 参数进行分页查询
可以指定要跳过的文档数量(from)和需要返回的文档数量(size)
GET /xdclass_shop_v1/_search
{
"size": 10,
"from": 0,
"query": {
"match_all": {}
}
}
sort字段可以进行排序 desc 和 asc
GET /xdclass_shop_v1/_search
{
"size": 10,
"from": 0,
"sort": [
{
"price": "asc"
}
],
"query": {
"match_all": {}
}
}
通过组合多个查询条件,使用布尔逻辑(与、或、非)进行复杂的查询操作
语法格式
"must"关键字用于指定必须匹配的条件,即所有条件都必须满足
"must_not"关键字指定必须不匹配的条件,即所有条件都不能满足
"should"关键字指定可选的匹配条件,即至少满足一个条件
{
"query": {
"bool": {
"must": [
// 必须匹配的条件
],
"must_not": [
// 必须不匹配的条件
],
"should": [
// 可选匹配的条件
],
"filter": [
// 过滤条件
]
}
}
}
案例实战
GET /xdclass_shop_v1/_search
{
"query": {
"bool": {
"must": [
{ "match": { "summary": "Cloud" }},
{ "range": { "price": { "gte": 5 }}}
]
}
}
}
来对搜索结果进行筛选和过滤,仅返回符合特定条件的文档,而不改变搜索评分
Filter查询对结果进行缓存,提高查询性能,用于数字范围、日期范围、布尔逻辑、存在性检查等各种过滤操作。
语法格式
{
"query": {
"bool": {
"filter": {
// 过滤条件
}
}
}
}
案例实战
PUT /product
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"product_id": {
"type": "integer"
},
"product_name": {
"type": "text"
},
"category": {
"type": "keyword"
},
"price": {
"type": "float"
},
"availability": {
"type": "boolean"
}
} }
}
POST /product/_bulk
{ "index": { "_id": "1" } }
{ "product_id": 1, "product_name": "Product 1", "category": "books", "price": 19.99, "availability": true }
{ "index": { "_id": "2" } }
{ "product_id": 2, "product_name": "Product 2", "category": "electronics", "price": 29.99, "availability": true }
{ "index": { "_id": "3" } }
{ "product_id": 3, "product_name": "Product 3", "category": "books", "price": 9.99, "availability": false }
{ "index": { "_id": "4" } }
{ "product_id": 4, "product_name": "Product 4", "category": "electronics", "price": 49.99, "availability": true }
{ "index": { "_id": "5" } }
{ "product_id": 5, "product_name": "Product 5", "category": "fashion", "price": 39.99, "availability": true }
案例一 :使用 term 过滤器查询 category 为 books 的产品:
GET /product/_search
{
"query": {
"bool": {
"filter": {
"term": {
"category": "books"
}
}
}
}
}
案例二:使用 range 过滤器查询价格 price 在 30 到 50 之间的产品:
GET /product/_search
{
"query": {
"bool": {
"filter": {
"range": {
"price": {
"gte": 30,
"lte": 50
}
}
}
}
}
}
总结
过滤条件通常用于对结果进行筛选,并且比查询条件更高效
而bool查询可以根据具体需求组合多个条件、过滤器和查询子句
GET /index/_search
{
"query": {
"multi_match": {
"query": "要搜索的文本",
"fields": ["字段1", "字段2", ...]
}
}
}
# query:需要匹配的查询文本。
# fields:一个包含需要进行匹配的字段列表的数组。
#数据准备
PUT /product_v2
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"product_name": {
"type": "text"
},
"description": {
"type": "text"
},
"category": {
"type": "keyword"
}
}
}
}
POST /product_v2/_bulk
{ "index": { "_index": "product_v2", "_id": "1" } }
{ "product_name": "iPhone 12", "description": "The latest iPhone model from Apple", "category": "electronics" }
{ "index": { "_index": "product_v2", "_id": "2" } }
{ "product_name": "Samsung Galaxy S21", "description": "High-performance Android smartphone", "category": "electronics" }
{ "index": { "_index": "product_v2", "_id": "3" } }
{ "product_name": "MacBook Pro", "description": "Powerful laptop for professionals", "category": "electronics" }
{ "index": { "_index": "product_v2", "_id": "4" } }
{ "product_name": "Harry Potter and the Philosopher's Stone", "description": "Fantasy novel by J.K. Rowling", "category": "books" }
{ "index": { "_index": "product_v2", "_id": "5" } }
{ "product_name": "The Great Gatsby", "description": "Classic novel by F. Scott Fitzgerald", "category": "books" }
GET /product_v2/_search
{
"query": {
"multi_match": {
"query": "iPhone",
"fields": ["product_name", "description"]
}
}
}
是Elasticsearch中提供的一种高级匹配查询类型,用于执行精确的短语搜索
相比于match查询,match_phrase会在匹配时考虑到单词之间的顺序和位置
语法格式
GET /index/_search
{
"query": {
"match_phrase": {
"field_name": {
"query": "要搜索的短语"
}
}
}
}
# field_name:要进行匹配的字段名。
# query:要搜索的短语。
案例
#match_phrase短语搜索
GET /product_v2/_search
{
"query": {
"match_phrase": {
"description": "classic novel"
}
}
}
#match搜索,会进行分词
GET /product_v2/_search
{
"query": {
"match": {
"description": "classic novel"
}
}
}
fuzzy查询是Elasticsearch中提供的一种模糊匹配查询类型,用在搜索时容忍一些拼写错误或近似匹配
使用fuzzy查询,可以根据指定的编辑距离(即词之间不同字符的数量)来模糊匹配查询词
拓展:编辑距离
是将一个术语转换为另一个术语所需的一个字符更改的次数。
比如
更改字符(box→fox)
删除字符(black→lack)
插入字符(sic→sick)
转置两个相邻字符(dgo→dog)
fuzzy模糊查询是拼写错误的简单解决方案,但具有很高的 CPU 开销和非常低的精准度
用法和match基本一致,Fuzzy query的查询不分词
基本语法格式
GET /index/_search
{
"query": {
"fuzzy": {
"field_name": {
"value": "要搜索的词",
"fuzziness": "模糊度"
}
}
}
}
解析
field_name:要进行模糊匹配的字段名。
value:要搜索的词
fuzziness参数指定了模糊度,常见值如下
0,1,2
指定数字,表示允许的最大编辑距离,较低的数字表示更严格的匹配,较高的数字表示更松散的匹配
fuziness的值,表示是针对每个词语而言的,而不是总的错误的数值
AUTO:Elasticsearch根据词的长度自动选择模糊度
如果字符串的长度大于5,那 funziness 的值自动设置为2
如果字符串的长度小于2,那么 fuziness 的值自动设置为 0
案例操作
# 指定模糊度2,更松散匹配
GET /xdclass_shop_v1/_search
{
"query": {
"fuzzy": {
"summary": {
"value": "clo",
"fuzziness": "2"
}
}
}
}
# 指定模糊度1,更严格匹配
GET /xdclass_shop_v1/_search
{
"query": {
"fuzzy": {
"summary": {
"value": "clo",
"fuzziness": "1"
}
}
}
}
# 使用自动检查,1个单词拼写错误
GET /xdclass_shop_v1/_search
{
"query": {
"fuzzy": {
"summary": {
"value": "Sprina",
"fuzziness": "auto"
}
}
}
}
#创建索引库
PUT /xdclass_high_light_test
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"content": {
"type": "text",
"analyzer": "ik_max_word"
}
}
},
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
}
}
#插入数据
PUT /xdclass_high_light_test/_doc/1
{
"title": "小滴课堂2028年最新好看的电影推荐",
"content": "每年都有新电影上线,2028年最新好看的电影有不少,小滴课堂上线了《架构大课》,《低代码平台》,《老王往事》精彩电影"
}
PUT /xdclass_high_light_test/_doc/2
{
"title": "写下你认为好看的电影有哪些",
"content": "每个人都看看很多电影,说下你近10年看过比较好的电影,比如《架构大课》,《海量数据项目大课》,《冰冰和老王的故事》"
}
GET /xdclass_high_light_test/_search
{
"query": {
"match": {
"content": "电影"
}
},
"highlight": {
"fields": {
"content": {}
}
}
}
GET /xdclass_high_light_test/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": "课堂"
}
},
{
"match": {
"content": "老王"
}
}
]
}
},
"highlight": {
"fields": {
"title": {},
"content": {}
}
}
}
pre_tags:前置标签
post_tags:后置标签
fields:需要高亮的字段
案例
GET /xdclass_high_light_test/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": "课堂"
}
},
{
"match": {
"content": "老王"
}
}
]
}
},
"highlight": {
"pre_tags": "<font color='yellow'>",
"post_tags": "</font>",
"fields": [{"title":{}},{"content":{}}]
}
}
什么是聚合查询
对大量数据聚合统计处理,类似Mysql数据库操作里面的group by 分组、sum、avg、max等函数处理
是 Elasticsearch 中强大的功能之一,根据数据进行分组、过滤、计算和统计,提取有关数据集信息,进行数据分析
数据可视化大屏里面的饼状图、柱状图、折线图、仪表盘数据等都是聚合查询的关键应用
术语一:对数据集求最大、最小、和、平均值等指标的聚合,称为 指标聚合 metric
GET /index/_search
{
"size": 0,
"aggs": {
"aggregation_name": {
"aggregation_type": {
"aggregation_field": "field_name"
// 可选参数
}
}
// 可以添加更多的聚合
}
}
# 解析
index:要执行聚合查询的索引名称。
size: 设置为 0 来仅返回聚合结果,而不返回实际的搜索结果,这里将hits改为0表示返回的原始数据变为0
aggs:指定聚合操作的容器。
aggregation_name:聚合名称,可以自定义。
aggregation_type:聚合操作的类型,例如 terms、avg、sum 等。
aggregation_field:聚合操作的目标字段,对哪些字段进行聚合
术语二:对数据集进行分组group by,然后在组上进行指标聚合,在 ES 中称为分桶,桶聚合bucketing
GET /index/_search
{
"size": 0,
"aggs": {
"aggregation_name": {
"bucket_type": {
"bucket_options": {
"bucket_option_name": "bucket_option_value",
...
},
"aggs": {
"sub_aggregation_name": {
"sub_aggregation_type": {
"sub_aggregation_options": {
"sub_aggregation_option_name": "sub_aggregation_option_value",
...
}
}
}
}
}
}
}
}
#解析
index: 替换为要执行聚合查询的索引名称。
aggregation_name: 替换为自定义的聚合名称。
bucket_type: 替换为特定的桶聚合类型(如 terms、date_histogram、range 等)。
bucket_option_name 和 bucket_option_value: 替换为特定桶聚合选项的名称和值。
sub_aggregation_name: 替换为子聚合的名称。
sub_aggregation_type: 替换为特定的子聚合类型(如 sum、avg、max、min 等)。
sub_aggregation_option_name 和 sub_aggregation_option_value: 替换为特定子聚合选项的名称和值
常见聚合用途和应用场景案例
聚合指标(Aggregation Metrics):
Avg Aggregation:计算文档字段的平均值。
Sum Aggregation:计算文档字段的总和。
Min Aggregation:找到文档字段的最小值。
Max Aggregation:找到文档字段的最大值。
聚合桶(Aggregation Buckets):
Terms Aggregation:基于字段值将文档分组到不同的桶中。
Date Histogram Aggregation:按日期/时间字段创建时间间隔的桶。
Range Aggregation:根据字段值的范围创建桶。
指标聚合案例实战
聚合查询 max 应用案例:
POST /sales_v1/_doc
{ "product_name": "手机", "price": 1000 }
POST /sales_v1/_doc
{ "product_name": "电视", "price": 1500 }
POST /sales_v1/_doc
{ "product_name": "小滴课堂老王的黑丝", "price": 4500 }
GET /sales_v1/_search
{
"size": 0,
"aggs": {
"max_price": {
"max": {
"field": "price"
}
}
}
}
聚合查询 - min 应用案例:
POST /exam_scores/_doc
{ "student_name": "小滴课堂-大钊", "score" : 80 }
POST /exam_scores/_doc
{ "student_name": "老王", "score" : 90 }
POST /exam_scores/_doc
{ "student_name": "小滴课堂-D哥", "score" : 40 }
GET /exam_scores/_search
{
"size": 0,
"aggs": {
"min_score": {
"min": {
"field": "score"
}
}
}
}
聚合查询 - avg 应用案例:
GET /exam_scores/_search
{
"size": 0,
"aggs": {
"avg_score": {
"avg": {
"field": "score"
}
}
}
}
聚合查询 - sum 应用案例:
POST /sales_order/_doc
{ "product_name": "手机", "sales_count" : 100 }
POST /sales_order/_doc
{ "product_name": "电视", "sales_count" : 50 }
POST /sales_order/_doc
{ "product_name": "小滴课堂永久会员", "sales_count" : 999 }
GET /sales_order/_search
{
"size": 0,
"aggs": {
"total_sales": {
"sum": {
"field": "sales_count"
}
}
}
}
桶聚合案例实战
分桶聚合查询 - Terms 案例:
数据准备:假设有一个在线书店的图书销售记录索引,包含图书名称和销售数量字段。
#创建索引库
PUT /book_sales
{
"mappings": {
"properties": {
"book_title": {
"type": "keyword"
},
"sales_count": {
"type": "integer"
}
}
},
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0
}
}
# 批量插入数据
POST /book_sales/_bulk
{ "index": {} }
{ "book_title": "Elasticsearch in Action", "sales_count" : 100 }
{ "index": {} }
{ "book_title": "小滴课堂微服务最佳实践", "sales_count" : 50 }
{ "index": {} }
{ "book_title": "海量数据项目大课", "sales_count" : 80 }
{ "index": {} }
{ "book_title": "小滴课堂面试宝典", "sales_count" : 120 }
{ "index": {} }
{ "book_title": "数据结构与算法之美", "sales_count" : 90 }
{ "index": {} }
{ "book_title": "Python编程快速上手", "sales_count" : 70 }
{ "index": {} }
{ "book_title": "小滴课堂面试宝典", "sales_count" : 110 }
{ "index": {} }
{ "book_title": "小滴课堂Java核心技术", "sales_count" : 200 }
{ "index": {} }
{ "book_title": "深入理解计算机系统", "sales_count" : 150 }
{ "index": {} }
{ "book_title": "小滴课堂Java核心技术", "sales_count" : 80 }
案例说明:使用 terms 聚合查询将图书按销售数量进行分桶,并获取每个分桶内的销售数量总和。
GET /book_sales/_search
{
"size": 0,
"aggs": {
"book_buckets": {
"terms": {
"field": "book_title",
"size": 10
},
"aggs": {
"total_sales": {
"sum": {
"field": "sales_count"
}
}
}
}
}
}
分桶聚合查询 - Date Histogram
将日期类型的字段按照固定的时间间隔进行分桶,并对每个时间间隔内的文档进行进一步的操作和计算
基本语法如下
GET /index/_search
{
"size": 0,
"aggs": {
"date_histogram_name": {
"date_histogram": {
"field": "date_field_name",
"interval": "interval_expression"
},
"aggs": {
"sub_aggregation": {
"sub_aggregation_type": {}
}
}
}
}
}
#解析
index:替换为要执行聚合查询的索引名称。
date_histogram_name:替换为自定义的 date_histogram 聚合名称。
date_field_name:替换为要聚合的日期类型字段名。
interval_expression:指定用于分桶的时间间隔。时间间隔可以是一个有效的日期格式(如 1d、1w、1M),也可以是一个数字加上一个时间单位的组合(如 7d 表示 7 天,1h 表示 1 小时)。
sub_aggregation:指定在每个日期桶内进行的子聚合操作。
sub_aggregation_type:替换单独子聚合操作的类型,可以是任何有效的子聚合类型。
数据准备:一个电商网站的订单索引,包含订单日期和订单金额字段。
POST /order_history/_bulk
{ "index": {} }
{ "order_date": "2025-01-01", "amount" : 100 ,"book_title": "小滴课堂Java核心技术"}
{ "index": {} }
{ "order_date": "2025-02-05", "amount" : 150, "book_title": "小滴课堂面试宝典" }
{ "index": {} }
{ "order_date": "2025-03-02", "amount" : 500 ,"book_title": "小滴课堂Java核心技术"}
{ "index": {} }
{ "order_date": "2025-05-02", "amount" : 250 , "book_title": "小滴课堂面试宝典"}
{ "index": {} }
{ "order_date": "2025-05-05", "amount" : 10 ,"book_title": "小滴课堂微服务最佳实践"}
{ "index": {} }
{ "order_date": "2025-02-18", "amount" : 290 , "book_title": "小滴课堂微服务最佳实践"}
案例说明:使用 date_histogram 聚合查询将订单按日期进行分桶,并计算每个分桶内的订单金额总和。
GET /order_history/_search
{
"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "order_date",
"calendar_interval": "month",
"format": "yyyy-MM"
},
"aggs": {
"total_sales": {
"sum": {
"field": "amount"
}
}
}
}
}
}
分桶聚合查询 - Range
将字段的值划分为不同的范围,并将每个范围内的文档分配给相应的桶,对这些范围进行各种操作和计算。
语法介绍
GET /index/_search
{
"size": 0,
"aggs": {
"range_name": {
"range": {
"field": "field_name",
"ranges": [
{ "key": "range_key_1", "from": from_value_1, "to": to_value_1 },
{ "key": "range_key_2", "from": from_value_2, "to": to_value_2 },
...
]
},
"aggs": {
"sub_aggregation": {
"sub_aggregation_type": {}
}
}
}
}
}
#解析
index:替换为要执行聚合查询的索引名称。
range_name:替换为自定义的 range 聚合名称。
field_name:替换为要聚合的字段名。
ranges:指定范围数组,每个范围使用 key、from 和 to 参数进行定义。
key:范围的唯一标识符。
from:范围的起始值(包含)。
to:范围的结束值(不包含)。
sub_aggregation:指定在每个范围内进行的子聚合操作。
sub_aggregation_type:替换单独子聚合操作的类型,可以是任何有效的子聚合类型。
数据准备:一个在线商店的商品索引,包括商品名称和价格字段
POST /product_v4/_bulk
{ "index": {} }
{ "product_name": "小滴课堂永久会员", "price" : 2000 }
{ "index": {} }
{ "product_name": "JVM专题课程", "price" : 200 }
{ "index": {} }
{ "product_name": "SpringBoot3.X最佳实践", "price" : 300 }
{ "index": {} }
{ "product_name": "高并发项目大课", "price" : 1500 }
{ "index": {} }
{ "product_name": "海量数据项目大课", "price" : 4120 }
{ "index": {} }
{ "product_name": "监控告警Prometheus最佳实践", "price" : 180 }
{ "index": {} }
{ "product_name": "全栈工程师学习路线", "price" : 250 }
{ "index": {} }
{ "product_name": "自动化测试平台大课", "price" : 4770 }
{ "index": {} }
{ "product_name": "小滴课堂-老王分手最佳实践", "price" : 400 }
{ "index": {} }
{ "product_name": "小滴课堂-大钊会所按摩往事", "price" : 150 }
案例说明:使用 range 聚合查询将商品按价格范围进行分桶,并计算每个分桶内的商品数量。
GET /product_v4/_search
{
"size": 0,
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 100 },
{ "from": 100, "to": 200 },
{ "from": 200 }
]
}
}
}
}
使用 Spring Data Elasticsearch
基于 Spring Data 的标准化数据访问技术,简化了与 Elasticsearch 的集成。
提供了丰富的 CRUD 操作和查询方法,简化了数据访问,包括自动化的索引管理和映射
Spring Data Elasticsearch 对于一些高级功能和复杂查询可能不够灵活,需要额外定制处理
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
spring.elasticsearch.uris=http://112.74.167.42:9200
什么是ElasticsearchTemplate
是 Spring Data Elasticsearch 提供的一个核心类,是 ElasticsearchClient 的一个具体实现
用于在 Spring Boot 中操作 Elasticsearch 进行数据的存取和查询
提供了一组方法来执行各种操作,如保存、更新、删除和查询文档,执行聚合操作等
ElasticsearchTemplate 的一些常用方法
save(Object): 保存一个对象到 Elasticsearch 中。
index(IndexQuery): 使用 IndexQuery 对象执行索引操作。
delete(String, String): 删除指定索引和类型的文档。
get(String, String): 获取指定索引和类型的文档。
update(UpdateQuery): 使用 UpdateQuery 对象执行更新操作。
search(SearchQuery, Class): 执行搜索查询,并将结果映射为指定类型的对象。
count(SearchQuery, Class): 执行搜索查询,并返回结果的计数
ElasticsearchTemplate 常见注解配置(都是属于spring data elasticsearch)
@Id 指定主键
@Document指定实体类和索引对应关系
indexName:索引名称
@Field指定普通属性
type 对应Elasticsearch中属性类型,使用FiledType枚举快速获取。
text 类型能被分词
keywords 不能被分词
index 是否创建索引,作为搜索条件时index必须为true
analyzer 指定分词器类型。
创建DTO
@Document(indexName = "video")
public class VideoDTO {
@Id
@Field(type = FieldType.Text, index = false)
private Long id;
@Field(type = FieldType.Text)
private String title;
@Field(type = FieldType.Text)
private String description;
@Field(type = FieldType.Keyword)
private String category;
@Field(type = FieldType.Integer)
private Integer duration;
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime createTime;
public VideoDTO(){}
public VideoDTO(Long id, String title, String description, Integer duration,String category) {
this.id = id;
this.title = title;
this.description = description;
this.duration = duration;
this.createTime = LocalDateTime.now();
this.category = category;
}
//省略set get方法
}
创建测试方法
@SpringBootTest
class XdclassEsProjectApplicationTests {
@Autowired
private ElasticsearchTemplate restTemplate;
/**
* 判断索引是否存在索引
*/
@Test
void existsIndex() {
IndexOperations indexOperations = restTemplate.indexOps(VideoDTO.class);
boolean exists = indexOperations.exists();
System.out.println(exists);
}
/**
* 创建索引
*/
@Test
void createIndex() {
// spring data es所有索引操作都在这个接口
IndexOperations indexOperations = restTemplate.indexOps(VideoDTO.class);
// 是否存在,存在则删除
if(indexOperations.exists()){
indexOperations.delete();
}
// 创建索引
indexOperations.create();
//设置映射: 在正式开发中,几乎不会使用框架创建索引或设置映射,这是架构或者管理员的工作,不适合使用代码实现
restTemplate.indexOps(VideoDTO.class).putMapping();
}
/**
* 删除索引
*/
@Test
void deleteIndex() {
IndexOperations indexOperations = restTemplate.indexOps(VideoDTO.class);
boolean delete = indexOperations.delete();
System.out.println(delete);
}
}
案例一 新增文档
@Test
void insert(){
VideoDTO videoDTO = new VideoDTO();
videoDTO.setId(1L);
videoDTO.setTitle("小滴课堂架构大课和Spring Cloud");
videoDTO.setCreateTime(LocalDateTime.now());
videoDTO.setDuration(100);
videoDTO.setCategory("后端");
videoDTO.setDescription("这个是综合大型课程,包括了jvm,redis,新版spring boot3.x,架构,监控,性能优化,算法,高并发等多方面内容");
VideoDTO saved = restTemplate.save(videoDTO);
System.out.println(saved);
}
案例二 更新文档
@Test
void update(){
VideoDTO videoDTO = new VideoDTO();
videoDTO.setId(1L);
videoDTO.setTitle("小滴课堂架构大课和Spring Cloud V2");
videoDTO.setCreateTime(LocalDateTime.now());
videoDTO.setDuration(102);
videoDTO.setCategory("后端");
videoDTO.setDescription("这个是综合大型课程,包括了jvm,redis,新版spring boot3.x,架构,监控,性能优化,算法,高并发等多方面内容");
VideoDTO saved = restTemplate.save(videoDTO);
System.out.println(saved);
}
案例三 批量插入
@Test
void batchInsert() {
List<VideoDTO> list = new ArrayList<>();
list.add(new VideoDTO(2L, "老王录制的按摩课程", "主要按摩和会所推荐", 123, "后端"));
list.add(new VideoDTO(3L, "冰冰的前端性能优化", "前端高手系列", 100042, "前端"));
list.add(new VideoDTO(4L, "海量数据项目大课", "D哥的后端+大数据综合课程", 5432345, "后端"));
list.add(new VideoDTO(5L, "小滴课堂永久会员", "可以看海量专题课程,IT技术持续充电平台", 6542, "后端"));
list.add(new VideoDTO(6L, "大钊-前端低代码平台", "高效开发底层基础平台,效能平台案例", 53422, "前端"));
list.add(new VideoDTO(7L, "自动化测试平台大课", "微服务架构下的spring cloud架构大课,包括jvm,效能平台", 6542, "后端"));
Iterable<VideoDTO> result = restTemplate.save(list);
System.out.println(result);
}
案例四 根据主键查询
@Test
void searchById(){
VideoDTO videoDTO = restTemplate.get("3", VideoDTO.class);
assert videoDTO != null;
System.out.println(videoDTO);
}
案例五 根据id删除
@Test
void deleteById() {
String delete = restTemplate.delete("2", VideoDTO.class);
System.out.println(delete);
}
ElasticSearch的Query接口
NativeQuery案例
案例一:搜索全部
/**
- 查询所有
*/
@Test
void searchAll(){
SearchHits<VideoDTO> search = restTemplate.search(Query.findAll(), VideoDTO.class);
List<SearchHit<VideoDTO>> searchHits = search.getSearchHits();
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
案例二:匹配搜索
/**
* match查询
*/
@Test
void matchQuery(){
Query query = NativeQuery.builder().withQuery(q -> q
.match(m -> m
.field("description") //字段
.query("spring") //值
)).build();
SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
案例三:分页搜索
/**
* 分页查询
*/
@Test
void pageSearch() {
Query query = NativeQuery.builder().withQuery(Query.findAll())
.withPageable(Pageable.ofSize(3).withPage(0)).build();
SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
案例四:搜索排序,withSort() 需要传入 Sort 对象,.by代表根据一个字段进行排序
.ascending() 方法:默认的,正序排序
.descending()方法:倒叙排序
/**
* 排序查询,根据时长降序排列
*/
@Test
void sortSearch() {
Query query = NativeQuery.builder().withQuery(Query.findAll())
.withPageable(Pageable.ofSize(10).withPage(0))
.withSort(Sort.by("duration").descending()).build();
SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
StringQuery 案例
案例:布尔must查询,搜索标题有 架构 关键词,描述有 spring关键字,时长范围是 10~6000之间的
原始DSL查询
GET /video/_search
{
"query": {
"bool": {
"must": [{
"match": {
"title": "架构"
}
}, {
"match": {
"description": "spring"
}
}, {
"range": {
"duration": {
"gte": 10,
"lte": 6000
}
}
}]
}
}
}
SpringBoot+SpringData查询
@Test
void stringQuery() {
//搜索标题有 架构 关键词,描述有 spring关键字,时长范围是 10~6000之间的
String dsl = """
{"bool":{"must":[{"match":{"title":"架构"}},{"match":{"description":"spring"}},{"range":{"duration":{"gte":10,"lte":6000}}}]}}
""";
Query query = new StringQuery(dsl);
List<SearchHit<VideoDTO>> searchHitList = restTemplate.search(query, VideoDTO.class).getSearchHits();
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHitList.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
聚合搜索案例实战
方案一:可以使用原始DSL进行处理
方案二:使用NativeQuery完成聚合搜索
案例实战:统计不同分类下的视频数量
/**
* 聚合查询
*/
@Test
void aggQuery() {
Query query = NativeQuery.builder()
.withAggregation("category_group", Aggregation.of(a -> a
.terms(ta -> ta.field("category").size(2))))
.build();
SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);
//获取聚合数据
ElasticsearchAggregations aggregationsContainer = (ElasticsearchAggregations) searchHits.getAggregations();
Map<String, ElasticsearchAggregation> aggregations = Objects.requireNonNull(aggregationsContainer).aggregationsAsMap();
//获取对应名称的聚合
ElasticsearchAggregation aggregation = aggregations.get("category_group");
Buckets<StringTermsBucket> buckets = aggregation.aggregation().getAggregate().sterms().buckets();
//打印聚合信息
buckets.array().forEach(bucket -> {
System.out.println("组名:"+bucket.key().stringValue() + ", 值" + bucket.docCount());
});
// 获得searchHits,进行遍历得到content
List<VideoDTO> videoDTOS = new ArrayList<>();
searchHits.forEach(hit -> {
videoDTOS.add(hit.getContent());
});
System.out.println(videoDTOS);
}
GET _cat/nodes?v
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.17.0.1 57 84 1 0.00 0.02 0.05 cdfhilmrstw * node-1
C:Coordinating Node(协调节点):负责请求的路由和负载均衡,不存储数据。
D:Data Node(数据节点):存储数据并执行数据相关的操作,如索引和搜索。
F:Fetch Node(提取节点):从其他节点获取数据,协助执行分布式搜索。
H:HTTP Node(HTTP 节点):可以使用HTTP协议与之通信的节点。
I:Ingest Node(摄取节点):对文档进行预处理,如转换和过滤。
L:Machine Learning Node(机器学习节点):运行机器学习任务。
M:Master Node(主节点):负责集群管理和控制,如选主、分配分片等。
R:Remote Client Node(远程客户端节点):允许远程客户端连接的节点。
S:Scripting Node(脚本节点):负责脚本的评估和执行。
T:Transform Node(转换节点):执行转换任务,将源索引数据转换为目标索引。
W:ML Datafeed Node(机器学习数据源节点):用于处理机器学习数据源。
默认的是主节点+数据节点(master+data) ,节点可以成为主节点的资格,又可以存储数据
node.master: true
node.data: true
节点没有成为主节点的资格,不参与选举,只存储数据
node.master: false
node.data: true
不能成为主节点,也不存储数据,主要是进行负载均衡
node.master: false
node.data: false
需要三台服务器,以下操作在三台服务器上均执行
上传安装包到 /usr/local/software/elk_test目录
解压
tar -zxvf elasticsearch-8.4.1-linux-x86_64.tar.gz
新建一个用户,安全考虑,elasticsearch默认不允许以root账号运行
创建用户:useradd es_user
设置密码:passwd es_user
修改目录权限
# chmod是更改文件的权限
# chown是改改文件的属主与属组
# chgrp只是更改文件的属组。
chgrp -R es_user /usr/local/software/elk_test/elasticsearch-8.4.1
chown -R es_user /usr/local/software/elk_test/elasticsearch-8.4.1
chmod -R 777 /usr/local/software/elk_test/elasticsearch-8.4.1
修改文件和进程最大打开数,需要root用户,如果系统本身有这个文件最大打开数和进程最大打开数配置,则不用
在文件内容最后添加后面两行(切记*不能省略)
vim /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
修改虚拟内存空间,默认太小
在配置文件中改配置 最后一行上加上
vim /etc/sysctl.conf
vm.max_map_count=262144
保存退出后执行 sysctl -p(立即生效)
修改elasticsearch的JVM内存,机器内存不足,常规线上推荐16到24G内存
vim config/jvm.options
-Xms1g
-Xmx1g
三台服务器构建3个节点(修改discovery.seed_hosts和cluster.initial_master_nodes)
节点一
vim config/elasticsearch.yml
cluster.name: xdclass-cluster
node.name: node-1
path.data: /usr/local/software/elk_test/elasticsearch-8.4.1/data
path.logs: /usr/local/software/elk_test/elasticsearch-8.4.1/logs
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["172.31.101.11:9300","172.31.101.13:9300","172.31.101.12:9300"]
cluster.initial_master_nodes: ["172.31.101.11:9300","172.31.101.13:9300","172.31.101.12:9300"]
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
ingest.geoip.downloader.enabled: false
节点二
vim config/elasticsearch.yml
cluster.name: xdclass-cluster
node.name: node-2
path.data: /usr/local/software/elk_test/elasticsearch-8.4.1/data
path.logs: /usr/local/software/elk_test/elasticsearch-8.4.1/logs
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["172.31.101.11:9300","172.31.101.13:9300","172.31.101.12:9300"]
cluster.initial_master_nodes: ["172.31.101.11:9300","172.31.101.13:9300","172.31.101.12:9300"]
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
ingest.geoip.downloader.enabled: false
节点三
vim config/elasticsearch.yml
cluster.name: xdclass-cluster
node.name: node-3
path.data: /usr/local/software/elk_test/elasticsearch-8.4.1/data
path.logs: /usr/local/software/elk_test/elasticsearch-8.4.1/logs
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["172.31.101.11:9300","172.31.101.13:9300","172.31.101.12:9300"]
cluster.initial_master_nodes: ["172.31.101.11:9300","172.31.101.13:9300","172.31.101.12:9300"]
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
ingest.geoip.downloader.enabled: false
配置说明
discovery.seed_hosts参数:
功能:discovery.seed_hosts参数用于配置集群中用于发现其他节点的主机名或IP地址列表。
作用:每个节点通过这个参数指定其他节点的地址,以便在启动时进行发现和加入集群。
配置:在每个节点的elasticsearch.yml配置文件中设置该参数,指定其他节点的地址,多个地址使用逗号分隔。
cluster.initial_master_nodes参数:
功能:cluster.initial_master_nodes参数用于配置初始主节点的名称。
作用:当集群启动时,用于指定初始的主节点,以启动集群的选主过程。
配置:只需在初始启动的几个节点的elasticsearch.yml配置文件中设置该参数,列出节点名称。
注意
启动ElasticSearch
切换到es_user用户启动, 进入bin目录下启动, &为后台启动,再次提示es消息时 Ctrl + c 跳出
./elasticsearch &
常见命令,可以用postman访问(网络安全组记得开发端口)
#查看集群健康情况
http://112.74.167.42:9200/_cluster/health
#查看分片情况
http://112.74.167.42:9200/_cat/shards?v=true&pretty
#查看节点分布情况
http://112.74.167.42:9200/_cat/nodes?v=true&pretty
#查看索引列表
http://112.74.167.42:9200/_cat/indices?v=true&pretty
集群状态说明
green:所有的主分片和副本分片都正常运行。
yellow:所有的主分片都正常运行,但有部分副本分片运行不正常。
red:主分片没能正常运行
spring.elasticsearch.uris=http://112.74.167.42:9200,http://112.74.50.221:9200,http://120.79.53.241:9200
PUT /xdclass_shop_v1
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "keyword"
},
"summary": {
"type": "text"
},
"price": {
"type": "float"
}
}
}
}
PUT /xdclass_shop_v1/_bulk
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "1", "title": "Spring Boot","summary":"this is a summary Spring Boot video", "price": 9.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "2", "title": "java","summary":"this is a summary java video", "price": 19.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "3", "title": "Spring Cloud","summary":"this is a summary Spring Cloud video", "price": 29.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "4", "title": "Spring_Boot", "summary":"this is a summary Spring_Boot video","price": 59.99 }
{ "index": { "_index": "xdclass_shop_v1" } }
{ "id": "5", "title": "SpringBoot","summary":"this is a summary SpringBoot video", "price": 0.99 }
硬件资源优化:
内存分配
存储器选择
使用高性能的存储器,如SSD,以提高索引和检索速度
SSD的读写速度更快,适合高吞吐量的应用场景。
CPU和网络资源
分片和副本优化:
合理设置分片数量
过多的分片会增加CPU和内存的开销,因此要根据数据量、节点数量和性能需求来确定分片的数量。
一般建议每个节点上不超过20个分片
考虑副本数量
根据可用资源、数据可靠性和负载均衡等因素,设置合适的副本数量
至少应设置一个副本,以提高数据的冗余和可用性。
不是副本越多,检索性能越高,增加副本数量会消耗额外的存储空间和计算资源,
索引和搜索优化
映射和数据类型
根据实际需求,选择合适的数据类型和映射设置
避免不必要的字段索引,尽可能减少数据在硬盘上的存储空间。
分词和分析器
根据实际需求,选择合适的分词器和分析器,以优化搜索结果。
了解不同分析器的性能特点,根据业务需求进行选择
查询和过滤器
使用合适的查询类型和过滤器,以减少不必要的计算和数据传输
尽量避免全文搜索和正则表达式等开销较大的查询操作。
缓存和缓冲区优化:
缓存大小
在Elasticsearch的JVM堆内存中配置合适的缓存大小,以加速热数据的访问
可以根据节点的角色和负载需求来调整缓存设置。
索引排序字段
选择合适的索引排序字段,以提高排序操作的性能
对于经常需要排序的字段,可以为其创建索引,或者选择合适的字段数据类型。
监控和日志优化
监控集群性能
使用Elasticsearch提供的监控工具如Elastic Stack的Elasticsearch监控、X-Pack或其他第三方监控工具
实时监控集群的健康状态、吞吐量、查询延迟和磁盘使用情况等关键指标。
集群规划和部署:
多节点集群
节点类型和角色
根据节点的硬件配置和功能需求,将节点设置为合适的类型和角色
如数据节点、主节点、协调节点等,以实现负载均衡和高可用性。
性能测试和优化:
压力测试
使用性能测试工具模拟真实的负载,评估集群的性能极限和瓶颈
根据测试结果,优化硬件资源、配置参数和查询操作等。
日常性能调优
升级和版本管理:
计划升级
定期考虑升级Elasticsearch版本,以获取新功能、性能改进和安全修复。
在升级过程中,确保备份数据并进行合理的测试。
版本管理
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。