一、 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 其性能优势主要集中两个方面:
- 它可以水平扩展
- 检索速度非常快
其中 Elasticsearch 的检索速度主要取决于索引对数据的存储方式。
4.1 索引阶段数据模型(写入)
Elasticsearch 索引文档有三个步骤来规范化文档:
- character filters(字符过滤器)
- tokenizer(标记生成器)
- token filters(标记过滤器)
例如:
To be or not to be, that is the question.
-
character filters:将
,
替换为.
替换为To
替换为to
,则原文档更新为to be or not to be that is the question
-
tokenizer:根据
token
to、be、or、not、to、be、that、is、the、question
-
token filters:按照停用词过滤器,它将删除所有常用语言术语 to、be、or、not、that、is、the,仅剩下
question
4.2 搜索阶段数据模型(查询)
在搜索文档时会应用相同的步骤。Elasticsearch 会搜索带有规范化词项的文档,因此在查询时也会通过相同的三个步骤来规范化文档,获得规范化的词项(taken
或 term
我理解这两个应该表达的是一个意思)后通过倒排索引结构,快速获取匹配文档。
- character filters(字符过滤器)
- tokenizer(标记生成器)
- 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 注意事项
- 为了知道每个索引应该有多少分片,可以简单地估计一下。首先通过将一些文档索引到一个临时索引中,看看它们消耗了多少存储空间,之后预测在某一段时间内有多少文档。这里的时间只的是时间序列数据集中部分时间,或静态数据集中全部时间。
- 可以新建索引,并通过
reindex
将数据迁移到合理的索引中。 - 查询包含单分片的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 配置大型集群的建议
- 三个主节点 -> 维护集群状态和集群设置,
- 两个仅协调节点 -> 它们监听外部请求,并充当整个集群的智能负载平衡器,
- 许多数据节点 -> 取决于数据集需求,
- 几个 Ingest节点(可选) - 如果 logstash 管道并希望减轻\预处理文档对其他节点的影响。