我们将更多地讨论使用 Elasticsearch 的最佳实践。这些做法是一般性建议,可以应用于任何用例。 让我们开始吧。

Bulk Requests

批量 API 使得在单个 API 调用中执行许多索引/删除操作成为可能。 这可以大大增加索引速度。 每个子请求都是独立执行的,因此一个子请求的失败不会影响其他子请求的成功。 如果任何请求失败,顶级错误标志设置为 true,错误详细信息将在相关请求下报告。

索引数据的多线程客户端

发送批量请求的单个线程不太可能最大化 Elasticsearch 集群的索引容量。 为了使用集群的所有资源,你应该从多个线程或进程发送数据。 除了更好地利用集群的资源之外,这应该有助于降低每次 fsync 的成本。 索引数据和事务日志都会定期刷新到磁盘。 多线程数据越多,同步到磁盘的数据越多,减少I/O,提高性能。

index.refresh_interval

默认情况下,Elasticsearch 每秒定期刷新索引,但仅限于在过去 30 秒内收到一个或更多搜索请求的索引。如果你没有搜索流量或搜索流量很少(例如,每一次搜索请求少于一个),这是最佳配置 5 分钟)并希望优化索引速度。 此行为旨在在不执行搜索的默认情况下自动优化批量索引。 为了选择退出此行为,请明确设置刷新间隔。 另一方面,如果你的索引遇到常规搜索请求,则此默认行为意味着 Elasticsearch 将每 1 秒刷新一次你的索引。 如果你有能力增加文档被索引和文档变为可搜索之间的时间量,将 index.refresh_interval 增加到一个更大的值,例如 30s,可能有助于提高索引速度,同时也允许有一定的搜索行为。如果你在索引的时候,完全不考虑有搜索数据的可能,你可以直接把这个刷新间隔设置为 -1。

自动生成 IDs

当索引一个具有显式 id 的文档时,Elasticsearch 需要检查同一个分片中是否已经存在具有相同 id 的文档,这是一个代价高昂的操作,并且随着索引的增长而变得更加昂贵。 通过使用自动生成的 ID,ElasticSearch 可以跳过此检查,从而加快索引速度。

index.translog.sync_interval

这个参数决定了 translog 被 fsync 到磁盘并提交的频率,与写操作无关。 默认为 5 秒。 不允许小于 100 毫秒的值。

index.translog.flush_threshold_size

Translog 存储所有尚未安全地持久化在 Lucene 中的操作(即,不是 Lucene 提交点的一部分)。 尽管这些操作可用于读取,但如果分片停止且必须恢复,则需要重放这些操作。 此设置控制这些操作的最大总大小,以防止恢复时间过长。 一旦达到最大大小,就会发生刷新,生成一个新的 Lucene 提交点。 默认为 512 MB。

大文件

大型文档会给网络、内存使用和磁盘带来更多压力。 索引大型文档可以使用一定数量的内存,该内存量是文档原始大小的倍数。 邻近搜索(例如短语查询)和突出显示也变得更加昂贵,因为它们的成本直接取决于原始文档的大小。

显式设置索引映射

Elasticsearch 可以动态创建映射,但它可能不适合所有场景。 例如,Elasticsearch 5.x 中默认的字符串字段映射都是 “keyword” 和 “text” 类型。 在很多情况下是不必要的。

Index Mapping — Nested Types

与父文档中的字段相比,对嵌套字段的查询速度较慢。 匹配嵌套字段的检索增加了额外的减速。 一旦更新包含嵌套字段的文档的任何字段,无论是否更新嵌套字段,所有底层 Lucene 文档(父文档及其所有嵌套子文档)都需要标记为已删除和重写。 除了减慢我们的更新速度之外,这样的操作还会产生垃圾,以便稍后通过段合并进行清理。

Index Mapping

禁用 _all 字段将所有其他字段的值连接到一个字符串中。 它比其他字段需要更多的 CPU 和磁盘空间。 大多数用例不需要 _all 字段。 你可以使用 copy_to 参数连接多个字段。 _all 字段在 Elasticsearch 6.0 及更高版本中默认禁用。 要禁用早期版本中的 _all 字段,请将 enabled 设置为 false。

