社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Elasticsearch

在生产环境运行Elasticsearch深度指南

高可用架构 • 5 年前 • 796 次点击  

在本文,我不是来告诉你 Elasticsearch 强大,快速并且几乎可以正常运行。


在本文,我也是来告诉你 Elasticsearch 可能是不透明的,让人困扰,并且似乎无缘无故地出问题。在这篇


在本文,我想分享我的经验和技巧,了解如何正确配置 Elasticsearch 并避免常见的陷阱。


我写文章目的也不是为了赢利,所以我会将全部内容放到这一篇文章中,而不是将它分解成一系列。你可以随意跳过不感兴趣的章节。


基础知识:集群,节点,索引和分片


如果你是 Elasticsearch(ES)新手,我想先解释一些基本概念。本节完全不涉及最佳实践,主要侧重于解释术语。大多数人可以直接跳过本节。


Elasticsearch 是用于运行 Apache Lucene(基于 Java 的搜索引擎)分布式管理框架。Lucene 是实际保存数据并进行索引和搜索的地方。ES 位于它之上,让你可以并行运行数千个 Lucene 实例。


ES 的最高级别单元是集群(cluster)。集群是 ES 节点 和索引的集合。


节点 (Node) 是 ES 的实例。它可以是单个服务器,也可以是服务器上运行的 一个 ES 进程。服务器和节点不同,一个 VM 或物理服务器可以包含许多 ES 进程,每个 ES 进程是一个节点。节点只可以加入一个集群。节点有不同类型(type),其中最值得关注的两个类型是数据节点(data node)主候选节点( Master-Eligible node)。一个节点可以同时具有多种类型。数据节点运行所有数据操作,即存储、索引和检索数据。主候选节点具有投票 master 的权限,用于管理集群和索引。


索引(Index)是对数据的高级抽象,索引本身不保存数据,它们只是实际存储数据的另一种抽象。对数据执行的任何操作(例如插入,删除,建立索引和搜索)都会对索引产生影响。索引可以完全属于一个集群,并且由分片( shard) 组成。


分片(Shard)是 Apache Lucene 的实例。一个分片可以容纳许多文档。分片是数据存储,索引和搜索的实际对象。分片只属于一个节点和索引。分片有两种类型:primary 和 replica,它们基本上是完全相同的,拥有相同的数据,并且搜索并行运行在所有分片。在拥有相同数据的所有分片中,其中一个属于 primary。这是唯一可以接受索引请求的分片。如果节点中的主分片挂了,副本将接管并成为主分片。然后,ES 将创建一个新的副本并复制数据。


总结一下,我们整理得到下图:


更深入了解 Elasticsearch


如果你想运行一个系统,相信你需要了解该系统。在本节中,我将解释 Elasticsearch 的各个部分,如果想在生产中进行管理,我相信你需要理解它。本节也不牵涉到具体建议,后文会介绍。本节目的只是为了介绍必要的背景。


Quorum 


理解 Elasticsearch 是一个(有缺陷的)选举体系非常重要。节点投票决定谁应该管理它们,即主节点。主节点运行大量集群管理进程,并且在许多事务方面拥有最终决策权。ES 选举是有缺陷的,是因为只有一小部分节点,即主候选(master-eligible)节点才具有投票权。主候选节点是通过以下配置启用:


node.master: true


在集群启动或主节点离开群集时,所有符合主选举条件的节点都会开始选举新的主节点。为此,你需要具有 2n + 1 个主候选节点。否则,可能会出现脑裂情况,比如同时两个节点获得 50% 的选票,将会导致两个分区之一中的所有数据丢失为了不发生这种情况。你需要 2n + 1 个符合主候选的节点。


节点如何加入集群


当 ES 节点启动时,它在广阔世界中独自存在。它怎么知道它属于哪个集群?有不同的方法可以完成此操作,如今大多使用种子主机(Seed Host)的方法来实现。


基本上,Elasticsearch 节点会不断地就他们所见过的所有其他节点进行通讯。因此一个节点最初只需要了解几个其他节点即可了解整个集群。让我们来看一个三节点集群的示例:

初始状态


最初,节点 A 和 C 只知道 B。B 是种子主机。种子主机要么以配置文件的形式提供给 ES,要么直接放入 elasticsearch.yml 中。


节点 A 与 B 连接并交换信息


一旦节点 A 连接到 B,B 就知道 A 的存在。对于 A 而言,没有任何变化。


节点 C 连接并与 B 共享信息


现在,C 连上来。一旦发生这种情况,B 就会告诉 C 有 A 的存在。C 和 B 现在知道群集中的所有节点。一旦 A 重新连接到 B,它也将了解 C 的存在。


段和段合并


上面我说过数据存储分片中,这只是部分正确。最终数据是以文件的形式存储在文件系统中。在 Lucene 和 Elasticsearch 中,这些文件称为段(Segment)。一个分片将具有一到数千个段。


