likes
comments
collection
share

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

作者站长头像
站长
· 阅读数 6

ES从单体到集群,从入门到精通,深入查询过程和近实时分析

为什么要使用ES

es的出现主要是为了应对全文检索的场景,例如搜索框

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

如果使用MySQL这样的数据库,要如何实现这个全文检索呢?

是不是要根据某个字段进行 like ‘%xx%’ 进行模糊查询?这样的效率是很差的,而且无法使用索引优化

为了解决这个问题es的使用就呼之欲出了

ES的基本名词概念

  1. Node节点,就是ES集群中的一台服务器
  2. index索引,直接等价于MySQL的数据库的一张表
  3. type类型(可忽略),用来定义数据结构的,每个index下可以有多个type,好比数据库里面的一张表的结构的描述,描述字段类型
  4. document文档,文档就是最终的数据了,可用认为一个文档就是数据库里面的一行数据,是ES中的最小单元
  5. Field字段,好比数据库中列的概念,即表中的数据的一个字段
  6. shard分片,一台服务器无法存储大量的数据,ES把一个索引里面的数据分为主分片和副本分片,分布式的存储在多个服务器上
  • 拓展一下:(此处的副本分片先理解,下文会有es集群介绍)

    在默认情况下,我们创建一个库的时候,默认会帮我们创建5个主分片(primary shrad)和5个副分片(replica shard),所以说正常情况下是有10个分片的。

    同一个节点上面,副本和主分片是一定不会在一台机器上面的,就是拥有相同数据的分片,是不会在同一个节点上面的。

    所以当你有一个节点的时候,这个分片是不会把副本存在这仅有的一个节点上的,当你新加入了一台节点,ES会自动的给你在新机器上创建一个之前分片的副本。

  • 思考问题:是否每新加入一个节点,都会在新节点上添加已经备份过了的副本分片呢?

    其实不完全如此,Elasticsearch在新节点加入集群后,会根据当前集群的分片分配情况和副本分片设置进行重新平衡。

    具体来说:

    1. 副本分片数量:是否在新节点上添加副本分片取决于当前索引的副本分片设置以及已有的副本分片分布情况。如果某个索引的副本分片尚未达到设定的数量,并且有剩余空间可以分配新的副本分片,那么Elasticsearch会尝试将部分主分片的副本分配到新节点上。

    2. 负载均衡:Elasticsearch在分配副本分片时,不仅考虑是否缺少副本,还会考虑整个集群的负载均衡。它会尽量使每个节点上的分片(包括主分片和副本分片)数量均匀分布,以优化存储资源和计算能力的使用。

    3. 约束条件:虽然理论上有可能在新节点加入时为其分配副本分片,但实际操作中还需要满足一些约束条件,例如,确保不会违反“一个分片的主分片和副本分片不会部署在同一台节点上”的原则。

    综上所述,增加一个新节点并不意味着一定会立即为所有索引在新节点上创建副本分片,而是根据集群状态、分片配置和负载均衡策略来动态调整分片分布。

倒排索引

先回忆一下MySQL的索引,最常用的就是主键索引了,一个主键id对应一条数据,即主键对应数据

倒排索引是分词字段对应主键索引的主键,这里可能并不好理解,看下面案例

我是后端男生程序员 ---> 文档id为1

我是后端女生程序员 ---> 文档id为2

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

分词

什么是分词,我是后端程序员 ---> 我、是、后端、程序员。这样就是分词了

那什么字段能够进行分词呢

在document中的字段属性properties是text或者keyword类型的就可以进行分词,index就是可以被索引到的意思

比如我要创建一个user的索引,并进行mapping映射,请求:http://127.0.0.1:9200/user/_mapping (前提是有user这个索引)

请求体内容:

{
				“properties” : {
					"name" : {
						"type" : "text",
						"index" : true
						}
					"sex" : {
						"type" : "keyword",
						"index" : true
						}
					"tel" : {
						"type" : "keyword",
						"index" : false
						}
				}
}

Text类型和Keyword类型的区别

Keyword类型是不会对该搜索字段进行分词的,会直接建立倒排缩影

Text类型在存入Elasticsearch的时候,会先进行分词,然后根据分词后的内容建立倒排索引

什么意思呢?拿回上面那个案例进行举例

比如说我有个索引里面,有个字段context,context的内容是:我是后端程序员,这个document的id为1

如果context这个字段的properties的type是text

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

如果context这个字段的properties的type是keyword

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

ES的路由原理

ElasticSearch如何知道一个文档应该存放到哪个分片当中呢?当我们创建文档的时候,它如何决定这个文档被存储在分片1还是在分片2?

