ElasticSearch简单介绍及原理解析

一、Elasticsearch简单介绍

Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上。它是面向文档 的,意味着它存储整个对象或文档。
Elasticsearch不仅存储文档,而且索引每个文档的内容,使之可以被检索。在 Elasticsearch 中对文档进行索引、检索、排序和过滤,而不是对行列数据,这是 Elasticsearch 能支持复杂全文检索的原因。

二、Elasticsearch通信方式

所有其他语言可以使用 RESTful API 通过端口 9200 和 Elasticsearch 进行通信

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d 	'<BODY>'

例如,计算集群中文档的数量,我们可以用这个:

curl -XGET 'http://localhost:9200/_count?pretty' -d '{
"query": {
    "match_all": {}
}}'

三、索引文档元数据

_index
	文档在哪存放,相当于DB
_type
	文档表示的对象类别,相当于表
_id
	文档唯一标识,相当于主键ID
_source
	Doc的内容主体

创建索引文档,ID可以指定也可以系统自动生成

PUT /{index}/{type}/{id}
{
"field": "value",
 ...
}

举例:
创建文档

PUT /website/blog/123
{
	"title": "My first blog entry",
	"text":  "Just trying this out...",
	"date":  "2014/01/01"
}

检索文档

GET /website/blog/123?pretty

更新文档

PUT /website/blog/123

删除文档

DELETE /website/blog/123

四、Elasticsearch集群架构

Elasticsearch 基于 Lucene,一个 Lucene 索引 在 Elasticsearch 中被称作 分片(shard) ,一个 Elasticsearch 索引 是分片的集合。 当 Elasticsearch 在索引中搜索的时候, 他发送查询到每一个属于索引的分片(Lucene 索引),然后合并每个分片的结果到一个全局的结果集,如下图所示:
在这里插入图片描述
1、文档路由规则

  shard = hash(routing) % number_of_primary_shards

  	Shard:分片号,文档所在分片的位置。
	Routing:默认是文档的 _id ,也可以设置成一个自定义的值。
	number_of_primary_shards:主分片的数量
	
	这个公式说明:创建索引的时候就要确定好主分片的数量 并且永远不会改变这个数量,
	因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。

2、主副分片的交互

集群中每个节点都有能力处理任意请求, 每个节点都知道集群中任一文档位置,可以 直接将请求转发到需要的节点上。 为了扩展负载能力,请求最好能轮询集群中所有的节点。
下面详细介绍读写流程,假设,所有的请求都发送到 Node 1 ,这个节点就被称为协 调节点(coordinating node) 。

a、写流程:

在这里插入图片描述

新建、索引和删除文档所需要的步骤顺序:

1.客户端向 Node 1 发送新建、索引或者删除请求。

2.节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的
主分片目前被分配在 Node 3 上。

3.Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2
 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节
 点向客户端报告成功。
 
Consistency:一致性。即在试图执行一个_写_操作之前,主分片都会要求 必须要有规定
数量(quorum)的分片副本处于活跃可用状态,才会去执行_写_操作(其中分片副本可以是主
分片或者副本分片), 有如下三种配置:
	one:只要主分片状态 ok 就允许执行_写_操作
	
	all:必须要主分片和所有副本分片的状态没问题才允许执行_写_操作
	
	quorum:大多数的分片副本状态没问题就允许执行_写_操作,计算公式如下:
	int( (primary + number_of_replicas) / 2 ) + 1

b、读流程
在这里插入图片描述

以下是从主分片或者副本分片检索文档的步骤顺序:
	1、客户端向 Node 1 发送获取请求。
	
	2、节点使用文档的 _id 来确定文档属于分片 0 。分片 0 的副本分片存在于所有的
	三个节点上。 在这种情况下,它将请求转发到 Node 2 。
	
	3、Node 2 将文档返回给 Node 1 ,然后将文档返回给客户端。

在处理读取请求时,协调结点在每次请求的时候都会通过轮询所有的副本分 片来达到负载均衡。

3、分页
如果每页展示 5 条结果,可以用下面方式请求得到 1 到 3 页的结果:

GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10

Size:显示应该返回的结果数量,默认是 10
From:显示应该跳过的初始结果数量,默认是 0

深度分页

	理解为什么深度分页是有问题的,我们可以假设在一个有 5 个主分片的索引中搜索。
 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返
 回给协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。现在假设我们请
 求第1000 页—​结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不
 产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果
 中的50040 个结果。可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数
 上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。

五、分片内部原理

1、倒排索引

它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个
词,有一个包含它的文档列表。
	Term  | Doc 1 | Doc 2 | Doc 3 | ...
	------------------------------------
	brown |   X   |       |  X    | ...
	fox   |   X   |   X   |  X    | ...
	quick |   X   |   X   |       | ...
	the   |   X   |       |  X    | ...

要构建倒排索引,需要对文档内容分析并分词,分析过程如下:

	首先,将一块文本分成适合于倒排索引的独立的 词条 ,
	之后,将这些词条统一化为标准格式以提高它们的“可搜索性”。

分析器执行上面的工作,由以下三部分构成:

字符过滤器:
	首先,字符串按顺序通过每个 字符过滤器 。他们的任务是在分词前整理字符串。一个
字符过滤器可以用来去掉HTML,或者将 & 转化成 and。

分词器:
	其次,字符串被 分词器 分为单个的词条。一个简单的分词器遇到空格和标点的时候,
可能会将文本拆分成词条。

Token 过滤器:
	最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写
化 Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,
像 jump 和 leap 这种同义词)。

Elasticsearch 中的文档是有字段和值的结构化 JSON 文档。事实上,在 JSON 文档中, 每个被索引的字段都有自己的倒排索引。