同样,段是实际的真实文件,你可以在 Elasticsearch 安装的 data 目录中查看它。这意味着使用段存在开销。如果要查看段,则必须找到文件并打开它。这意味着需要打开许多文件,并且将会有很多开销。Lucene 中的段是不可变的,这是个问题。它们只能写一次,然后就不能改了。反过来,这意味着你放入 ES 中的每个文档都将创建一个仅包含这个文档的段。显然,一个拥有十亿个文档的集群具有十亿个段,这意味着文件系统上确实有十亿个文件,这样理解对吗?非也。


在后台,Lucene 进行持续的段合并,它不能更改段,但是可以使用两个较小段的数据合并创建新的段。

这样,lucene 会不断尝试使段数(文件数,即开销)保持较小。你也可以使用强制合并。


消息路由


在 Elasticsearch 中,你可以对集群中的任何节点运行任何命令,返回结果是相同的。有趣的是,文档最终将只存在于一个主分片及其副本中,而 ES 不知道它在哪里。没有一个映射来记录某个文档位于哪个分片中。


当执行搜索时,获取请求的 ES 节点会将其广播到索引中的所有分片。即主分片及所有副本。这些分片然后会在包含该文档的所有段中进行查找。


当执行插入时,ES 节点将随机选择一个主分片并将文档放在其中。然后将其写入该主分片及其所有副本。


如何在生产环境运行 Elasticsearch?


本节是实践部分。我前面提到,我管理 ES 的主要目的是为了记录日志,本文将尽力避免这种倾向的影响,但有可能会失败。


大小


需要提出并随后回答自己的第一个问题是关于大小调整。你需要多少规模的 ES 集群?


内存


我首先说的是 RAM,因为 RAM 将限制所有其他资源。



ES 用 Java 编写,Java 使用堆,你可以将其视为 Java 保留的内存。如果将所有堆的重要因素都列出,会使这个文档的大小增加三倍,所以我将介绍最重要的部分,即堆大小。


尽量使用更多内存,但堆大小不得超过 30G。


有一个很多人都不知道的关于堆的秘密:堆中的每个对象都需要一个唯一的地址,即一个对象指针。该地址的长度是固定的,这意味着可以寻址的对象数量是有限的。简单一点来描述就是,超出某个范围时,Java 将开始使用压缩的对象指针而不是未压缩的对象指针。这意味着每个内存访问都将涉及其他步骤,这会严重拖慢速度因此你 100% 不需要设置过此阈值(大约 32G)。


我曾经整整一个星期都呆在一个黑暗的房间里,没做别的,只是使用 esrally 基准化测试 Elasticsearch 在不同文件系统、堆大小、文件和 BIOS 设置组合。长话短说,下面就是关于堆大小如何设置的内容:


添加索引延迟,越低越好


命名约定为 fs_heapsize_biosflags。如你所见,从 32G 的堆大小开始,性能突然开始变差。吞吐量也同样情况:


索引附加中值吞吐量。越高越好。


长话短说:如果想幸运一点,请使用 29G 或 30G 的 RAM,并使用 XFS,并尽可能启用 hardwareprefetch 和 llc-prefetch。


文件缓存


大多数人在 Linux 上运行 Elasticsearch,Linux 使用内存作为文件系统缓存。常见的建议是 ES 服务器使用 64G 内存,这样的想法是一半用于缓存,一半用于堆。我尚未测试过文件缓存。但是不难看出,大型 ES 集群(如用于日志记录)可以从配置大文件缓存中受益匪浅。如果你所有的索引都适合放入内存堆,则不会那么多好处。


CPU


这取决于对集群执行的操作。如果进行大量索引,与仅执行日志记录相比,你需要更多更快的 CPU。对于日志记录,我发现 8 个 CPU 核绰绰有余,但是发现很多人使用更大的配置,但是对他的使用场景并没有什么好处。


磁盘


这块也没有想像那么直接。首先,如果索引能放入 RAM,则磁盘仅在节点冷启动时才重要。其次,实际可以存储的数据量取决于索引布局。每个分片都是一个 Lucene 实例,它们都有内存需求。这意味着你可以在堆中容纳最大数量分片是有限的。我将在索引布局部分中详细讨论这一点。


通常,你可以将所有数据磁盘放入 RAID0。你需要在 Elasticsearch 级别进行复制,因此丢失一个节点无关紧要。请勿将 LVM 与多个磁盘一起使用,因为 LVM 一次只能写入一个磁盘,根本就不会带来多个磁盘的好处。


关于文件系统和 RAID 设置,我整理了以下几点:


  • Scheduler:cfq 和  deadline 优于 noop。如果你有 nvme,Kyber 可能会很好,但我还没有测试过

  • QueueDepth:尽可能高

  • 预读:请打开

  • Raid chunk size:无影响

  • FS 块大小:无影响

  • FS 类型:XFS > ext4