首先我们要明确一点,一个文档被路由到一个分片肯定不是随机路由的,不然查找的时候就不知道去哪找了

路由的过程是根据这个公式进行路由的:shard = hash(document_id) % number_of_primary_shards

什么意思呢?就是对document_id进行哈希算法得到一个hash值,然后跟分片数量进行取模,跟Java的HashMap类似

得到的值就是被分配到哪个分片了

这个公式也就能够解释为什么我们在创建索引的时候就需要确定好主分片的数量,并且永远不会改变主分片的数量

如果说主分片的数量被改变了,那么之前所有路由的值都会无效,再也找不到该document数据了

ES的整个查询过程

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

接图中的问题

在Elasticsearch中,当你已知文档ID并知道它所在的分片时,查找该文档的过程并不涉及遍历整个分片的所有数据。Elasticsearch使用Lucene作为底层索引引擎,对于每个分片内部,文档ID与文档位置之间存在一个映射关系。

具体步骤如下:

  1. 文档ID到分片计算:首先,Elasticsearch根据其内置的哈希算法将文档ID转换成一个特定的分片编号。这个过程确保了相同ID的文档总是被分配到同一个分片上(在没有重新索引或迁移的情况下)。
  2. 段内定位:在确定了分片后,Elasticsearch会根据文档ID在该分片内部的各个段(Segment)中进行查找。每个段都有自己的倒排索引和文档存储结构。其中,每个文档在内存中有一个唯一的内部ID(称为doc values),并且通常会在段文件中按照某种顺序紧凑存储。
  3. 快速查找:由于文档ID在段内部是有序且可以直接寻址的,Elasticsearch可以利用二分查找等高效方法快速定位到包含指定ID的文档在段内的确切位置,而不是通过遍历所有文档来找到目标文档。
  4. 读取文档:一旦找到了文档在磁盘上的位置,Elasticsearch会从相应的段文件中加载文档内容,并将其返回给客户端。

综上所述,即使你知道了文档ID和分片信息,在实际查询过程中也不会通过遍历的方式寻找文档,而是利用了高效的索引结构和寻址方式直接定位文档数据。

ES的内存和磁盘IO问题 --- 近实时性分析

先分析一下近实时的概念,近实时是指在处理索引更新和搜索请求时,能够提供极低延迟的搜索体验,虽然不是严格意义上的实时搜索,但延迟时间非常短,通常在1秒以内。这意味着当你向Elasticsearch中插入、更新或删除数据后,这些变更几乎可以立即反映在后续的搜索结果中。

这个时候大家会不会有疑问呢?

先清楚一个查询顺序

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

这个时候想一下,新增一条数据,会先进入内存,然后再刷入磁盘,那我查这个新增加的数据,明明在内存中有,为什么不给实时查?

其实大家联想一下数据库的事务的这个概念,没有被提交的事务,其他线程可见吗?其实这是一个道理

没有被刷新到磁盘的数据,内存中是不可见的,只有数据刷到了磁盘中,你才能在内存中能够看得到新增的数据

而从内存刷新到磁盘的时间大概在1s左右,所以才说ES新增数据的近实时是1s,及从新增到能搜索到的时间是1s

ES集群的基本概念

ES可用作为一个独立的单个搜索服务器,但是为了处理大数据量大场景,实现容错和高可用性,ES可以运行在寻多互相合作的服务器上

这些服务器上的es称作为集群,形成集群的每个服务器的es称之为节点

ES为分配不同的任务,定义了以下几个节点角色:Master、Data Node、Coordinating Node、Ingest Node

  • Master节点

    每个 ES 节点启动之前都会有个默认配置 node.master:true ,也就是说每个节点都有可能成为 Master 节点

    当然 Master 只能有一个,所以会通过选举的方法对这启动的节点选举,被选中的节点才会成为 Master 节点。

    Master 节点主要是负责维护集群的状态,像所有节点的信息,所有的索引和它相关的 Mapping 关系,配置信息,分片的路由等。注意Master节点不存储document数据,只存储元数据如索引啊Mapping。

    既然 Master 节点维护了这么重要的信息,玩意它挂了怎么办?

    挂了的话,将会对其他的有资格成为 Master 节点的节点重新选举出另一个 Master 节点,因此这就说明了其他有资格成为主节点的子节点也会保存集群信息,但是只有 Master 节点有权限能够修改,试想如果其他节点也能修改的话,这将会导致数据不一致的问题。

  • Data Node 数据节点

    这个节点主要负责数据的存储,在数据扩展上起到了至关重要的作用。也就是说读写数据都会找到相应的 Data Node 节点。但是索引的创建删除可不是在数据节点,是在Master节点,Master节点负责管理元数据

  • Coordinating Node 协调节点

    协调节点主要负责协调客户端的请求,将接收到的请求分发给合适的节点,并把结果汇集到一起。比如客户端请求查询某个索引的数据,协调节点将会把请求分发给保存相关的数据的 DataNode 节点,找到相应的分片,并把查询到的结果都汇集返回。并且每个节点都默认起到了 Coordinating Node 的职责。也就是说所有节点默认全部都是协调节点。

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

