3.ES核心
3.1.核心概念
3.1.1.索引
一个索引就是一个拥有几分相似特征的文档的集合。
一个索引由一个名字来标识(必须全部是小写字母),并且当我们要对这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。
能搜索的数据必须索引,这样的好处是可以提高查询速度,
索引:一切设计都是为了提高搜索的性能。
3.1.2.类型
在一个索引中,可以定义一种或多种类型。
类型是索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。
版本 | Type |
---|---|
5.x | 支持多种 type |
6.x | 只能有一种 type |
7.x | 默认不再支持自定义索引类型(默认类型为:_doc) |
3.1.3.文档
一个文档是一个可被索引的基础信息单元,也就是一条数据
在一个 index/type 里面,可以存储任意多的文档。
3.1.4.字段
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识。
3.1.5.映射
mapping
是处理数据的方式和规则方面做一些限制
处理ES里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。
3.1.6.分片
一个索引可以存储超出单个节点硬件限制的大量数据。
Elasticsearch
提供了将索引划分成多份的能力,每一份就称之为分片。当创建一个索引的时,可以指定想要的分片的数量。每个分片本身也是一个功能完善并且独立的索引
,这个索引
可以被放置到集群中的任何节点上。
分片是一个很重要的概念
- 允许水平分割 / 扩展你的内容容量。
- 允许在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量。
3.1.7.副本
Elasticsearch
允许创建分片的一份或多份拷贝,这些拷贝叫做复制分片()。
副本也是一个很重要的概念
- 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的
- 扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行。
- 每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。
- 分片和复制的数量可以在索引创建的时候指定。在索引创建之后,可以在任何时候动态地改变副本的数量,但是不能改变分片的数量。
- 默认情况下,
Elasticsearch
中的每个索引被分片 1 个主分片和 1 个复制。
3.1.8.分配
将分片分配给某个节点的过程,包括分配主分片或者副本。
如果是副本,还包含从主分片复制数据的过程。这个过程是由master
节点完成的。
3.2.系统架构
一个运行中的Elasticsearch
实例称为一个节点
,而集群是由一个或者多个拥有相同cluster.name
配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。
当一个节点被选举成为主节点时, 它将负责管理集群范围内的所有变更。 而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加它也不会成为瓶颈。 任何节点都可以成为主节点。
我们可以将请求发送到集群中的任何节点 ,包括主节点。 每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。 无论我们将请求发送到哪个节点,它都能负责从各个包含我们所需文档的节点收集回数据,并将最终结果返回給客户端。
3.3.分布式集群
3.3.1. 故障转移
当集群中只有一个节点在运行时,意味着会有一个单点故障问题——没有冗余。
在同一台机器上启动了第二个节点时,只要它和第一个节点有同样的cluster.name
配置,它就会自动发现集群并加入到其中。
但是在不同机器上启动节点的时候,要加入到同一集群,需要配置一个可连接到的单播主机列表。之所以配置为使用单播发现,以防止节点无意中加入集群。只有在同一台机器上运行的节点才会自动组成集群。
3.3.2.水平扩容
当集群中的节点超过分片数量时可以进行扩容
主分片的数目在索引创建时就已经确定了下来。
在运行中的集群上是可以动态调整副本分片数
{
"number_of_replicas" : 2
}
如果只是在相同节点数目的集群上增加更多的副本分片并不能提高性能,因为每个分片从节点上获得的资源会变少。 但是更多的副本分片数提高了数据冗余量。
3.3.3应对故障
当主节点宕机之后,集群会重新选举一个新的主节点来保证正常工作。
旧的主节点重新运行之后,也不会恢复为主节点,但是数据会进行同步
3.3.4.路由计算
当索引一个文档的时候,文档会被存储到一个主分片中。ES会通过下面这个公式进行分配存储
routing
:可变值,默认是文档的id,也可以设置成一个自定义的值。
routing
通过hash函数生成一个数字,然后这个数字再除以number_of_primary_shards
(主分片的数量)后得到余数。- 这个分布在0到
number_of_primary_shards-1
之间的余数,就是寻求的文档所在分片的位置。
这解释了为什么要在创建索引的时候就确定好主分片的数量并且永远不会改变这个数量,因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
所有的文档API都接受一个叫做routing
的路由参数 ,通过这个参数可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档。
3.4.分片控制
我们可以发送请求到集群中的任一节点。 每个节点都有能力处理任意请求。 每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上。
3.4.1.写流程
新建、索引和删除请求都是写操作, 必须在主分片上面完成之后才能被复制到相关的副本分片
流程
- 客户端向
节点1
发送请求。 - 节点使用文档的id确定文档属于
分片0
。请求会被转发到节点3
(分片0所在的节点)。 节点3
在主分片上执行请求。- 如果成功:将请求并行转发到
其他节点
的副本分片上。当所有的副本分片都报告成功,节点3
向协调节点报告成功,协调节点向客户端报告成功。
- 如果成功:将请求并行转发到
3.4.2.读流程
我们可以从主分片或者从其它任意副本分片检索文档
流程
- 客户端向
节点1
发送获取请求。 - 节点使用文档的id来确定文档属于
分片0
。分片0
的副本分片存在于所有的节点上。 在这种情况下,它将请求转发到节点2
。 节点2
将文档返回给节点1
,然后将文档返回给客户端。- 在处理读取请求时,协调结点在每次请求的时候都会通过轮询所有的副本分片来达到负载均衡。
- 在文档被检索时,已经被索引的文档可能已经存在于主分片上但是还没有复制到副本分片。 在这种情况下,副本分片可能会报告文档不存在,但是主分片可能成功返回文档。 一旦索引请求成功返回给用户,文档在主分片和副本分片都是可用的。
3.4.3.更新流程
部分更新一个文档
流程
- 客户端向
节点1
发送更新请求。 节点1
将请求转发到主分片所在的节点3
。节点3
从主分片检索文档,修改source
字段中的JSON
,并且尝试重新索引主分片的文档。如果文档已经被另一个进程修改,它会重试步骤3
,超过retry_on_conflict
次后放弃。- 如果
节点3
成功地更新文档,它将新版本的文档并行转发到节点1和节点2
上的副本分片,重新建立索引。- 一旦所有副本分片都返回成功,
节点3
向协调节点也返回成功,协调节点向客户端返回成功。
- 一旦所有副本分片都返回成功,
当主分片把更改转发到副本分片时, 它不会转发更新请求。 相反,它转发完整文档的新版本。请记住,这些更改将会异步转发到副本分片,并且不能保证它们以发送它们相同的顺序到达。 如果 Elasticsearch 仅转发更改请求,则可能以错误的顺序应用更改,导致得到损坏的文档。
3.4.4.多文档操作流程
mget
和bulk API
的模式类似于单文档模式。区别在于协调节点知道每个文档存在于哪个分片中。它将整个多文档请求分解成每个分片的多文档请求,并且将这些请求并行转发到每个参与节点。协调节点一旦收到来自每个节点的应答,就将每个节点的响应收集整理成单个响应,返回给客户端
mget
- 客户端向
节点1
发送mget
请求。 节点1
为每个分片构建多文档获取请求,然后并行转发这些请求到托管在每个所需的主分片或者副本分片的节点上。- 一旦收到所有答复,
节点1
构建响应并将其返回给客户端
- 一旦收到所有答复,
可以对 docs 数组中每个文档设置 routing 参数。
bulk API
- 客户端向
节点1
发送bulk
请求。 节点1
为每个节点创建一个批量请求,并将这些请求并行转发到每个包含主分片的节点主机。- 主分片一个接一个按顺序执行每个操作。当每个操作成功时,主分片并行转发新文档(或删除)到副本分片,然后执行下一个操作。 一旦所有的副本分片报告所有操作成功,该节点将向协调节点报告成功,协调节点将这些响应收集整理并返回给客户端。
3.5.分片原理
分片是Elasticsearch
最小的工作单元。
3.5.1.倒排索引
适用于快速的全文搜索。
正向索引:搜索引擎会将待搜索的文件都对应一个文件 ID,搜索时将这个ID和搜索关键字进行对应,形成 K-V 对,然后对关键字进行统计计数
但是互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。
3.5.2.文档搜索
早期的全文检索会为整个文档集合建立一个很大的倒排索引并将其写入到磁盘。 一旦新的索引就绪,旧的就会被其替换,这样最近的变化便可以被检索到。
倒排索引被写入磁盘后是不可改变
的。
不可改变的好处
- 不需要锁。
- 一旦索引被读入内核的文件系统缓存,旧会一直留在那里。
- 其它缓存在索引的生命周期内始终有效。
- 写入单个大的倒排索引允许数据被压缩,减少磁盘I/O和需要被缓存到内存的索引的使用量。
不可改变的缺点:如果让一个新的文档可被搜索,需要重建整个索引。
- 这要么对一个索引所能包含的数据量造成了很大的限制
- 要么对索引可被更新的频率造成了很大的限制。
3.5.3.动态更新索引
用于在保留不变性的情况下能实现倒排索引的更新
通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到,从最早的开始查询完后再对结果进行合并。
Lucene
引入了按段搜索
的概念。 每一 段本身都是一个倒排索引
,但索引在Lucene
中除表示所有段的集合外, 还增加了提交点
的概念(一个列出了所有已知段的文件)
按段搜索执行流程
- 新文档被收集到内存索引缓存
- 不时的缓存被提交
- 一个新的段被写入磁盘
- 一个新的包含新段名字的
提交点
被写入磁盘 - 磁盘进行同步
- 新的段被开启,让它包含的文档可见以被搜索
- 内存缓存被清空,等待接收新的文档
3.5.4.近实时搜索
可以通过设置 refresh_interval , 降低每个索引的刷新频率
{
"settings": {
"refresh_interval": "30s"
}
}
refresh_interval
可以在既存索引上进行动态更新。 在生产环境中,正在建立一个大的新索引时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来
# 关闭自动刷新
PUT /users/_settings
{ "refresh_interval": -1 }
# 每一秒刷新
PUT /users/_settings
{ "refresh_interval": "1s" }
3.5.5.持久化变更
translog
提供所有还没有被刷到磁盘的操作的一个持久化记录。当Elasticsearch
启动的时候, 它会从磁盘中使用最后一个提交点
去恢复已知的段,并且会重放 translog
中所有在最后一次提交后发生的变更操作。
3.5.5.段合并
Elasticsearch
通过在后台进行段合并,小的段被合并到大的段,然后这些大的段再被合并到更大的段。
一旦合并结束,老的段被删除
3.6.文档分析
分析包含下面的过程
- 将一块文本分成适合于倒排索引的独立的词条
- 将这些词条统一化为标准格式以提高它们的
可搜索性
,或者recall
分析器执行上面的工作。分析器实际上是将三个功能封装到了一个包里- 字符过滤器:字符串按顺序通过每个字符过滤器 。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉 HTML,或者将 & 转化成 and
- 分词器:字符串被分词器分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条。
- Token过滤器:词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化 Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像 jump 和 leap 这种同义词)。