草庐IT

Elasticsearch:如何部署 Elasticsearch 来满足自己的要求

Elastic 中国社区官方博客 2023-10-08 原文

在我之前的文章:

我们涉及到一些分片管理,内存管理已经分片策略的一些知识。在实际的使用中,我们该如何对 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 版本发布后有所改变。详细规则请阅读链接。依据这个原则,假如我们有 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 primary 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

有关Elasticsearch:如何部署 Elasticsearch 来满足自己的要求的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  8. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  10. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

随机推荐