ElasticSearch学习笔记
约 5447 字大约 18 分钟
2025-01-16
1.ElasticSearch是什么
1.1ElasticSearch简介
Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎
ElasticSearch特点:
- 分布式的文件存储,每个字段都被索引且可用于搜索。
- 分布式的实时分析搜索引擎,海量数据下近实时秒级响应。
- 简单的restful api,天生的兼容多语言开发。
- 易扩展,处理PB级结构化或非结构化数据
ElasticSearch缺点:
- 由于 Elasticsearch 需要处理大量的数据,因此需要较高的硬件性能和存储空间。
- 由于 Elasticsearch 是一个分布式系统,可能存在数据一致性问题
适用场景:
- 日志分析:Elasticsearch 可以快速地搜索和分析大量的日志数据。
- 搜索引擎:Elasticsearch 可以作为搜索引擎用于搜索和排序数据。
- 数据分析:Elasticsearch 可以用于数据分析,支持聚合和分析数据。
- 地理位置搜索:Elasticsearch 支持地理位置搜索,可以用于地图应用等。
1.2ES与Solr对比
- Solr是Apache Lucene项目的开源企业搜索平台。其主要功能包括全文检索、命中标示、分面搜索、动态聚类、数据库集成,以及富文本(如Word、PDF)的处理;Solr是高度可扩展的,并提供了分布式搜索和索引复制功能
- 当单纯的对已有数据进行搜索时,Solr更快;当实时建立索引时,Solr会产生io阻塞,查询性能较差
- 随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化。
1.3ES与MySQL对比
- 结论:ElasticSearch比MySQL查询快,原因如下:
- 基于分词后的全文检索:
- 例如select * from test where name like ‘%张三%’,对于mysql来说,因为索引失效,会进行全表检索;
- 对es而言分词后,每个字都可以利用FST高速找到倒排索引的位置,并迅速获取文档id列表,大大的提升了性能,减少了磁盘IO
- 精确检索:
- 进行精确检索,有些时候可能mysql要快一些,当mysql的非聚合索引引用上了聚合索引,无需回表,则速度上可能更快;
- es还是通过FST找到倒排索引的位置比获取文档id列表,再根据文档id获取文档并根据相关度进行排序
- 但是es还有个优势,就是es即天然的分布式能够在大量数据搜索时可以通过分片降低检索规模,并且可以通过并行检索提升效率,用filter时,更是可以直接跳过检索直接走缓存
1.4ES基本概念
1.4.1Index-索引
Index,即索引,关键词有两种用法,可用作动词、或者名词
- 动词,相当于MySQL中的insert、插入
- 名词,相当于MySQL中的Database、数据库
1.4.2Type-类型
- 在Index(索引)中,可以定义一个或多个类型,每种类型的数据放一起;类似于MySQL中数据库中可以定义一个或多个表(Table);
- ES7、8版本差异:
- Elasticsearch 7. X URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。
- Elasticsearch 8.X 不再支持URL中的type参数。ElasticSearch8开始,将索引从多类型迁移到单类型,每种类型文档一个独立索引
- 原因:Elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降,去掉type就是为了提高ES处理数据的效率。
1.4.3Document-文档
- 保存到某个索引(Index)下,某种类型(Type)的一个数据(Document),文档是JSON格式的
- 一个Document就像是MySQL中某个表的一条记录.
1.4.4倒排索引
由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)
- 索引存储示例
将整句拆分为单词,将单词的值与索引存储起来,就可以根据单词查询索引位置,然后根据检索条件的相关性得分进行排序
2.安装ElasticSearch
2.1基于Docker安装ES与Kibana
2.1.1下载镜像文件
elasticsearch与kibana版本是同步的
docker pull elasticsearch:7.4.2 #存储和检索数据
docker pull kibana:7.4.2 #可视化检索数据
2.1.2创建实例
(1)创建ElasticSearch实例
#先将es的数据与配置与需要映射的文件夹创建好
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
#配置es地址
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
#保证权限
chmod -R 777 /mydata/elasticsearch/
#创建并启动实例
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
注意:-e ES_JAVA_OPTS="-Xms64m -Xmx256m" \ 测试环境下, 设置 ES 的初始内存和最大内存, 否则会导致占用内存过多
效果:
ES的9200和9300端口区别
9200作为Http协议,主要用于外部通讯
9300作为Tcp协议,jar之间就是通过tcp协议通讯 .ES集群之间是通过9300进行通讯
测试是否创建成功
192.168.234.128:9200 虚拟机地址+9200
记得防火墙开放9200端口
(2)创建Kibana实例
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.234.128:9200 -p 5601:5601 \
-d kibana:7.4.2
测试是否创建成功
192.168.234.128:5601

