文章目录
核心概念
节点
一个节点就是一个ElasticSearch的实例,注意,一个节点≠一台服务器,因为一台服务器可以运行多个ES进程
角色Roles
ES的角色分类:
-
主节点 active master
一个集群只有一个,主要作用的对集群的管理
-
候选节点master-eligible
主节点宕机,候选节点参与选举
-
数据节点 data node
保存已编入索引的文档的分片。数据节点处理数据相关操作,如 CRUD、搜索和聚合。这些操作是 I/O 密集型、内存密集型和 CPU 密集型的。监控这些资源并在它们过载时添加更多数据节点非常重要。
-
预处理节点ingest-node
数据写入之前的预处理操作
如果node.reles为缺省配置,那么当前节点具备所有的角色
索引index
在ES中,索引类似于Mysql中的表(仅针对 ES 7.x 以后版本),只是类似 索引并不等于表
在ES中,索引在不同的环境下有以下几个意思:
-
表示源文件数据
当做数据的载体,类似于数据表。通常说集群中有User索引就表示当前ES服务中存在User这一张“表”
-
表示索引文件
比如倒排索引,目的是加速文件的检索。索引文件和源数据文件是完全独立的,互不影响。
-
表示创建数据的动作
新增一条数据,可以所是索引了一个文档,或索引了一条数据
索引的组成部分:
- aliases:索引别名
- mappings:映射,定义了索引包含的哪些字段,以及字段的类型、长度、分词器等
- settings:索引设置,比如分片数量、副本数量等
类型type
ElasticSearch7.x之后的版本已删除此概念
在之前的版本中,一个索引可以包含多个类型,每个类型代表了不同的文档结构。从ES7.X开始,类型已经被弃用,一个索引只有一个类型
ES7.X
-
不推荐在请求中指定类型。
索引文档不再需要文档type。新的索引API适用于
PUT {index}/_doc/{id}
于显示ID 和PUT {index}/_doc
自动生成id。在ES7.x中_doc
是路径的永久部分,表示端点名称,而不是文档类型 -
索引创建、索引模板和映射API中的include_type_name参数将默认为false,完全设置参数将导致弃用警告
-
_default_
映射类型被删除 。
ES8.X
- 不再支持在请求中指定类型
- 该
include_type_name
参数被删除。
文档document
文档是ES中最小数据单元。是一个具有结构化JSON格式的记录,文档可以被索引并进行搜索、更新、删除操作
文档元数据中,字段以下划线开头,为系统字段,用于标注文档相关信息
-
_index:文档所属索引名
-
_type:文档所属的类型名
-
_id: 文档唯一id
-
_version:文档版本号,修改/删除操作改字段都会+1
-
_seq_no:和version一样文档数据更改,该字段值自增。Shard级别严格递增,保存后写入DOC的
_seq_no
大于先写入DOC的_seq_no
-
_primary_term:恢复数据时处理多个文档的
_seq_no
一样时的冲突,避免Primary Shard上写入的数据被覆盖。每当Primary Sharding重新分配时该字段+1 -
_source:文档的原始JSON数据
ElasticSearch索引操作
参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/7.17/index.html
创建索引
PUT /索引名称
命名规范:
- 以小写字母命名
- 不要用驼峰命名
- 多个单词用下划线分割
ES的索引创建成功后,以下属性将不可更改
- 索引名称
- 主分片数量
- 字段类型
创建索引
PUT /sys_user
执行结果
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "sys_user"
}
查询索引
GET /索引名称
#查询索引
GET /sys_user
#es_db是否存在
HEAD /sys_user
删除索引
DELETE /索引名
设置settings
创建索引的时候指定 settings
PUT <index_name>
{
"settings": {}
}
创建索引时可以设置分片数和副本数
# 创建索引时指定分片数为3, 每个主分片的副本数量为 2
PUT /sys_user
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
}
}
# 创建索引时可以指定IK分词器作为默认分词器
PUT /sys_user
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2,
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
}
静态索引设置
只能在创建索引时或在关闭状态的索引上设置
- index.number_of_shards,索引的主分片的个数,默认为 1
动态索引设置
可以使用 _setting
实时更改的配置
- index.number_of_replicas:每个主分区的副本数。默认为1,允许配置为0
- index.refresh_interval:执行刷新操作的频率,默认为1s. 可以设置 -1 为禁用刷新。
- index.max_result_window:from + size 搜索此索引 的最大值,默认为 10000。
使用 _setting 只能修改允许动态修改的配置项
PUT /sys_user/_settings
{
"index": {
"number_of_replicas": 1
}
}
设置Mappings
ES中的Mappings类似于关系型数据库的表结构
在 Mapping 里也包含了一些属性,比如字段名称、类型、字段使用的分词器、是否评分、是否创建索引等属性,并且在 ES 中一个字段可以有多个类型。ES中Mapping可以分为动态映射和静态映射。
# 查看整个索引信息
GET /<index_name>
# 查看完整的索引 mapping
GET /<index_name>/_mappings
# 查看索引中指定字段的 mapping
GET /<index_name>/_mappings/field/<field_name>
注意事项:
- ES没有隐式类型转换
- ES不支持类型修改
- 生产环境劲浪避免使用动态映射
动态映射
关系型数据库中需要先建表、定义字段的类型,然后才能插入数据。而ES不需要事先定义Mapping映射,在文档写入时会根据字段自动识别类型,这种称为动态映射
自动类型推断规则
其中text会分词后创建倒排索引,而keyword不会分词,直接使用现有的数据建立的另一种索引
# 删除原来的索引
DELETE /sys_user
# 创建文档(ES根据数据类型, 会自动创建映射)
POST /sys_user/_doc/1
{
"username": "hushang",
"age": 24,
"address": "广州天河公园"
}
#获取文档映射
GET /sys_user/_mapping
静态映射
在文档写入之前,创建索引时就指定索引中每个字段的类型、分词器等参数
# 删除原索引
DELETE /sys_user
PUT /sys_user
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"username": {
"type": "keyword"
},
"age": {
"type": "long",
"index": false
},
"address": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
常用Mapping参数配置
参数名称 | 释义 |
---|---|
analyzer | 指定分析器,只有 text 类型字段支持。 |
copy_to | 该参数允许将多个字段的值复制到组字段中,然后可以将其作为单个字段进行查询 |
dynamic | 控制是否可以动态添加新字段,支持以下四个选项: true:(默认)允许动态映射 false:忽略新字段。这些字段不会被索引或搜索,但仍会出现在 _source 返回的命中字段中。这些字段不会添加到映射中,必须显式添加新字段。runtime:新字段作为运行时字段添加到索引中,这些字段没有索引,是 _source 在查询时加载的。strict:如果检测到新字段,则会抛出异常并拒绝文档。必须将新字段显式添加到映射中。 |
doc_values | 为了提升排序和聚合效率,默认true,如果确定不需要对字段进行排序或聚合,也不需要通过脚本访问字段值,则可以禁用doc值以节省磁盘空间(不支持 text 和 annotated_text) |
eager_global_ordinals | 用于聚合的字段上,优化聚合性能。 |
enabled | 是否创建倒排索引,可以对字段操作,也可以对索引操作,如果不创建索引,任然可以检索并在_source元数据中展示,谨慎使用,该状态无法修改。 |
fielddata | 查询时内存数据结构,在首次用当前字段聚合、排序或者在脚本中使用时,需要字段为fielddata数据结构,并且创建倒排索引保存到堆中 |
fields | 给 field 创建多字段,用于不同目的(全文检索或者聚合分析排序) |
format | 用于格式化代码,如 “data”:{ “type”: “data”, “format”: “yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis” } |
index | 是否对创建对当前字段创建倒排索引,默认 true,如果不创建索引,该字段不会通过索引被搜索到,但是仍然会在 source 元数据中展示。 |
norms | 是否禁用评分(在filter和聚合字段上应该禁用) |
null_value | 为 null 值设置默认值 |
search_analyzer | 设置单独的查询时分析器 |
案例,address字段禁用倒排索引
DELETE /sys_user
PUT /sys_user
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
},
"mappings": {
"properties": {
"username": {
"type": "text"
},
"age": {
"type": "long"
},
"address": {
"type": "text",
"index": false # address字段禁用倒排索引
}
}
}
}
# 查询一下索引信息
GET /sys_user
# 添加一个文档
POST /sys_user/_doc/1
{
"username": "hushang",
"age": 24,
"address": "广州天河公园"
}
# 通过关键词进行搜索,就会发现报错了
GET /sys_user/_search
{
"query": {
"match": {
"address": "广州"
}
}
}
dynamic设为true时,一旦有新增字段的文档写入,Mapping 也同时被更新;dynamic设置成strict(严格控制策略),文档写入失败,抛出异常
PUT /sys_user
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
},
"mappings": {
"dynamic": "strict", # dynamic的值设置为strict
"properties": {
"username": {
"type": "text"
},
"age": {
"type": "long"
},
"address": {
"type": "text"
}
}
}
}
# 添加一条文档, 其中加了一个sex性别字段
POST /sys_user/_doc/1
{
"username": "hushang",
"age": 24,
"address": "广州天河公园",
"sex": "男"
}
报错信息如下
修改dynamic后再次插入文档成功
PUT /sys_user/_mapping
{
"dynamic": true
}
注意:对已有字段,一旦已经有数据写入,就不再支持修改字段定义
-
Lucene 实现的倒排索引,一旦生成后,就不允许修改
-
如果希望改变字段类型,可以利用 reindex API,重建索引
使用reindex重建索引
具体使用步骤:
- 如果想推到现有的映射,需要重新创建一个索引
- 把之前索引的数据导入到新索引中
- 删除老索引
- 为新索引起一个别名,为原索引
通过这几个步骤可以实现索引的平滑过渡,并且是零停机
# 创建一个新索引
PUT /sys_user2
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
},
"mappings": {
"dynamic": "strict",
"properties": {
"username": {
"type": "keyword"
},
"age": {
"type": "long",
"index": false
},
"address": {
"type": "text"
},
"sex": {
"type": "text",
"index": false
}
}
}
}
# 把之前索引的数据导入到新索引中
POST _reindex
{
"source": {
"index": "sys_user"
},
"dest": {
"index": "sys_user2"
}
}
# 删除原索引
DELETE /sys_user
# 为新索引起一个别名,为原索引
PUT /sys_user2/_aliases/sys_user
错误示范
如果新索引中没有包含老索引的所有字段,那么就会报错,如下所示
# sys_user索引中有username、age、address、sex四个字段, 新索引只有前面三个字段
PUT /sys_user2
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
},
"mappings": {
"dynamic": "strict",
"properties": {
"username": {
"type": "keyword"
},
"age": {
"type": "long",
"index": false
},
"address": {
"type": "text"
}
}
}
# 然后在进行reindex就会报错
POST _reindex
{
"source": {
"index": "sys_user"
},
"dest": {
"index": "sys_user2"
}
}
ES文档操作
示例数据
PUT /sys_user
{
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
PUT /sys_user/_doc/1
{
"name": "张三",
"sex": 1,
"age": 25,
"address": "广州天河公园",
"remark": "java developer"
}
PUT /sys_user/_doc/2
{
"name": "李四",
"sex": 1,
"age": 28,
"address": "广州荔湾大厦",
"remark": "java assistant"
}
PUT /sys_user/_doc/3
{
"name": "王五",
"sex": 0,
"age": 26,
"address": "广州白云山公园",
"remark": "php developer"
}
PUT /sys_user/_doc/4
{
"name": "赵六",
"sex": 0,
"age": 22,
"address": "长沙橘子洲",
"remark": "python assistant"
}
PUT /sys_user/_doc/5
{
"name": "张龙",
"sex": 0,
"age": 19,
"address": "长沙麓谷企业广场",
"remark": "java architect assistant"
}
PUT /sys_user/_doc/6
{
"name": "赵虎",
"sex": 1,
"age": 32,
"address": "长沙麓谷兴工国际产业园",
"remark": "java architect"
}
PUT /sys_user/_doc/7
{
"name": "李虎",
"sex": 1,
"age": 32,
"address": "广州番禺节能科技园",
"remark": "java architect"
}
PUT /sys_user/_doc/8
{
"name": "张星",
"sex": 1,
"age": 22,
"address": "武汉东湖高新区未来智汇城",
"remark": "golang developer"
}
索引文档
索引文档也就是新增文档数据
[PUT或POST] /索引名/[_doc或_create]/文档id
{
"FIELD": "TEXT"
}
# 创建文档,指定id,如果id不存在,创建新的文档,否则先删除现有文档,再创建新的文档,版本会增加
PUT /sys_user/_doc/1
{
"name": "张三",
"sex": 2,
"age": 18,
"address": "广州天河公园",
"remark": "java developer"
}
#创建文档,ES生成id 需要使用post请求
POST /sys_user/_doc
{
"name": "张11",
"sex": 1,
"age": 11,
"address": "广州天河公园",
"remark": "java developer"
}
PUT 和 POST请求都能进行创建/修改操作。PUT请求需要指定id 对一个具体的资源进行操作,而POST是可以对整个资源集合进行操作的
POST请求如果在url中不指定id那就是ES自动生成一个id,如果url中填了id那就针对这个id文档进行创建/更新
查询文档
# 根据id查询文档
GET /索引名/_doc/文档id
# 条件查询
GET /索引名/_doc/_search
ES对条件查询提供了两种方式
- REST风格的请求URL,直接在URL中将参数传递过去 (了解即可)
- 封装到RequestBody中,这种方式可以定义更加易读的JSON格式
URI Query(了解即可)
#通过URI搜索,使用“q”指定查询字符串,“query string syntax” KV键值对
#条件查询, 如要查询age等于28岁的 _search?q=*:***
GET /es_db/_doc/_search?q=age:28
#范围查询, 如要查询age在25至26岁之间的 _search?q=***[** TO **] 注意: TO 必须为大写
GET /es_db/_doc/_search?q=age[25 TO 26]
#查询年龄小于等于28岁的 :<=
GET /es_db/_doc/_search?q=age:<=28
#查询年龄大于28前的 :>
GET /es_db/_doc/_search?q=age:>28
#分页查询 from=*&size=*
GET /es_db/_doc/_search?q=age[25 TO 26]&from=0&size=1
#对查询结果只输出某些字段 _source=字段,字段
GET /es_db/_doc/_search?_source=name,age
#对查询结果排序 sort=字段:desc/asc
GET /es_db/_doc/_search?sort=age:desc
DSL Query
DSL(Domain Specific Language领域专用语言)查询是使用Elasticsearch的查询语言来构建查询的方式。
# match 匹配查询,会对查询文本分词后匹配
GET /sys_user/_search
{
"query": {
"match": {
"address": "广州天河"
}
}
}
# 上方可以直接省略 _doc , 也是一样的
GET /sys_user/_doc/_search
{
"query": {
"match": {
"address": "广州天河"
}
}
}
# term 词项查询,属于精确查询,不会对查询文本分词
GET /sys_user/_search
{
"query": {
"term": {
"address": "广州天河"
}
}
}
更新文档
-
全量更新,使用
PUT或POST /索引名/_doc/文档id
这种方式会先将现有的文档删除,然后索引一个新文档
-
部分更新,使用
_update
,格式POST /索引名/_update/文档id
_update
文档必须已经存在,更新只会对相应字段做增量修改
- 使用 _update_by_query 更新文档
# 更新复合条件的文档
POST /sys_user/_update_by_query
{
"query": {
"match": {
"_id": "1"
}
},
"script": {
"source": "ctx._source.age = 24",
"lang": "painless"
}
}
# 查询
GET /sys_user/_doc/1
- 并发场景下修改文档
_seq_no
和_primary_term
是对_version
的优化,7.X版本的ES默认使用这种方式控制版本,所以当在高并发环境下使用乐观锁机制修改文档时,要带上当前文档的_seq_no
和_primary_term
进行更新:
# 方式一
POST /es_db/_doc/2?if_seq_no=20&if_primary_term=6
{
"name": "李四xxx"
}
# 方式二
POST /sys_user/_update_by_query
{
"query": {
"match": {
"_id": "1",
"_seq_no": 17,
"_primary_term": 5
}
},
"script": {
"source": "ctx._source.age = 25",
"lang": "painless"
}
}
删除文档
DELETE /索引名/_doc/文档id
文档批量操作
批量操作可以减少网络连接产生的开销,提高性能
- 支持再一次API调用中,对不同的索引进行操作
- 操作中单条失败,并不会影响其他操作
- 返回结果包含了每一条操作执行的结果
批量写入
批量对文档进行写操作是通过_bulk
的API来实现的
-
请求方式:POST
-
请求地址:_bulk
-
请求参数:通过_bulk操作文档,一般至少有两行参数(或偶数行参数)
-
- 第一行参数为指定操作的类型及操作的对象(index,type和id)
- 第二行参数才是操作的数据
参数类似于:
# actionName:表示操作类型,主要有create,index,delete和update
{"actionName":{"_index":"indexName", "_type":"typeName","_id":"id"}}
{"field1":"value1", "field2":"value2"}
批量创建文档create
使用create
POST _bulk
{"create":{"_index":"sys_user","_type":"_doc","_id":10}}
{"name":"王五1","sex":0,"age":26,"address":"广州白云山公园","remark":"php developer"}
{"create":{"_index":"sys_user","_type":"_doc","_id":11}}
{"name":"王五2","sex":0,"age":26,"address":"广州白云山公园","remark":"php developer"}
普通创建或全量替换index
使用index
。如果id指定文档不存在则创建,如果id指定文档存在则全量修改源文档,也也就是先删除再新增
POST _bulk
{"index":{"_index":"sys_user","_type":"_doc","_id":10}}
{"name":"王五1","sex":0,"age":26}
{"index":{"_index":"sys_user","_type":"_doc","_id":11}}
{"name":"王五2","sex":0,"age":26,"address":"广州白云山公园"}
批量修改update
使用update
POST _bulk
{"update":{"_index":"sys_user","_type":"_doc","_id":10}}
{"doc":{"name":"王五11","sex":1}}
{"update":{"_index":"sys_user","_type":"_doc","_id":11}}
{"doc":{"name":"王五22","sex":1,"age":45,"address":"广州白云山公园111"}}
批量删除delete
使用delete
,此时就不需要在添加 field和value这些数据了
POST _bulk
{"delete":{"_index":"sys_user","_type":"_doc","_id":10}}
{"delete":{"_index":"sys_user","_type":"_doc","_id":11}}
组合应用
可以把create
、index
、update
、delete
结合使用
POST _bulk
{"index":{"_index":"sys_user","_type":"_doc","_id":10}}
{"name":"李四","sex":1,"age":21,"address":"广州荔湾大厦","remark":"java assistant"}
{"index":{"_index":"sys_user","_type":"_doc","_id":11}}
{"name":"李四11","sex":1,"age":21,"address":"广州荔湾大厦11","remark":"java assistant"}
{"update":{"_index":"sys_user","_type":"_doc","_id":10}}
{"doc":{"name":"王五","sex":0}}
{"delete":{"_index":"sys_user","_type":"_doc","_id":11}}
批量查询
ES的批量查询可以使用_mget
和_msearch
两种方式
_mget
:需要我们知道文档id,可以指定不同的index,也可以指定返回值source_msearch
:通过字段查询来进行一个批量的查找
_mget
案例
# 可以通过ID批量获取不同index的数据
GET _mget
{
"docs": [
{
"_index": "sys_user",
"_id": 10
},
{
"_index": "article",
"_id": 4
}
]
}
# 根据id批量获取一个索引下的多个文档
GET /sys_user/_mget
{
"docs": [
{"_id": 3},
{"_id": 4}
]
}
# 上面可以简化
GET /sys_user/_mget
{
"ids": [3, 4]
}
_msearch
,它的使用和_bulk
类似,查询一条数据需要两个对象,第一个设置查询的索引,第二个设置查询语句。查询语句和search相同。
如果只查询一个index,我们可以在rul中带上index,这样如果查询该index就可以直接用空对象表示
GET /sys_user/_msearch
{}
{"query": {"match_all": {}}, "from": 0, "size": 2}
{"index": "article"}
{"query": {"match": {"title": "老师"}}}