2、倒排索引不变性:
倒排索引被写入磁盘后是不可改变 的:它永远不会修改。
不变性有重要的价值:

	不需要锁;
	
	有利性能提升:一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。
只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘;

	写入单个大的倒排索引允许数据被压缩,减少磁盘 I/O 及需要被缓存到内存的索引数
量。

不变性的缺陷:

	如果要让一个新的文档 可被搜索,需要重建整个索引。这要么对一个索引所能包含的
数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。

3、分片内部原理:

Elasticsearch 基于 Lucene,一个 Lucene索引 在Elasticsearch 被称作分片 。
Lucene索引结构:commit point(提交点) + segment(段)的集合,如下图。
在这里插入图片描述

	commit point:列出所有已知段的文件,并记录被删除的文档(每个提交点会包含一
个 .del 文件,文件中会列出被删除/更新文档的段信息)

	segment:是一个倒排索引。

Lucene索引更新流程

新的文档首先被添加到内存索引缓存中,然后写入到一个基于磁盘的段中,具体流程如下:

	a.新文档被收集到内存索引缓存,此时文档对索引不可见, 见图一.

	b.默认每秒缓存被refresh到文件系统缓存,内存索引缓存内容被清空,此时对搜索可见,
见图二。

	c.每隔一段时间,例如 translog 变得越来越大—​索引被刷新(flush);一个新的 
translog 被创建,并且一个全量提交被执行,图三,图四
		a).所有在内存缓冲区的文档都被写入一个新的段。
		b).缓冲区被清空。
		c).一个提交点被写入硬盘。
		d).文件系统缓存被刷新(flush)。
		e).老的 translog 被删除。

	d.段合并:自动刷新流程每秒会创建一个新的段 ,会导致短时间内的段数量暴增,
而段数目太多会带来如下问题:
		a).资源消耗高: 每一个段都会消耗文件句柄、内存和cpu运行周期。

		b).影响查询效率:每个搜索请求都必须轮流检查每个段,所以段越多,搜索越慢。
		
		段合并的时候会将那些旧的已删除文档从文件系统中清除,被删除的文档(或被更新文档
的旧版本)不会被拷贝到新的大段中。图五、图六。

图一、一个在内存缓存中包含新文档的 Lucene 索引,新的文档被添加到内存缓冲区并且被追加到了事务日志
在这里插入图片描述

图二、刷新(refresh)完成后, 缓存被清空但是事务日志不会,缓冲区的内容已经被写入一个可被搜索的段中(文件系统缓存),但还没有进行提交,
在这里插入图片描述

图三、事务日志不断积累索引文档
在这里插入图片描述

图四:在刷新(flush)之后,段被全量提交,并且事务日志被清空
在这里插入图片描述

图五:两个提交了的段和一个未提交的段正在被合并到一个更大的段
在这里插入图片描述
图六:一旦合并结束,老的段被删除
在这里插入图片描述

六、集群扩容

1、名词解释:

扩容单元

	一个分片即为扩容单元,一个最小的索引拥有一个分片,分片可以是主分片或副分片,
索引内任意一个文档都归属于一个主分片,单个分片最大能够存储Integer.MAX_VALUE - 
128 个文档,所以主分片的数目决定着索引能够保存的最大数据量。

索引分片数

	索引建立的时候就已经确定了主分片数,不能修改,但是副本分片数可以随时修改,
理由如下:

	a.shard = hash(routing) % number_of_primary_shards,一旦修改,将不能路
	由到正确的分片;
	
	b.重建索引:新建一个拥有更多分片的一个更大的索引,会消耗大量时间及主机资源。 

分片预分配

	一个分片存在于单个节点,但一个节点可以持有多个分片,通过预分片,可以储备扩容
能力,最大扩容实例个数 = 分片数(包括副本分片)。 比如,有两个分片的索引,可以
扩容到2个实例。

海量分片: 1000个分片,预留海里存储能力?

	不合适,理由如下:
		a.分片是 Lucene 索引,每个分片都会消耗一定文件句柄、内存、以及 CPU资源,
		分片越多,消耗资源越多。
		
		b.每个搜索请求都需要命中索引中的每一个分片,如果每一个分片都处于不同的
		节点还好, 但如果多个分片都需要在同一个节点上竞争使用相同的资源就有些糟
		糕了。
		
		c.用于计算相关度的词项统计信息是基于分片的。如果有许多分片,每一个都只有
		很少的数据会导致很低的相关度。

2、该如何扩容:
方案:

新建索引 + 索引别名 +多索引检索

依据:

	搜索 1 个有着 50 个分片的索引与搜索 50 个每个都有 1 个分片的索引完全等价:
搜索请求均命中 50 个分片

步骤:

a、假设原有索引tweets_1只有一个分片;

b、为索引添加别名:tweets_search 、tweets_index
	PUT /tweets_1/_alias/tweets_search 
	PUT /tweets_1/_alias/tweets_index 
	
c、tweets_1容量不够,需新建索引tweets_2,规划5个分片;

d、别名指向新的索引;
	POST /_aliases
	{
		"actions": [
	 		{ "add":    { "index": "tweets_2", "alias": "tweets_search" }}, 
			{ "remove": { "index": "tweets_1", "alias": "tweets_index"  }}, 
			{ "add":    { "index": "tweets_2", "alias": "tweets_index"  }}  
		]
	}
	说明:
	tweets_search:指向tweets_2、tweets_1两个索引。
		(一个搜索请求可以以多个索引为目标,所以将搜索别名指向 tweets_1 以及
	tweets_2 是完全有效的)
	
	tweets_index:由 tweets_1 切换至 tweets_2
		(索引写入请求只能以单个索引为目标,必须将索引写入的别名只指向新的索引)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值