2.1.3设置es和kibana自启动
先不设置,担心内存不够
# 设置es在docker开启的时候启动
docker update elasticsearch --restart=always
# 设置Kibana在docker开启的时候启动
docker update kibana --restart=always
2.2基于Windows安装ES与Kibana
待写
3.初级检索
3.1查看节点/索引
#查看所有节点
GET /_cat/nodes
#查看所有节点
GET /_cat/health
#查看主节点
GET /_cat/master
#查看所有索引——show databases;
GET /_cat/indices
3.2索引(保存)文档
3.2.1PUT方法
保存一个数据, 保存在哪个索引的哪个类型下, 指定用哪个唯一标识
3.2.2POST方法
3.2.3PUT和POST的区别
相同
PUT与POST都可以新增与修改文档
PUT与POST修改文档都是指定id再索引一次
不同—新增上
- PUT新增只能自定义id,不能自动生成(PUT本就是设定用来修改的)
- POST新增可以不带id(自动生成),也可以自定义id
补充
- PUT修改必须指定文档id、否则报错
- POST修改若指定存在的文档id,则为修改;若指定不存在或者没有接上文档id,则为新增
3.3查询指定id文档
3.4更新文档
# 之前就是这种写法:版本直接加
POST customer/external/1
{
"name": "John Doe2"
}
# 会对比之前的内容,相同则版本不变
POST customer/external/1/_update
{
"doc": {
"name": "John Doe2"
}
}
# 更新同时增加属性
PUT customer/external/1
{
"name": "John Doe3"
}
# 更新同时增加属性
POST customer/external/1/_update
{
"doc": {
"name": "Jane Doe",
"age": 20
}
}
POST方式一与方式二的区别是否带update
带_update的POST更新:会对比源文档数据, 如果相同不会有什么操作, 文档 version 不增加
不带_update的POST:总会将数据重新保存并增加 version 版本
PUT 操作总会将数据重新保存并增加 version 版本;
3.5删除索引
#删除文档
DELETE customer/external/1
#删除索引
DELETE customer
3.6bulk批量API
在单个 API 调用中执行多个索引或删除操作。这减少了开销并且可以大大提高索引速度。
3.6.1语法格式
{ action: { metadata }} //action: 操作; metadata:对哪一个数据进行操作
{ request body } //操作的内容
{ action: { metadata }}
两个一组
3.6.2复杂bulk
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
{ "index": { "_index": "website", "_type": "blog" }}
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123"}}
{ "doc" : {"title" : "My updated blog post"} }
bulk API 以此按顺序执行所有的 action(动作) 。 如果一个单个的动作因任何原因而失败,它将继续处理它后面剩余的动作。 当 bulk API 返回时, 它将提供每个动作的状态(与发送的顺序相同) , 所以您可以检查是否一个指定的动作是不是失败了。
4.进阶检索
4.1导入数据
数据链接:
https://gitee.com/zhourui815/gulimall/blob/master/doc/es%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE.json#
POST bank/account/_bulk
测试数据
4.2SearchAPI
ES 支持两种基本方式检索 :
- 一个是通过使用 REST request URI 发送搜索参数(uri+检索参数)
- 另一个是通过使用 REST request body 来发送它们(uri+请求体
4.2.1检索信息
**一切检索从_search 开始 **
4.2.2请求参数方式检索
#请求参数方式检索
q=* 代表要查询的字段类似于select *
sort=account_number:asc 代表按照account_number字段排序,升序
GET bank/_search?q=*&sort=account_number:asc
4.2.3uri+请求体进行检索
#uri+请求体进行检索
GET bank/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"account_number": {
"order": "desc"
}
}
]
}
4.3Query DSL
url+请求体
Elasticsearch 提供了一个可以执行查询的 Json 风格的 DSL( domain-specific language 领域特定语言) 。 这个被称为 Query DSL。 该查询语言非常全面, 并且刚开始的时候感觉有点复杂,真正学好它的方法是从一些基础的示例开始的。
4.3.1语法格式
# 一个查询语句 的典型结构
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
# 如果是针对某个字段, 那么它的结构如下:
{
QUERY_NAME: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
4.3.2查询示例
查询bank索引并按照account_number字段降序排序,分页大小为5
#基本查询示例
GET bank/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 5,
"sort": [
{
"account_number": {
"order": "desc"
}
}
]
}
- query 定义如何查询,
- match_all 查询类型【代表查询所有的所有】 ,
- es 中可以在 query 中组合非常多的查 询类型完成复杂查询
- 除了 query 参数之外, 我们也可以传递其它的参数以改变查询结果。 如 sort, size
- from+size 限定, 完成分页功能
- sort 排序, 多字段排序, 会在前序字段相等时后续字段内部排序, 否则以前序为准
4.3.3match匹配
match 返回 account_number=20 的
#match 基本类型(非字符串类型),精确匹配
GET bank/_search
{
"query": {
"match": {
"account_number": "20"
}
}
}
最终查询出 address 中包含 mill 单词的所有记录 match 当搜索字符串类型的时候, 会进行全文检索, 并且每条记录有相关性得分。
#match 字符串类型,全文检索,模糊匹配
GET bank/_search
{
"query": {
"match": {
"address": "mill"
}
}
}
最终查询出 address 中包含 mill 或者 road 或者 mill road 的所有记录, 并给出相关性得分
#match 字符串, 多个单词( 分词+全文检索)
GET bank/_search
{
"query": {
"match": {
"address": "mill road"
}
}
}
4.3.4match_phrase短语匹配
短语匹配:将需要匹配的值当成一个整体单词( 不分词) 进行检索
查出 address 中包含 mill road 的所有记录, 并给出相关性得分
#match_phrase 短语匹配
GET bank/_search
{
"query": {
"match_phrase": {
"address": "mill road"
}
}
}
4.3.5multi_match 多字段匹配
查询 state 或者 address字段 包含 mill
#multi_match 多字段匹配
GET bank/_search
{
"query": {
"multi_match": {
"query": "mill",
"fields": ["address","state"]
}
}
}
4.3.6bool复合查询
bool 用来做复合查询: 复合语句可以合并 任何 其它查询语句, 包括复合语句, 复合语句之间可以互相嵌套, 可以表达非常复杂的逻辑。
(1)must
必须达到 must 列举的所有条件
# must 必须达到 must 列举的所有条件
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "Mill"
}
},
{
"match": {
"gender": "M"
}
}
]
}
}
}
(2)should
应该达到 should 列举的条件, 如果达到会增加相关文档的评分, 并不会改变查询的结果。
如果 query 中只有 should 且只有一种匹配规则, 那么 should 的条件就会被作为默认匹配条件而去改变查询结果
# 应该达到 should列举的条件,如果达到会增加相关文档的评分,并不会改变查询的结果。
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
},
{
"match": {
"gender": "M"
}
}
],
"should": [
{
"match": {
"address": "lane"
}
}
]
}
}
}
(3)must_not
必须不是指定的情况
address 包含 mill, 并且 gender 是 M, 如果 address 里面有 lane 最好不过, 但是匹配的字段必须不是文本类型
#must_not 必须不是指定的情况
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
},
{
"match": {
"gender": "M"
}
}
],
"should": [
{
"match": {
"address": "lane"
}
}
],
"must_not": [
{"match": {
"FIELD": "TEXT"
}}
]
}
}
}
(4)filter结果过滤
并不是所有的查询都需要产生分数, 特别是那些仅用于 “filtering”(过滤) 的文档。 **为了不计算分数 **Elasticsearch 会自动检查场景并且优化查询的执行。
(5)小结
4.3.7term—非text字段检索
和 match 一样。 匹配某个属性的值。 全文检索字段用 match, 其他非 text 字段匹配用 term。
#term 其他非 text 字段检索
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"account_number": {
"value": "970"
}
}
},
{
"match": {
"address": "Mill"
}
}
]
}
}
}
4.3.8aggregations聚合检索
- 最简单的聚合方法大致等于 SQL GROUP BY 和 SQL 聚合函数。
- 在 Elasticsearch 中, 您有执行搜索返回 hits( 命中结果),并且同时返回聚合结果, 把一个响应中的所有 hits( 命中结果) 分隔开的能力。
- 可以执行查询和多个聚合, 并且在一次使用中得到各自的( 任何一个的) 返回结果, 使用一次简洁和简化的 API 来避免网络往返。
写在前面:有很多种聚合方式:terms、avgs聚合

