一、ElasticSearch 基础概念

一、 Elastic Stack

Elasticsearch 最初是作为独立产品开发的。它的核心作用是提供可扩展的搜索引擎服务,它提供多种语言库API(包括JAVA、Python等),基于分布式模型创建,并对外提供 REST API 接口服务。随着Elastic生态圈的发展,衍生出了 Elasticsearch 的相关 工具集合。例如 Kibana (用于可视化和数据分析)、Logstash (用于日志收集)、Beats (数据传输,轻量级的Logstash)等。Elastic Stack 不单单是Elasticsearch,而是一体化大数据解决方案的工具集。

二、ES 建模方式

2.1 数据集分类

根据数据性质的不同,Elasticsearch 中存储的数据可以分成两类:静态数据时间序列 数据。它们会严重影响集群的配置和管理方式。

  • 静态数据: 增长或更新缓慢的数据集。类似于存储在常规数据库中的数据,就像博客文章、图书馆书籍等。在这些数据中通过 elasticsearch 快速检索相关数据,完成常规数据库难以实现的功能。
  • 时间序列数据: 通常是快速增长或在时间维度上相互关联数据,例如日志文件、度量数据、传感器数据等。在这些数据中通过 elasticsearch 编制索引,以进行数据分析、模式发现和系统监视。

2.2 数据集建模方式

根据不同的数据类型,应该以不同的方式为数据建模。

  • 静态数据: 应该选择固定数量的索引和分片。它们不会快速增长,并且总是希望能够检索数据集中的所有文档。
  • 时间序列数据: 应该选择基于时间的滚动索引。因为会相对频繁地查询最近的数据,并且最终甚至会删除或者归档过时的文档以便节约物理存储资源。

三、ES 搜索评分

对于每个搜索查询,Elasticsearch 都会计算相关性分数。该分数基于 TF-IDF 算法,该算法代表 词频-逆文档频率。在该算法中需要计算两个值,分别是TF和IDF。
基本上,在该算法中计算两个值。

  • TF: 表示在文档中使用给定词项的频率。
  • IDF: 表示给定词项在所有文档中的唯一性。

3.1 词项 TF 计算

例如,如果我们有两个文档:

文档1:To be or not to be, that is the question.
文档2:To be. I am. You are. He, she is.

question 词项的 TF 计算如下:

  • 对于文档1:1/10(10个词项中有1个出现)
  • 对于文档2:0/9(9个词项中出现0次)。

3.2 词项 IDF 计算

IDF 计算为整个数据集的单个值。它是所有文档与包含搜索词的文档的比率。

question 词项的 IDF 计算如下:

在我们的例子中它是:log(2/1)= 0.301 (简化)

其中:

  • 2 - 所有文档的数量,
  • 1 - 包含“question”词项的文档数量。

3.3 相关性得分结果

最后,两个文档的 tf-idf 分数计算为两个值的乘积:

  • 文档1:1/10 x 0.301 = 0.1 * 0.301 = 0.03
  • 文档2:0/9 x 0.301 = 0 * 0.301 = 0.00
    现在可以看到文档1的值为0.03,而文档2的值为0.00。因此,文档1将在结果列表中优先返回。

例如:

# 创建索引
PUT index_test
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      }
    }
  }
}

# 创建文档1
POST index_test/_doc/1
{
  "title":"To be or not to be, that is the question."
}

# 创建文档2
POST index_test/_doc/2
{
  "title":"To be. I am. You are. He, she is."
}

# 创建文档2 
POST index_test/_search
{
  "explain": true, 
  "query": {
    "match": {
        "title":"question"
    }
  }
}

结果:

