小白看过来:Es相关概念

ES 中的概念有很多,如:集群(cluster)、节点(node)、索引(index)、分片(shards)、副本(replicas)、集群状态(status)、映射(Mapping)、文档(doc)、字段(Field)、类型(type)、词项(Term)、分配(allocation)、Lucene、倒排索引、正排索引、相关性评分等。

从大的层面到小的层面大概可以分为:

  1. 集群,节点
  2. 分片,副本
  3. 索引,映射
  4. 文档,字段,类型

1.从架构层面看

1.1 集群

多个共同工作的Es实例组成的集合叫做Es集群,分布式Es集群可以存储海量数据,可以支撑更高的并发读写。集群是一种提供无间断服务的方案,得益于分布式系统的架构设计,让Es具有高可用性和高可扩展性。

  1. 高可用:服务高可用&数据高可用
  • 服务高可用:在部分节点down掉的情况下系统还可以对外提供服务。
  • 数据高可用:部分节点挂掉,并且这些节点的数据即使无法恢复,也能保证数据不丢失。
  1. 高可扩展性:当并发/数据量上升的时候,可以通过水平扩容(增加节点数)来解决性能问题。

1.2 节点

单个 ES 的服务实例叫做节点,本质上就是一个 Java 进程。每个实例都有自己的名字,就是配置文件里的 node.name 设置的内容。为了标识每个节点,每个节点启动后都会分配一个 UID,存储在 data 目录。各个节点受到集群的管理,我们可以通过增加或者减少节点来达到扩容或减容的目的。

集群里的节点是有分类的,主要的节点类型如下。

  • 主节点(Master) 。主节点在整个集群是唯一的,Master 从有资格进行选举的节点(Master Eligible)中选举出来。主节点主要负责管理集群变更、元数据的更改。如果要类比的话,主节点像是公司的总经办。
  • 数据节点(Data Node) 。其负责保存数据,要扩充存储时候需要扩展这类节点。数据节点还负责执行数据相关的操作,如:搜索、聚合、CURD 等。所以对节点机器的 CPU、内存、I/O 要求都比较高。
  • 协调节点(Coordinating Node) 。负责接受客户端的请求,将请求路由到对应的节点进行处理,并且把最终结果汇总到一起返回给客户端。因为需要处理结果集和对其进行排序,需要较高的 CPU 和内存资源。
  • 预处理节点(Ingest Node) 。预处理操作允许在写入文档前通过定义好的一些 processors(处理器)和管道对数据进行转换。默认情况下节点启动后就是预处理节点。
  • 部落节点(Tribe Node) 。部落节点可以连接到不同集群,并且支持将这些集群当成一个单独的集群处理。但它在未来的版本中将会被淘汰。
  • Hot & Warm Node。不同硬件配置的 Data Node,用来实现 Hot & Warm 架构的节点,有利于降低集群部署成本。例如,在硬件资源好的机器中部署 Hot 类型的数据节点,而在硬件资源一般的机器上部署 Warm Node 节点。

在生产环境中建议将每个节点设置为单一角色。如果业务量或者并发量不大的情况下,为了节省成本可以调整为一个节点多种角色的集群。在开发环境中的话,为了节省资源,一个节点可以设置多种角色。

下表是7.8 及之前各个节点类型的配置方式(Hot & Warm Node 需要通过其他方式来设定,不在此列):

节点类型 配置 默认值
Master Eligible node.master true
Data Node node.data true
Ingest Node node.ingest true
Coordinating Node 不需要配置 默认就是coordinating,所以需要设置单一角色时,设置其他类型全部为false

在新版的 ES 中,这个配置方式就做出了改变,使用 node.roles 参数来指定一个节点的角色。其示例如下:

bash
复制代码
node.roles: [ master, data ] //设置节点为 master 候选节点和数据节点

如上示例,node.roles 的值是一个数组,说明一个节点可以有多个角色。node.roles 的可选项如下:

  • master,master 候选节点,master 将会从这些节点中选取出来。
  • voting_only,参与 master 选举的节点,其只有投票权限,当不会成为 master。
  • data,我们最熟悉的一类数据节点。保存文档数据的 shard 将分配到 data 节点中保存。
  • data_content,此角色的节点会处理用户创建的文档内容,如书本信息,歌曲信息这类数据。可以处理 CRUD、数据搜索和聚合等。
  • data_hot,此角色的节点会根据数据写入 ES 的时间存储时序数据,例如日志数据,data_hot 节点对数据读写要求快速,应当使用 SSD 存储。
  • data_warm,data_warm 节点会存储不会经常更新但是仍然被查询的数据,相对于 data_hot,其查询的频率要低。
  • data_cold,很少再被读取的数据可以存储在 data_cold,此类节点的数据是只读的
  • data_frozen,专门用于存储 partially mounted indices 的数据节点。
  • ingest,  预处理数据的节点。
  • ml,  提供机器学习的功能,此类节点运行作业并处理机器学习 API 请求。
  • remote_cluster_client,充当跨集群客户端并连接到其他集群。
  • transform,运行 transforms 并处理 transform API 请求。