(1)年龄分布、并求出年龄的平均值
搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄, 但不显示这些人的详情(size=0)

(2)求出年龄分布,并在每个年龄求平均薪资
4.4Mapping
4.4.1字段类型
- 字段类型有核心类型、复合类型、地理类型、特定类型以及多字段类型共5种
- 核心类型:
- 字符串:string、text、keyword
- 数字类型:long、integer、short、byte、double、float
- 日期类型:date
- 布尔类型:boolean
- 二进制类型:binary
- 复合类型:数组类型(Array)、对象类型(Object)、嵌套类型(Nested)
- 地理类型:地理坐标(Geo-points)、地理图形(Geo-Shape)
- 特定类型:IP类型(ip)、补全类型(Completion)、令牌计数类型(Token count)、附件类型(attachment)
- 多字段类型:通常用于为不同目的用不同的方法索I同一个字段。例如,string字段可以映射为一个text字段用于全文检索,同样可以映射为一个keyword字段用于排序和聚合
4.4.2映射简介
- 定义:Mapping 是用来定义一个文档( document) , 以及它所包含的属性( field) 是如何存储和索引的。 比如, 使用 mapping 来定义:
- 哪些字符串属性应该被看做全文本属性(full text fields)
- 哪些属性包含数字, 日期或者地理位置。
- 文档中的所有属性是否都能被索引
#查看mapping信息
GET bank/_mapping