索引布局


这在很大程度上取决于你的用例。我只能从日志场景(尤其是使用 Graylog)讨论一下。


分片


精简版:


  • 对于写入繁重的工作负载,主分片 = 节点数

  • 对于读取繁重的工作负载,主分片 * 副本数 = 节点数

  • 多副本 = 更高的搜索性能


可以通过以下公式给出最大写入性能:


node_throughput * number_of_primary_shards 

节点吞吐量 * 主分片数量


原因很简单:如果只有一个主分片,那么写入速度只类似于单节点,因为一个分片只能位于一个节点上。如果确实想优化写入性能,则应确保每个节点上只有一个分片(主节点或副本),因为在此情况下副本可以获得与主节点相同的写入速度,并且写入很大程度上取决于磁盘 IO。注意:如果有很多索引,那么上述的策略可能有问题,性能瓶颈可能是其他原因。


如果要优化搜索性能,可以通过以下公式给出:


node_throughput * (number_of_primary_shards + number_of_replicas) 

节点吞吐量 *(主分片数量 + 副本数)


对于搜索,主分片和副本基本相同。因此,如果想提高搜索性能,只需增加副本的数量。


大小


关于索引大小,我已经多次讨论过。以下是我的经验:


30G of heap = 140 shards maximum per node

30G 堆内存 = 可以在一个节点最多启动 140 个分片


使用 140 个以上的分片,Elasticsearch 就会进程崩溃并出现内存不足错误。这是因为每个分片都是 Lucene 实例,并且每个实例都需要一定数量的内存。这意味着每个节点可以拥有的分片数量是有限制的。


如果你有大量节点,分片和索引大小,则可以容纳多少个索引可以由以下公式计算:


number_of_indices = (140 * number_of_nodes) / (number_of_primary_shards * replication_factor) 

索引数 = (140*节点数)/(主分片数 * 复制因子)


根据磁盘大小,可以很容易地计算出索引的大小:


index_size = (number_of_nodes * disk_size) / number_of_indices

索引大小 = (节点数量 * 磁盘大小)/ 索引数量


然而索引越大搜索越慢。对于日志记录来说,慢一点问题不大,但是对于真正的搜索量大的应用程序,应该根据 RAM 大小来调整索引大小。


段合并


每个段都是文件系统中的一个实际文件,更多的段意味着更大的读取开销。基本上,对于每个搜索查询,它都会转到索引中的所有分片,再转到分片中的所有段。多个段极大地增加集群读取 IOPS,直到它变得不可用。因此,需要尽可能减少段的数量。


force_merge API 允许你将段合并到某个数量,比如 1。如果你执行索引滚动(例如使用 Elasticsearch 做日志记录),那么在集群未使用时执行常规强制合并是一个好建议。强制合并会占用大量资源,并且会显著降低集群的运行速度。因此,最好不要让 Graylog 帮你来做,而是选择在较少使用集群时间自己完成。如果你有很多索引的话,你必须定期执行段合并。否则,集群运行速度将会非常慢并最终挂掉。


集群布局


对于除最小设置外的所有设置,最好使用专用的候选主节点。主要原因是确保始终有 2n + 1 个候选主节点来确保仲裁。但是对于数据节点,可以在任何时候添加新节点,而不必担心这个需求。另外,我们不希望数据节点上的高负载影响主节点。


最后,主节点是种子节点的理想候选节点。请记住,种子节点是在 Elasticsearch 中进行节点发现的最简单方法。由于主节点很少更改,因此它是最佳选择,因为它很可能已经知道集群中的所有其他节点。


主节点可以非常小,一个 CPU 核,4G 的 RAM 已经足够大多数集群使用。当然也需要关注实际使用情况,并进行相应调整。


监控


ES 为你提供了大量的指标,并且以 JSON 的形式提供所有指标,这使得传递给监控工具非常容易。以下是一些有用的监控指标:


  • 段数

  • 堆使用

  • 堆 GC 时间

  • 平均 搜索,索引,合并时间

  • IOPS

  • 磁盘利用率


结论


本文花了我大约 5 个小时,包含了我对 ES 的所有了解,希望能让你碰到问题的时候不那么头疼。


资源


  • https://www.elastic.co/guide/zh-CN/elasticsearch/reference/current/modules-node.html

  • https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-discovery-quorums .html

  • https://github.com/elastic/rally

  • https://tech.ebayinc.com/engineering/elasticsearch-performance-tuning-practice-at-ebay/


原文链接:

https://facinating.tech/2020/02/22/in-depth-guide-to-running-elasticsearch-in-production/


参考阅读:


本文作者 Mattis Haase,由高可用架构翻译。技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。


高可用架构

改变互联网的构建方式


长按二维码 关注「高可用架构」公众号

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/55266
 
796 次点击