注意,上述类型中并没有协调节点的类型选项,那怎么设置一个节点为协调节点呢?其实每个节点本身就是一个协调节点,一定要指定一个节点为协调节点的话,可以这样设置:

node.roles: [ ] //就是啥都不写

在生产环境中建议将节点分层部署,使用专门的节点类型来处理相应的业务需求。

1.3 分片

一个索引可以存储超出单个节点硬件限制的大量数据。比如,一个具有 10 亿文档数据的索引占据 1TB 的磁盘空间,而任一节点都可能没有这样大的磁盘空间。或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch 提供了将索引划分成多份的能力,每一份就称之为分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。

  1. 允许水平扩容
  2. 允许在分片上进行分布式,并行计算,实现高性能,高吞吐量。

一个分片怎样分布,它的文档怎样聚合和搜索请求,是完全由 Elasticsearch 管理的,对于作为用户的你来说,这些都是透明的,无需过分关心。

一个 Lucene 索引 我们在 Elasticsearch 称作 分片 。 一个Elasticsearch 索引 是分片的集合。 当 Elasticsearch 在索引中搜索的时候, 他发送查询到每一个属于索引的分片(Lucene 索引),然后合并每个分片的结果到一个全局的结果集。

1.4 副本

Elasticsearch 允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片(副本)。

  • 在分片/节点失败的情况下,提供了高可用性。因为这个原因,复制分片从不与原/主要分片置于同一节点上是非常重要的。
  • 扩展你的搜索量/吞吐量,因为搜索可以在所有的副本上并行运行。

每个索引可以被分成多个分片。一个索引也可以被复制 0 次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和副本的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变副本的数量,但是你事后不能改变分片的数量。默认情况下,Elasticsearch 中的每个索引被分片 1 个主分片和 1 个副本,这意味着,如果你的集群中至少有两个节点,你的索引将会有 1 个主分片和另外 1 个复制分片(1 个完全拷贝),这样的话每个索引总共就有 2 个分片,我们需要根据索引需要确定分片个数。

在kibana中设置分片数&副本数。

# 创建 mapping 的时候定义好分片和副本数量。
PUT books
{
  "mappings": {
    "properties": {
        "book_id": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        }
      }
  },
  "settings": {
    "number_of_shards": 2, # 定义了 2 个分片
    "number_of_replicas": 2 # 定义了每个分片 2 个副分片
  }
}

1.5 集群健康状态

通过集群的健康状态,我们可以了解集群是不是出现问题了。 集群健康状态有以下 3 种。

  • Green,集群处于健康状态,所有的主分片和副本分片都正常运行。
  • Yellow,所有的主分片都运行正常,但是有部分副本分片不正常,意味着可能存在单点故障的风险(如果部分主分片没有备份了,一旦这个主分片数据丢失,将导致这些数据永久丢失)。如果集群只有 3 个数据节点,但是分配了 4 个副本(主分片 + 副本分片的总数),这个时候有一个副本无法分配的,因为相同的两份数据不应该被分配到同一个节点上。
  • Red,有部分主分片没有正常运行。

需要注意的是,每个索引也有这三种状态,如果索引丢失了一个副本分片,那么这个索引和集群的状态都变为 Yellow 状态,但是其他索引的的状态仍为 Green

1.6 分配

将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。这个过程是由 master 节点完成的。

2.从数据层面看

2.1 索引

一个索引就是一个拥有几分相似特征的文档集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据索引。一个索引由一个名字来标识(必须全部是小写字母),并且当我们要对这个索引中的文档进行索引,搜索,更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多个索引。

能搜索的数据必须索引,这样的好处是可以提高查询速度,ElasticSearch索引的精髓:一切设计都是为了提高搜索的性能。

image.png
上图的索引有三个分片,主分片分别是P1,P2,P3;对应的副本分片为R1,R2,R3;他们分别位于三个节点中。主分片和它的副本分片不会同时分配在同一个节点上。这样是为了保证当一个节点上的主分片down了的时候,其他节点上的副本可以升级为主分片,保证数据高可用。