- 问题:我们在创建索引是没有指定类型,为什么会查询出来呢?
- 答案:es会根据数据自动猜测的映射类型,自动猜测的映射类型有:
- 布尔型:true或者false,猜测的映射类型为boolean
- 整数:123,猜测的映射类型为long
- 浮点数:123.45,猜测的映射类型为double
- 字符串,有效日期:2024-10-10,猜测的映射类型为date
- 字符串:foo bar,猜测的映射类型为string
4.4.3创建映射
4.4.4更新映射
对于已经存在的映射字段, 我们不能更新。 更新必须创建新的索引进行数据迁移
4.4.5数据迁移
(1)查询之前的映射
GET bank/_mapping
- 将查询的字段映射复制出来
- 假设age字段类型为long,想换成integer类型
(2)新增一个新索引
将复制的属性粘贴到属性中,先不执行
"account_number" : {
"type" : "long"
},
"address" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"age" : {
"type" : "long"
},
"balance" : {
"type" : "long"
},
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"email" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"employer" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"firstname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"gender" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"lastname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"state" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
(3)修改映射
# 创建一个新索引
PUT newbank2
{
"mappings":{
"properties": {
"account_number": {
"type": "long"
},
"address": {
"type": "text"
},
"age": {
"type": "integer"
},
"balance": {
"type": "long"
},
"city": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"email": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"employer": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"firstname": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"gender": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"lastname": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"state": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
(4)数据迁移
#数据迁移
POST _reindex
{
"source": {
"index": "bank"
},
"dest": {
"index": "newbank2"
}
}
迁移成功
4.5分词(待重写)
在ES中,一个 tokenizer( 分词器) 接收一个字符流, 将之分割为独立的 tokens( 词元, 通常是独立的单词) , 然后输出 tokens 流。
- 例如, whitespace tokenizer 遇到空白字符时分割文本。 它会将文本 “Quick brown fox!” 分割为 [Quick, brown, fox!]。
- 该 tokenizer(分词器) 还负责记录各个 term(词条) 的顺序或 position 位置(用于 phrase 短语和 word proximity 词近邻查询) , 以及 term(词条) 所代表的原始 word(单词) 的 start(起始) 和 end(结束) 的 character offsets(字符偏移量) (用于高亮显示搜索的内容) 。
Elasticsearch 提供了很多内置的分词器, 可以用来构建 custom analyzers(自定义分词器) 。
4.5.1安装ik分词器
没有安装分词器之前的效果

**注意: 不能用默认 elasticsearch-plugin install xxx.zip 进行自动安装 **
由于之前映射了plugins目录,所以在/mydata/elasticsearch/plugins/下载elasticsearch-analysis-ik-7.4.2.zip
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
解压
unzip elasticsearch-analysis-ik-7.4.2.zip
删除zip文件
rm –rf *.zip
将elasticsearch文件夹下的所有文件 移动至ik目录下(自建目录)
使用xftp进行移动
修改ik文件夹权限
chmod -R 777 ik
确认是否安装好了分词器
#进入容器内部
docker exec -it 容器 id /bin/bash
#切换目录
cd bin
#列出安装的plugin
elasticsearch plugin list
发现ik后重启容器
docker restart elasticsearch
4.5.2测试分词器
(1)使用默认分词器
(2)使用ik_smart分词器
(3)使用ik_max_word 分词器
(4)总结
能够看出不同的分词器, 分词有明显的区别, 所以以后定义一个索引不能再使用默认的 mapping 了, 要手工建立 mapping, 因为要选择分词器。
4.5.3自定义词库
4.5.4未自定义词库
自定义词库测试
- 创建词库
在4.6:搭建好nginx的基础上,搭建nginx在8.x章节
cd /mydata/nginx/html/
#创建自定义词库
vim fenci.txt
添加新词
- 修改分词器配置文件
vim /mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml
- 重启es
docker restart elasticsearch
- 效果
5.SpringBoot整合ES
5.1xml配置
SpringBoot想和ES进行通信传递消息的方式有两种:tcp、http
9300: TCP
- 需要的jar包:spring-data-elasticsearch:transport-api.jar;
- springboot 版本不同, transport-api.jar 不同, 不能适配 es 版本
- 7.x 已经不建议使用, 8 以后就要废弃
9200: HTTP,有以下四种通信方式
- JestClient: 非官方, 更新慢
- RestTemplate: 模拟发 HTTP 请求, ES 很多操作需要自己封装, 麻烦
- HttpClient: 同上
- Elasticsearch-Rest-Client: 官方 RestClient, 封装了 ES 操作, API 层次分明, 上手简单
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.2</version>
</dependency>
可能出现bug,换成以下依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.2</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.4.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.4.2</version>
</dependency>
5.2配置类
主启动类添加注解:@EnableDiscoverClient

添加:配置类

5.3测试连接

测试结果:

5.4测试使用
5.4.1索引(新增)记录

- 测试结果

5.4.2获取数据

#match 字符串类型全文检索
GET bank/_search
{
"query": {
"match": {
"address": "mill"
}
}
}
两个查询结果相同
5.4.3聚合查询


5.4.4结果转为对象
具体结果在下面那个hits里

利用json生成javabean,并使用lombok

- 测试

- 结果