{
  "took" : 13,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.6785375,
    "hits" : [
      {
        "_shard" : "[index_test][0]",
        "_node" : "_s5Ha-5nRoKfE_Az6iiujg",
        "_index" : "index_test",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6785375,
        "_source" : {
          "title" : "To be or not to be, that is the question."
        },
        "_explanation" : {
          "value" : 0.6785375,
          "description" : "weight(title:question in 0) [PerFieldSimilarity], result of:",
          "details" : [
            {
              "value" : 0.6785375,
              "description" : "score(freq=1.0), computed as boost * idf * tf from:",
              "details" : [
                {
                  "value" : 2.2,
                  "description" : "boost",
                  "details" : [ ]
                },
                {
                  "value" : 0.6931472,
                  "description" : "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
                  "details" : [
                    {
                      "value" : 1,
                      "description" : "n, number of documents containing term",
                      "details" : [ ]
                    },
                    {
                      "value" : 2,
                      "description" : "N, total number of documents with field",
                      "details" : [ ]
                    }
                  ]
                },
                {
                  "value" : 0.4449649,
                  "description" : "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
                  "details" : [
                    {
                      "value" : 1.0,
                      "description" : "freq, occurrences of term within document",
                      "details" : [ ]
                    },
                    {
                      "value" : 1.2,
                      "description" : "k1, term saturation parameter",
                      "details" : [ ]
                    },
                    {
                      "value" : 0.75,
                      "description" : "b, length normalization parameter",
                      "details" : [ ]
                    },
                    {
                      "value" : 10.0,
                      "description" : "dl, length of field",
                      "details" : [ ]
                    },
                    {
                      "value" : 9.5,
                      "description" : "avgdl, average length of field",
                      "details" : [ ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    ]
  }
}
 

四、 数据模型

Elasticsearch 其性能优势主要集中两个方面:

  1. 它可以水平扩展
  2. 检索速度非常快

其中 Elasticsearch 的检索速度主要取决于索引对数据的存储方式。

4.1 索引阶段数据模型(写入)

Elasticsearch 索引文档有三个步骤来规范化文档:

  1. character filters(字符过滤器)
  2. tokenizer(标记生成器)
  3. token filters(标记过滤器)

例如:

To be or not to be, that is the question.

  1. character filters:将 , 替换为 ,将 . 替换为 并且将所有的大写字母替换为小写字母即 To 替换为 to,则原文档更新为

    to be or not to be that is the question

  2. tokenizer:根据 将文档分割为一个个 token

    to、be、or、not、to、be、that、is、the、question

  3. token filters:按照停用词过滤器,它将删除所有常用语言术语 to、be、or、not、that、is、the,仅剩下

    question

4.2 搜索阶段数据模型(查询)

在搜索文档时会应用相同的步骤。Elasticsearch 会搜索带有规范化词项的文档,因此在查询时也会通过相同的三个步骤来规范化文档,获得规范化的词项(takenterm 我理解这两个应该表达的是一个意思)后通过倒排索引结构,快速获取匹配文档。

  1. character filters(字符过滤器)
  2. tokenizer(标记生成器)
  3. token filters(标记过滤器)

Elasticsearch 可以为每个字段定义特定的过滤器。借助于analyzers实现定义。可以使用多个analyzers分词器分析字段以实现不同的目标。例如可以使用 standard 分词器逐字分词,使用 ik_max_word 细粒度分词,使用 ik_smart 粗粒度分词。然后在搜索阶段,通过定义要扫描的字段,获得检索结果。也正是因为 ES 对数据的存储方式,和规范化文档使得它可以更快的提供查询结果

五、分片规划

5.1 分片数和索引数

索引在时就需要确定分片数,并且索引建成后分片数量不能修改,因此确定索引的分片数就尤为重要。根据经验来说,数据集的规模决定了索引的分片数,单个分片最大应包含 20-40 GB的数据。

每一个 shard 都对应一个 Lucene,考虑到 Lucene 用于倒排索引和快速搜索的所有结构和开销,较小的 shard(例如 1 GB)时没有意义的。

所以答案真的取决于你拥有的数据集。根据经验,单分片最大应包含20-40 GB的数据。 Shards来自Apache Lucene。此外,由于分片不能进一步划分,并且始终驻留在单个节点上。20-40 GB 的分片也可以很容易地移动到其他节点,可以说 20-40 GB 的分片是在提供恢复、重建速度和内存消耗之间的折衷值。

当然,这只是一个建议,最合理的规划应当根据实际业务场景,并实现其性能目标。

5.2 注意事项

  1. 为了知道每个索引应该有多少分片,可以简单地估计一下。首先通过将一些文档索引到一个临时索引中,看看它们消耗了多少存储空间,之后预测在某一段时间内有多少文档。这里的时间只的是时间序列数据集中部分时间,或静态数据集中全部时间。
  2. 可以新建索引,并通过 reindex 将数据迁移到合理的索引中。
  3. 查询包含单分片的30个索引和包含30个分片的1个大索引的性能是一致的。

六、节点类型

Elasticsearch节点可以包括多个角色。角色包括:

  • Master:主节点,
  • Data:数据节点,
  • Ingest:摄取节点,
  • Coordinating-only:仅协调节点。

每个角色都有对应的用途。

6.1 主节点

  • 作用: 负责集群范围的设置和更改,例如创建或删除索引,添加或删除节点以及将分片分配给节点。针对大数据量级规模的集群,每个集群中应至少包含3个候选主节点。系统会从所有符合主节点的节点中,选择一个节点作为主节点,其作用是执行集群范围的操作。另外两个节点纯粹是为了获得高可用性。
    硬件要求: 主节点对CPU,RAM和磁盘存储的要求相对较低。

6.2 数据节点

作用: 用于存储和搜索数据。
**硬件要求:**数据节点对所有资源都有很高的要求:CPU,RAM和磁盘。您拥有的数据越多,硬件资源要求也就越高。

6.3 Ingest节点

作用: 在实际索引发生之前,Ingest节点用于文档预处理。Ingest节点拦截批量和索引查询,应用转换,然后将文档传递回索引或批量API。
硬件要求: 低磁盘、中等RAM和高CPU,

6.4 仅协调节点

作用: 客户端请求的负载平衡器。
它知道特定文档可以驻留的位置,并将搜索请求路由到对应节点。
【官方文档警告】:
将过多的仅协调节点添加到集群会增加整个集群的负担,因为所选主节点必须等待来自每个节点的集群状态更新的确认!不应过分夸大仅协调节点的好处 - 数据节点可以愉快地用于相同的目的。

硬件要求: 低磁盘,中高速RAM和中高CPU。

6.5 配置大型集群的建议

  1. 三个主节点 -> 维护集群状态和集群设置,
  2. 两个仅协调节点 -> 它们监听外部请求,并充当整个集群的智能负载平衡器,
  3. 许多数据节点 -> 取决于数据集需求,
  4. 几个 Ingest节点(可选) - 如果 logstash 管道并希望减轻\预处理文档对其他节点的影响。

你可能感兴趣的:(Elastic,Stack,elasticsearch,搜索引擎,大数据)