2.2 映射

mapping 是处理数据的方式和规则方面做一些限制,如:某个字段的数据类型、默认值、分析器、是否被索引等等。这些都是映射里面可以设置的,其它就是处理 ES 里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。

Mapping 一旦定义后,已经定义的字段的类型是不能更改的

2.3 文档

一个文档是一个可被索引的基础信息单元,也就是一条数据。文档以 JSON(Javascript Object Notation)格式来表示,而 JSON 是一个到处存在的互联网数据交互格式。在一个 index 里面,你可以存储任意多的文档。

我们通过kibana的devTool插入索引一条数据:

POST huidong_order/_doc/1
{


  "id":"1",
  "name":"618 ElasticSearch book"
}

在kibana中查询这条数据

POST huidong_order/_search
{


  "query": {
    "match_phrase": {
      "id": "1"
    }
  }
}

查询结果

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "huidong_order",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.2876821,
        "_source" : {
          "id" : "1",
          "name" : "618 ElasticSearch book"
        }
      }
    ]
  }
}

从上面的结果可以看出,我们插入的数据包含在 _source 字段里,结果中还带有其他字段,这些额外的字段都是 ES 为文档加上的元数据,下面是这些字段的解析。

  • _index,文档所属的索引名字,上述是 huidong_order。
  • _type,文档所属的类型名字,现在 ES 7.x 版本的类型统一为 _doc
  • _id,文档的唯一 id。如果我们插入时不指定文档 id,ES 会随机分配,这样有利于数据均匀分散到各个分片。
  • _version,文档的版本信息,并发读写时可以解决文档冲突。
  • _score,相关性算分,代表着查询的匹配性,用来排序。
  • _seq_no 和 _primary_term,是 ES 内部用来保证主分片和副本数据一致性的。当一个 Index 每次选择出主分片后都会有一个序号,记为 _primary_term,其实递增的。而在同一个 Index 下,每次写入数据后都会有一个写入顺序号,记为 _seq_no,其同样也是递增的。  _primary_term 和 _seq_no 在逻辑上构成了一个文档写入的唯一位置。

每个文档都有唯一的 ID。如果插入的时候没有指定,ES会自动生成一个ID。不指定 ID 时插入的性能会好点,因为系统不需要进一步判断这个 ID 是否已经存在。

2.4 字段

相当于是数据表的字段,对文档数据根据不同属性进行的分类标识。

每个字段都有指定的类型,常见的有:keyword、text、数字类型(integer、long、float、double等)、对象类型等。

keyword 类型适合存储简短、结构化的字符串,例如产品 ID、产品名字等。而 text 类型的字段适合存储全文本数据,如短信内容、邮件内容等。对象类型简单来说就是字段的值还是一个 json 对象。

For Example:

PUT huidong_order
{


  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name": {
        "type": "text"
      },
      "product": { 
        "properties": {
          "first": { "type": "text" },
          "last":  { "type": "text" }
        }
      }
    }
  }
}

2.5 词项(Term)

将全文本的内容进行分词后得到的词语就是词项了。例如 “Programmers Love Cat” 使用标准分词器分词后得到 [programmer, love, cat] 这 3 个词项。

分词器除了进行分词外还会进行大小写转换等其他操作。

2.6 倒排索引&正排索引

Elasticsearch 使用一种称为倒排索引的结构,它适用于快速的全文搜索。

有倒排索引,肯定会对应有正向索引。正向索引(forward index),反向索引(inverted index)更熟悉的名字是倒排索引。

所谓的正向索引,就是搜索引擎会将待搜索的文件都对应一个文件 ID,搜索时将这个ID 和搜索关键字进行对应,形成 K-V 对,然后对关键字进行统计计数。

image.png

但是互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID 对应到关键词的映射转换为关键词到文件 ID 的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。

image.png

一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。例如,假设我们有两个文档,每个文档的 content 域包含如下内容:

The quick brown fox jumped over the lazy dog

Quick brown foxes leap over lazy dogs in summer

为了创建倒排索引,我们首先将每个文档的 content 域拆分成单独的 词(我们称它为 词条或 tokens ),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。结果如下所示:

Term Doc_1 Doc_2
Quick X
The X
brown X X
dog X
dogs X
fox X
foxes X
in X
jumped X
lazy X X
leap X
over X X
quick X
summer X
the X

现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档:

Term Doc_1 Doc_2
brown X X
quick X
Total 2 1

两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单相似性算法,那么我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。