使用索引模板

索引模板定义分片数量、副本和映射等设置,你可以在创建新索引时自动应用这些设置。 Elasticsearch 根据与索引名称匹配的索引模式将模板应用于新索引。

使用副本实现可扩展性和弹性

Elasticsearch 旨在始终可用并根据你的需求进行扩展。 它通过自身的分布式设计来做到这一点。 你可以向集群添加节点以增加容量,Elasticsearch 会自动将你的数据和查询负载分布到所有可用节点上。 为了使 Elasticsearch 具有高可用性,其索引需要具备适当的容错能力。 这可以使用副本分片来实现。 副本分片是主分片的副本。 副本提供数据的冗余副本,以防止硬件故障并增加处理读取请求(如搜索或检索文档)的能力。

分片大小

分片是幕后的 Lucene 索引,它使用文件句柄、内存和 CPU 周期。 ES 中索引的默认分片策略是 5 个主分片和一个副本。 选择多个分片的目的是在集群中的所有数据节点上均匀分布一个索引。 但是,这些碎片不应该太大或太多。 一个好的经验法则是尝试将分片大小保持在 10–50 GB 之间。 大分片会使 Elasticsearch 难以从故障中恢复,但由于每个分片都使用一定量的 CPU 和内存,因此拥有太多小分片会导致性能问题和内存不足错误。

在多个数据节点中保持索引的分片数,这些节点具有相同的大小并跨节点分布

通过模板设置主分片计数,将每个主分片的最大容量设定为 50GB(日志分析)或最大 30GB(搜索用例)。数据节点之间的分片分配将根据 2 个重要规则进行。

相同索引的主分片副本分片不会分配在同一个数据节点上。

根据节点上可用的分片数量或均衡集群中所有节点中每个索引的分片数量,将分片放置在节点上。 另请注意,可能会有较大的分片分配给某些节点而较小的分片分配给其他节点。 建议索引的分片数(主+副本)应该是数据节点数的倍数。 比方说,你有一个 4 节点的集群,索引的总分片(主 + 副本)应该是 4 或 8 或 12 等。这确保数据在节点之间均匀分布。

索引生命周期管理

如果你要处理时间序列数据,则不想将所有内容连续转储到单个索引中。 取而代之的是,你可以定期将数据滚动到新索引,以防止数据过大而又缓慢又昂贵。 随着索引的老化和查询频率的降低,你可能会将其转移到价格较低的硬件上,并减少分片和副本的数量。

要在索引的生命周期内自动移动索引,可以创建策略来定义随着索引的老化对索引执行的操作。 索引生命周期策略在与 Beats 数据发件人一起使用时特别有用,Beats 数据发件人不断将运营数据(例如指标和日志)发送到 Elasticsearch。 当现有索引达到指定的大小或期限时,你可以自动滚动到新索引。 这样可以确保所有索引具有相似的大小,而不是每日索引,其大小可以根 beats 数和发送的事件数而有所不同。

让我们通过动手操作场景跳入索引生命周期管理(Index cycle management: ILM)。 本文章将利用你可能不熟悉的ILM独有的许多新概念。 我们先用一个示例来展示。本示例的目标是建立一组索引,这些索引将封装来自时间序列数据源的数据。 我们可以想象有一个像Filebeat这样的系统,可以将文档连续索引到我们的书写索引中。 我们希望在索引达到 50 GB,或文档的数量超过10000,或已在30天前创建索引后对其进行 rollover,然后在90天后删除该索引。

按日期组织索引数据

对于大多数日志记录或监控用例,我们可以将索引组织为每天、每周或每月,然后我们可以得到一个指定日期范围的索引列表。 Elasticsearch 只需要查询较小的数据集而不是整个数据集。 此外,当数据过期时,收缩/删除旧索引也很容易。我们可以参考文章 “Elasticsearch:使用 ingest pipeline 来管理索引名称” 来管理索引名称。