集群
- 一个运行中的 Elasticsearch 实例称为一个节点,而集群是由一个或者多个拥有相同
cluster.name
配置的节点组成 - 当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据
- 主节点负责增加、删除索引,或者增加、删除节点等,不需要涉及到文档级别的变更和搜索
- 每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。
集群状态的查看
- GET /_cluster/health
响应:
{
"cluster_name" : "es-cn-xxxx",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 6,
"number_of_data_nodes" : 6,
"active_primary_shards" : 323,
"active_shards" : 601,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}
-
cluster_name:集群的名称。在这个例子中是
es-cn-xxxx
。 -
status:集群的健康状态,
-
green
(所有主分片和副本分片都分配了) -
yellow
(所有主分片都分配了,但不是所有副本分片都分配了)单节点集群,副本分片是无法分配的,会处于yellow状态 -
red
(有未分配的主分片)
-
-
timed_out:表示获取集群健康状态的请求是否超时。如果是
true
,则表示请求超时;如果是false
,则表示请求没有超时。 -
number_of_nodes:集群中所有节点的数量。在这个例子中是6。
-
number_of_data_nodes:集群中数据节点的数量。在这个例子中是6。
-
active_primary_shards:当前活跃的主分片数量。在这个例子中是323。
-
active_shards:当前活跃的分片(包括主分片和副本分片)总数量。在这个例子中是601。
-
relocating_shards:当前正在重新分配的分片数量。在这个例子中是0。
-
initializing_shards:当前正在初始化的分片数量。在这个例子中是0。
-
unassigned_shards:当前未分配的分片数量。在这个例子中是0。
-
delayed_unassigned_shards:因分配延迟而未分配的分片数量。在这个例子中是0。
-
number_of_pending_tasks:当前待处理的任务数量。在这个例子中是0。
-
number_of_in_flight_fetch:当前正在进行的fetch操作的数量。在这个例子中是0。
-
task_max_waiting_in_queue_millis:队列中任务等待的最长时间(以毫秒为单位)。在这个例子中是0。
-
active_shards_percent_as_number:活跃分片的百分比。在这个例子中是100.0%。
分片
- 一个 分片是一个底层的 工作单元,它仅保存了一部分数据
- 文档保存在分片内,分片又被分配到集群内的各个节点里。
- 当你的集群规模扩大或者缩小时, Elasticsearch 会自动的在各节点中迁移分片,使得数据仍然均匀分布在集群里。
- 主分片:它的数目决定着索引能够保存的最大数据量
- 副本分片:作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务(在运行中的集群上可以动态调整副本分片数目)
文档怎样存到指定分片?
shard = hash(routing) % number_of_primary_shards
routing
是一个可变值,默认是文档的 _id
,也可以设置成一个自定义的值,通过这个算法我们找到文档所在分片的位置,这也是为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了
在ES中,索引的主分片数量是一个关键的设计决策,需要在索引创建前慎重考虑。
索引
- 索引实际上是指向一个或者多个物理 分片 的 逻辑命名空间 。
- 查询索引的分片数目可以用语句:GET /sp_game_index/_settings,返回的数据很多,但是主要关注:
number_of_shards
主分片数量,number_of_replicas
每个主分片的副本数量。
文档
- 在ES官方文档里,认为文档也是对象。在大多数应用中,多数实体或对象可以被序列化为包含键值对的 JSON 对象,存为文档
- 在Elasticsearch中,文档的基本结构(例如字段类型)一经创建后,是不允许直接修改的。
- 如果需要修改索引的映射(例如新增字段、更改字段类型等),需要先进行索引重建或者使用动态映射功能。
文档元数据
执行任何查询,在hit里应该都能看到这三个字段
_index
:文档在哪个索引存放_type
:文档表示的对象类别,自Elasticsearch 7.0.0起,类型已被弃用,一个索引只能有一个类型:_doc_id
:文档唯一标识
其他:
_version:文档的版本号,用于版本控制和并发处理(看到这个,应该就会很自然的想到乐观锁,没错,es使用的就是乐观并发控制)
_score:文档与查询的相关性评分,主要用于搜索结果排序。
_source:文档的实际内容(JSON格式),即存储在索引中的数据。
_routing:用于自定义路由的值,以决定文档存储在哪个分片中。
_parent:父文档的ID,用于父子关系类型(已在Elasticsearch 6.0.0中弃用)。
_timestamp:文档的时间戳(如果已启用)。
_ttl:文档的生存时间(如果已启用,已在Elasticsearch 5.0.0中弃用)。
文档的更新
更新文档(Update):
- 更新文档允许你对已经存在的文档进行部分更新,而不需要重新索引整个文档。
- 更新操作可以修改文档中的部分字段或添加新的字段,而不会影响其余部分。
- 更新操作是原子的,可以保证数据的一致性。
重新索引文档(Reindex):
- 如果需要更改文档的ID或对文档进行较大的结构变更,可以使用重新索引操作。
- 重新索引操作会创建一个新的文档,用来替换原来的文档,可以在此过程中进行任何所需的变更。
- 重新索引通常用于重建索引、数据迁移或对索引结构进行大规模修改的场景。
文档更新,分片怎么调整
我们知道,es中有很多分片,还有副本分片,当想要改动一个文档时(新建,索引,删除),会发生什么?这个在es官网写的很清晰
- 客户端向
Node 1
发送新建、索引或者删除请求。 - 节点使用文档的
_id
确定文档属于分片 0 。请求会被转发到Node 3
,因为分片 0 的主分片目前被分配在Node 3
上。 Node 3
在主分片上面执行请求。如果成功了,它将请求并行转发到Node 1
和Node 2
的副本分片上。一旦所有的副本分片都报告成功,Node 3
将向协调节点报告成功,协调节点向客户端报告成功。
文档更新和参数控制
- consistency,即一致性。在默认设置下,即使仅仅是在试图执行一个_写_操作之前,主分片都会要求 必须要有 规定数量(quorum)(或者换种说法,也即必须要有大多数)的分片副本处于活跃可用状态,才会去执行_写_操作(有点像zookeeper的分布式更新策略)
consistency
参数的值可以设为:one
:只要主分片状态 ok 就允许执行_写_操作all
:必须要主分片和所有副本分片的状态没问题才允许执行_写_操作quorum
:默认值为quorum
, 即大多数的分片副本状态没问题就允许执行_写_操作
- timeout:默认情况下,最多等待1分钟,
timeout
不是停止执行查询,它仅仅是告知正在协调的节点返回到目前为止收集的结果并且关闭连接。在后台,其他的分片可能仍在执行查询即使是结果已经被发送了。
索引
GET /_cat/indices:查看所有索引
倒排索引
一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表
举个例子:有两个文本,分别为:
- The quick brown fox jumped over the lazy dog
- Quick brown foxes leap over lazy dogs in summer
那么会把它切分为:Quick ,brown ,foxes 等等,一个个的单词。当然,实际上不可能这么简单的按空格分词,还会考虑到同义词,名词复数等等情况
分析器的作用
上面一小节提到,分词肯定不会是按照空格来简单区分的,那么具体怎么分的呢?
分析器主要有三个核心的功能:
-
字符过滤器:首先,字符串按顺序通过每个 字符过滤器 。他去掉HTML,或者将
&
转化成and
,等等。 -
分词器:其次,字符串被 分词器 分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条。
-
Token 过滤器:最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化
Quick
),删除词条(例如, 像a
,and
,the
等无用词),或者增加词条(例如,像jump
和leap
这种同义词)。
内置分析器
分析器的种类有很多,不一一介绍,直接看看区别:
先用标准分析器:
GET /_analyze
{
"analyzer": "standard",
"text": "今天天气很好"
}
{
"tokens" : [
{
"token" : "今",
"start_offset" : 0,
"end_offset" : 1,
"type" : "<IDEOGRAPHIC>",
"position" : 0
},
{
"token" : "天",
"start_offset" : 1,
"end_offset" : 2,
"type" : "<IDEOGRAPHIC>",
"position" : 1
},
{
"token" : "天",
"start_offset" : 2,
"end_offset" : 3,
"type" : "<IDEOGRAPHIC>",
"position" : 2
},
{
"token" : "气",
"start_offset" : 3,
"end_offset" : 4,
"type" : "<IDEOGRAPHIC>",
"position" : 3
},
{
"token" : "很",
"start_offset" : 4,
"end_offset" : 5,
"type" : "<IDEOGRAPHIC>",
"position" : 4
},
{
"token" : "好",
"start_offset" : 5,
"end_offset" : 6,
"type" : "<IDEOGRAPHIC>",
"position" : 5
}
]
}
不难看出分词效果很差,但是如果用中文语言的分析器,效果会好很多。
GET /_analyze
{
"analyzer": "ik_smart",
"text": "今天天气很好"
}
{
"tokens" : [
{
"token" : "今天天气",
"start_offset" : 0,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "很好",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 1
}
]
}
- 当我们索引一个文档,它的全文域被分析成词条以用来创建倒排索引,当我们在全文域 搜索 的时候,我们需要将查询字符串通过 相同的分析过程 ,以保证我们搜索的词条格式与索引中的词条格式一致
域类型(字段类型)
主要的类型有:
- 字符串:
string
- 整数 :
byte
,short
,integer
,long
- 浮点数:
float
,double
- 布尔型:
boolean
- 日期:
date
实际上,当你索引一个全新的文档时,ES会默认进行映射,根据字段内容简单判断,然后映射一个域类型
使用GET sp_game_index/_mapping查看下(很长,只截选部分)
- “scaled_float”是一种特殊的数据类型,用于存储浮点数,但它通过乘以一个“scaling_factor”(缩放因子)来存储整数形式的浮点数 ,可以节约存储空间
"amount" : {
"type" : "scaled_float",
"scaling_factor" : 100.0
}
- 类型:
object
,这意味着它可以包含嵌套的对象或键值对。 enabled
:设置为false
,表示这个字段在当前的索引或映射中是不可用的或不被索引的。
"advertiser_name" : {
"type" : "object",
"enabled" : false
},
- 类型:
text
,这是一个文本字段,用于全文搜索。 analyzer
:设置为zf_analyzer
,这是一个自定义的分析器,用于在索引和搜索时处理文本。
"advertiser_name_search" : {
"type" : "text",
"analyzer" : "zf_analyzer"
},
修改ES索引映射
- 一般情况下,不允许直接修改一个已存在字段的类型
- 可以向现有的映射中添加新的字段。这通常是最简单的修改,因为它不会影响到已有的数据。
- 并不是所有字段里面都能嵌套增加一个字段,long,text之类的是不行的,通常只有object可以
- 新建立一个索引,并设置关系映射
PUT /gb
{
"mappings": {
"properties": {
"date": {
"type": "date"
},
"user_id": {
"type": "long"
},
"name": {
"type": "text"
},
"tweet": {
"type": "text",
"analyzer": "english"
}
}
}
}
- 新增一个object字段
PUT /gb/_mapping
{
"properties": {
"about": { // 新增字段
"type": "object"
}
}
}
- 继续在这个object字段中增加新字段
PUT /gb/_mapping
{
"properties": {
"about": {
"type": "object",
"properties": {
"age": {
"type": "integer"
}
}
}
}
}
- 使用GET gb/_mapping,可以看到索引结构为:
{
"gb": {
"mappings": {
"properties": {
"about": {
"properties": {
"age": {
"type": "integer"
}
}
},
"date": {
"type": "date"
},
"name": {
"type": "text"
},
"tweet": {
"type": "text",
"analyzer": "english"
},
"user_id": {
"type": "long"
}
}
}
}
}
学习内容来源于官网:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html