目前的倒排索引有一些问题:

  • Quick 和 quick 以独立的词条出现,然而用户可能认为它们是相同的词。
  • fox 和 foxes 非常相似, 就像 dog 和 dogs ;他们有相同的词根。
  • jumped 和 leap, 尽管没有相同的词根,但他们的意思很相近。他们是同义词。

使用前面的索引搜索 +Quick +fox 不会得到任何匹配文档。(记住,+ 前缀表明这个词必须存在。)只有同时出现 Quick 和 fox 的文档才满足这个查询条件,但是第一个文档包含quick fox ,第二个文档包含 Quick foxes 。

我们的用户可以合理的期望两个文档与查询匹配。我们可以做的更好。

如果我们将词条规范为标准模式,那么我们可以找到与用户搜索的词条不完全一致,但具有足够相关性的文档。例如:

  • Quick 可以小写化为 quick 。
  • foxes 可以 词干提取 –变为词根的格式– 为 fox 。类似的, dogs 可以为提取为 dog 。
  • jumped 和 leap 是同义词,可以索引为相同的单词 jump 。

现在索引看上去像这样:

Term Doc_1 Doc_2
brown X X
dog X
fox X
in X
jumped X
lazy X X
over X X
quick X
summer X
the X

这还远远不够。我们搜索 +Quick +fox 仍然 会失败,因为在我们的索引中,已经没有 Quick了。但是,如果我们对搜索的字符串使用与 content 域相同的标准化规则,会变成查询+quick +fox,这样两个文档都会匹配.分词和标准化的过程称为分析。

这非常重要。你只能搜索在索引中出现的词条,所以索引文本和查询字符串必须标准化为相同的格式。

3. 从业务层面看

3.1 准实时

ES 是一个近实时系统,我们写入的数据默认的情况下会在 1 秒后才能被查询到。ES 每秒都会把缓存中的数据写入到 Segment 文件中(写入到 Segment 中才能被检索),然后根据某些规则进行刷盘,并且合并这些 Segment。所以需要注意的是,不能在写入数据成功后,立刻进行查询,这个时候可能会出现查询不到数据或者获取到旧数据的情况。

3.2 Lucene&Es

Lucene不是一个完整的搜索引擎产品,他仅仅是一个搜索内核,它提供了创建文档,分析文档,最终基于分析(分词)结果,建立底层索引的过程。并且Lucene支持了丰富多样的查询,比如基于词的查询,范围查询,前缀匹配查询,组合查询,短语查询,通配符查询,模糊查询等等。

一个 Lucene 索引 我们在 Elasticsearch 称作 分片 。 一个Elasticsearch 索引 是分片的集合。 当 Elasticsearch 在索引中搜索的时候, 他发送查询到每一个属于索引的分片(Lucene 索引),然后合并每个分片的结果到一个全局的结果集。

3.3 相关性打分

Elasticsearch 中的相关性打分是指在执行搜索查询时,根据文档与查询的匹配程度来计算每个文档的相关性得分。相关性打分用于确定搜索结果的排序顺序,将最相关的文档排在前面。

相关性打分基于倒排索引和词频统计等信息进行计算。以下是一些常见的相关性打分算法和概念:

  1. TF-IDF (Term Frequency-Inverse Document Frequency): 这是一种常用的相关性打分算法。它结合了词频和逆文档频率两个因素。词频表示在一个文档中一个词出现的次数,逆文档频率表示一个词在整个文集中的重要性程度。TF-IDF 打分越高,表示词在当前文档中出现频繁且在整个文集中相对稀有,因此与查询的相关性更高。
  2. BM25 (Best Match 25): 这是一种基于概率模型的相关性打分算法。它考虑了词频、文档长度和查询词项的重要性等因素。BM25 在 Elasticsearch 中被广泛使用,默认情况下是默认的相关性打分算法。
  3. Vector Space Model: 这是一种基于向量空间的相关性打分模型。它将文档和查询表示为向量,并计算它们之间的相似度。常见的相似度计算方法包括余弦相似度和欧氏距离等。

这些相关性打分算法根据不同的原理和计算方式来评估文档与查询的匹配程度。它们在 Elasticsearch 中以可插拔的方式实现,您可以根据需要选择合适的打分算法或自定义打分插件。

在查询结果中,每个文档都会被分配一个相关性得分,得分越高表示与查询的匹配程度越高。Elasticsearch 默认按照相关性得分进行排序,将最相关的文档排在前面。我们可以通过自定义查询和打分参数来调整相关性得分的计算方式,以满足用户的搜索需求。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY2Ykkht' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片