ES集群建立节点的过程

建立索引的请求会先发到Master节点,Master建立完索引后,再将集群状态同步至slave

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

ES的分布式原理 --- 分布式架构&水平拓展原理

首先你得知道的是,什么是分布式,可以先简单理解分布式是一种部署方式,就是服务部署到多个服务器上

ES为什么天然支持分布式架构呢?

Elasticsearch是基于分布式设计,数据被分片并分布在集群中的多个节点上。

简单来说就是:在es中一个索引的主分片如果有5片,那么这5个主分片可能会不在同一台es服务上

依此我们来举例子:

在默认情况下,我们创建一个库的时候,默认会帮我们创建5个主分片(primary shrad)和5个副分片(replica shard),所以说正常情况下是有10个分片的。

同一个节点上面,副本和主分片是一定不会在一台机器上面的,就是拥有相同数据的分片,是不会在同一个节点上面的。

所以当你有一个节点的时候,这个分片是不会把副本存在这仅有的一个节点上的,当你新加入了一台节点,ES会自动的给你在新机器上创建一个之前分片的副本。

举个例子:

启动 2 个 ES 节点,配置分片个数为 3,副本个数为 1(每个分片有一个副本)。如下图,蓝色的代表主分片,绿色的是副本,仔细一点不难发现,分片与其副本不在同一个节点内。这是非常合理的,因为副本本来就是主分片的备胎,当主分片节点挂了,另外一个节点的副本将会充当主分片,如果它们在同一个节点内,副本将发挥不到作用。

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

水平扩展原理

单个节点的容量是有限的,如果后期两个节点的容量不能够支持三个分片,那么另外启动一个节点就可以了,ES 会自动的重新规划分片,如下图:可以看到 A3 节点已经被自动的分配到 Node3 节点里面了,另外副本 B1 从 Node2 移动到 Node3 节点,B3 分片从 Node1 节点被分配到 Node2 节点。这里想一下,如果再启动一个节点呢?是的,再启动一个节点将不会对主分片起到任何作用,因为主分片不可以修改,只有三个,但是副本可以修改,能够起到扩容的作用。

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快

ES的并发场景会有锁竞争的情况吗?

在学习ES的时候,猛然地发现自己好像没有考虑过并发场景下的安全问题,ES主要是通过乐观锁

Elasticsearch 在处理并发场景时是需要考虑锁竞争和数据安全问题,尤其是在索引更新和并发控制方面。在早期版本中,Elasticsearch 采用了一些传统的并发控制机制,如悲观锁等,但随着发展,它更多地倾向于使用乐观并发控制策略来解决并发问题。

在 Elasticsearch 中,乐观锁的实现体现在文档级别的更新操作上,通过 if_seq_no 和 if_primary_term 参数来保证更新的原子性和避免并发冲突:

  • if_seq_no:表示对文档进行更新或删除操作时期望的序列号(Sequence Number),每个文档每次成功更新时,其对应的序列号都会递增。
  • if_primary_term:是与序列号配套使用的主术语(Primary Term),每次创建新的索引或者索引被重置时,该值也会增加。 当客户端尝试更新一个文档时,可以同时提供这两个参数以确保在执行更新前文档没有被其他事务修改过。如果当前文档的序列号或主术语与请求中的不匹配,则更新操作会失败,从而避免了并发写入导致的数据不一致问题。

总结 --- ES为什么快

  1. 分布式架构:ES是基于分布式架构进行设计的,数据被分片并分布在集群的多个节点上,将查询操作分散到整个集群
  2. 倒排索引:在倒排索引中,关键字与文档之间的关系被反转,每个关键字关联着包含它的所有文档列表,而非像传统数据库那样以文档为中心组织数据,这样,在执行全文搜索时,可以直接从关键词快速定位到包含该词的文档集合,显著减少了查找时间。
  3. 并行查询处理:在查询阶段,Elasticsearch可以针对不同的分片并行执行查询请求,汇总结果,充分利用现代多核处理器的优势。

ES从单体到集群,从入门到精通,深入查询过程和近实时分析,一篇看懂ES为什么快 4. 多层缓存策略:Elasticsearch不仅有内存缓存,还支持自定义的fielddata缓存,能够将常用字段的值加载到内存中以便更快地进行聚合计算和过滤操作。