Elasticsearch:如何部署 Elasticsearch 来满足自己的要求
2022年11月10日 | by mebius
在我之前的文章:
我们涉及到一些分片管理,内存管理已经分片策略的一些知识。在实际的使用中,我们该如何对 Elasticsearch 集群做正确的 sizing。我们到底需要多少内存,多少个 CPU,多少个 shards 等等。在今天的文章中,我总结一些专家的建议,希望对于正确使用 Elasticsearch 提供一些参考的意见。在实际的使用中,可能需要根据自己的使用进行调整。如果你对分片(shard)的概念不是很熟悉的话,请阅读我之前的文章 “Elasticsearch 中的一些重要概念: cluster, node, index, document, shards 及 replica”。
分片策略
在最新的 Elasticsearch 的默认部署中,每个创建的索引只是有一个 primary shard。这个是在索引创建的时候定义的。比如我们创建一个索引:
PUT order
我们通过如下的命令来获得它的设置:
GET order/_settings
上面的命令将返回如下的结果:
{
"order": {
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "order",
"creation_date": "1666584276053",
"number_of_replicas": "1",
"uuid": "tmVY0iDoTnGDuEZRpf_0hA",
"version": {
"created": "8040399"
}
}
}
}
}
我们可以看到在默认的情况下, number_of_shards 的数值为 1,也即一个 primary shard。同时它的 number_of_replicas 的值为 1,也即一个 replica。你可以在创建索引的时候,指定 number_of_shards 为超过 1 的数值。最多可以达到 1024。比如:
PUT order
{
"settings": {
"number_of_shards": 4,
"number_of_replicas": 2
}
}
如果你已经创建了索引,那么再重新定义 number_of_shards 时,你需要 reindex 才可以。
无法在实时索引上修改分片的数量
索引创建并运行后,分片数量无法更改。 当我们创建索引时,Elasticsearch 默认关联一个分片和一个副本(在版本 7 之前,默认是 5 个分片和 1 个副本)。 虽然设置了分片的数量(很像写在石头上),但可以在索引的生命周期中使用索引设置 API 更改副本的数量。 根据路由算法,文档存放在特定的分片中:
shard_number = hash(document_id) % number_of_primary_shards
由于算法直接取决于分片的数量,因此在实时运行期间更改分片数量将修改当前文档的位置并破坏它。 这反过来又会损害倒排索引和检索过程。 不过,有一种方法可以解决这个问题:重新索引(reindex)。 通过重新索引,我们可以根据需要更改分片设置。 有关 reindex 的介绍,请阅读文章 “Elasticsearch: Reindex 接口”。
我们该如何计算多少个 shard 呢?如上所示,假如我们定义创建的索引 order 含有 4个 primary shards 及 2 个 replica shards,那么:
针对这个 order 索引,我们供需要 4 个 primary shards 以及 8 个 replica shards。这样我们供需要 8 + 4 = 12 个 shards。
如果我们为一个索引分配更多的 primary shards,则意味着:
- 跟多的并行处理 indexing。当我们向集群同时写入大量数据时,那么有更多的 primary shards 可以同时对写入的文档进行分词处理从而提高写入的速度
- 对搜索的速度可能稍微有些影响,尽管不是绝对的。这是因为 Elasticsearch 的搜索是一个分布式的,当一个请求发送到一个 coodinating 节点后,它会分发这个搜索到各个节点进行搜索。当每个节点在本地完成了搜索后,它们会把自己的结果一并发送到 coordinating 节点,并进行合并得出最后的结果,比如前十匹配的结果。Primary shard 的数量越多,那么合并的工作会更多。
在实践中,为了更好的搜索效果,我们建议每个 shard 的大下位于 10 GB 到 50 GB 之间:
经常被问到的一个常见问题是关于分片的大小。 这里没有万能的。 根据组织当前的数据要求和未来需求,必须进行(尽职调查)规模试验以获得结论性结果。 业界的最佳实践是将单个分片的大小设置为不超过 50 GB。 但我也看到分片的大小达到 400 GB。 事实上,GitHub 的索引分布在 128 个分片中,每个分片 120 GB。 我个人的建议是将它们保持在 25 GB 到 40 GB 之间,同时牢记节点的堆内存。 如果我们知道某个电影索引在某个时候最多可以保存 500 GB 的数据,建议将这些数据分布在 10 到 20 个分片中。为了达到调优,一般可以考虑针对 search 使用案例时,shard 的大小为 25 GB。不要超过 50 GB。
更大的 shard 大小很难是一个集群在经历失败后恢复过来,这是因为更难把一个 shard 从一个节点移动到另外一个节点,特别是一个节点在进行维护时。
在调整分片大小时还需要考虑一个参数:堆内存。 众所周知,节点的计算资源是有限的,例如内存和磁盘空间。 每个 Elasticsearch 实例都可以根据可用内存进行调整以使用堆内存。默认情况下,Elasticsearch 使用 1 GB 内存进行实例化,但可以通过编辑安装的 config 目录中的 jvm.options 文件来更改设置。 调整 JVM 的 Xms 和 Xmx 属性以根据您的可用性和需要设置帮助内存。针对每个 GB 的 heap 内存,我们希望对于的 shard 数量是 20,尽管这个在 Elastic Stack 8.3 版本发布后有所改变。详细规则请tgcode阅读链接。依据这个原则,假如我们有 30 GB 的 heap 内存,那么我们理想的 shard 数量应该为 600 个。
这里的要点是分片是保存我们数据的分片,因此我们必须做初步的工作以正确调整大小。 大小取决于索引保存的数据量(包括未来的需求)以及我们可以分配给节点的堆内存量。 在载入数据之前,每个组织都必须制定分片策略。 必须在数据需求和最佳分片数量之间取得平衡。
分配多少个 shards 及多少个 nodes?
我们可以参考之前的文章 “Elasticsearch:我的 Elasticsearch 集群中应该有多少个分片?” 做更进一步的阅读。根据一些公司的经验,我们可以简单地做如下的表述:
Number of shards = (SourceData + Room to Grow) x (1 + 10% Indexing Overhead) / Desired Shard Size
我们可以根据一下的公式计算出需要多少存储:
Minimum storage required = SourceData x (1+ Number of Replicas) x 1.45
在上面,这个系数 1.45 包含 Elasticsearch, 索引以及 Linux kernel 的开销。
针对一个处理复杂聚合,搜索强求以及高输出,经常更新的集群来说,我们可以通过如下的公式来计算 CPU 以及内存的需求:
node config = 2 vCPU and 8GB memory for every 100 GB of storage
重要的是,在设置 Elasticsearch 的节点 JVM heap 大小不要超过 50% 的机器物理内存,并且这个数值不可以超过 32 GB。
下面,我们以一个简单的例子来展示上面的公式。假设我们有 150 GB 的数据需要索引到 Elasticsearch 集群。假如你这个数据是静态的,并且将来不会有增长,那么我们可以可以计算出 primary shards 的数量:
Number of primary shards (starting from scratch or no growth) = (150+0) x 1.10 / 25 = 7
在上面,我们假设每个 shard 的大小为 25 GB。上面的公式给出的结果为 7,也即我们需要有 7 个 primary shards。
假如,你在未来的一年,可能需要有 150 GB 的大小增长,那么我们可以使用如下的公式来算出 primary shards 的数量:
Number of primartgcodey shards (with 100% growth in a year) = (150+150) x 1.10 /25 = 13
上面的公式给出的结果约为 13,也即我们需要有 13 个 primary shards。
这个在实际的使用中非常重要,因为一旦我们定义好 primary shards 的数量,那么我们就不能再改动了,除非我们创建一个新的索引,并使用 reindex 包数据重新写入。
我们再假设我们有一个比较小的 Elasticsearch 集群,它含有 3 个节点 (master + data)。同时,我们假设我们的每个 shard 有一个 replica。我们通过如下的公式来计算最小的存储空间:
Minimum storage required = 150 x (1+1) x 1.45 = 435 GB
上面的公式给出的答案是 435 GB。
我们通过如下的公式来计算内存的要求:
Compute and Memory = 435 x (2 vCPU and 8 GB memory)/100 = 9 vCPUs and 35 GB of memory
根据上面的计算,我们的3个节点,每个需要有 3 vCPUs,12 GB 内存 以及 150 GB 的磁盘存储。
我们的每个节点含有 12 GB 的内存,我们可以设置 JVM 的 heap 大小为 6GB 。这个相当于每个节点最多含有 120 个 shards。3 个节点总共最多含有 360 个shards。
如果你需要检测你的配置,你可以使用 Elastic 官方提供的工具GitHub – elastic/rally: Macrobenchmarking framework for Elasticsearch。