一、ES 究竟要设置多少分片数?

在构建 Elasticsearch 集群的初期如果集群分片设置不合理,可能在项目的中后期就会出现性能问题。

Elasticsearch 是一个非常通用的平台,支持各种各样的用例,并且为数据组织和复制策略提供了巨大灵活性。这种灵活性使得作为 ELK 新手的你将数据组织成索引和分片变得困难。虽然不一定会在首次启动时出现问题,但由于数据量随时间的推移,可能会导致性能问题。集群所拥有的数据越多,纠正问题就越困难,甚至有时可能需要重新索引大量数据。

当我们遇到遭遇性能问题的用户时,可以追溯到关于数据索引的数据和群集数量的问题并不罕见。对于涉及 multi-tenancy 或使用基于时间的索引的用户尤其如此。在与用户讨论这个问题时(会议、论坛形式),引申出的一些最常见的问题是:

1)“我应该有多少个分片?”
2)“我的分片应该有多大?”

这篇博客文章旨在帮助您回答这些问题,并为使用基于时间的索引的使用案例( 日志记录或安全分析 )提供实用的指导。

1、什么是分片?

在开始之前,让我们约定文章中用到的一些概念和术语。
Elasticsearch 中的数据组织成索引。每一个索引由一个或多个分片组成。每个分片是 Luncene 索引的一个实例,你可以把实例理解成自管理的搜索引擎,用于在 Elasticsearch 集群中对一部分数据进行索引和处理查询。

【刷新】当数据写入分片时,它会定期地发布到磁盘上的新的不可变的 Lucene 段中,此时它可用于查询。—— 这被称为刷新。

【合并】随着分段数(segment)的增长,这些 segment 被定期地整合到较大的 segments。这个过程被称为合并(merging)。

由于所有段都是不可变的, 因为新的合并段需要创建,旧的分段将被删除 ,这意味着所使用的磁盘空间通常在索引时会波动。合并可能资源相当密集,特别是在磁盘 I/O 方面。

分片是 Elasticsearch 在集群周围分发数据的单位。Elasticsearch 在重新平衡数据时 (例如 发生故障后) 移动分片的速度取决于分片的大小和数量以及网络和磁盘性能。

提示:避免有非常大的分片,因为大的分片可能会对集群从故障中恢复的能力产生负面影响。对于多大的分片没有固定的限制,但是分片大小为 50GB 通常被界定为适用于各种用例的限制。

2、索引有效期( retention period )

由于段是不可变的,更新文档需要 Elasticsearch 首先查找现有文档,然后将其标记为已删除,并添加更新的版本。删除文档还需要找到文档并将其标记为已删除。因此,删除的文档将继续占据磁盘空间和一些系统资源,直到它们被合并,这将消耗大量的系统资源。

Elasticsearch 允许从文件系统直接删除完整索引,而不必明确地必须单独删除所有记录。这是迄今为止从 Elasticsearch 删除数据的最有效的方式。

提示:尽可能使用基于时间的索引来管理数据。根据保留期(retention period,可以理解成有效期)将数据分组。基于时间的索引还可以轻松地随时间改变主分片和副本分片的数量(以为要生成的下一个索引进行更改)。这简化了适应不断变化的数据量和需求。

3、索引和分片不是空闲的?

【集群状态】对于每个 Elasticsearch 索引,其映射和状态的信息都存储在集群状态。这些集群状态信息保存在内存中以便快速访问。因此,如果在集群中拥有大量索引,可能导致大的集群状态(特别是如果映射较大)。所有更新集群状态操作为了在集群中保证一致性,需要通过单个线程完成,因此更新速度将变慢。

提示:为了减少索引数量并避免大的乃至非常庞大的映射,请考虑将相同索引结构的数据存储在相同的索引中,而不是基于数据的来源将数据分割成独立的索引。在每个索引的索引数量和映射大小之间找到一个很好的平衡很重要。

每个分片都有数据需要保存在内存中并使用堆空间。这包括在分片级别保存信息的数据结构,也包括在段级别的数据结构,以便定义数据驻留在磁盘上的位置。这些数据结构的大小不是固定的,并且将根据用例而有所不同。

然而,段相关开销的一个重要特征是它与分段的大小不成正比。这意味着与较小的段相比,较大的段的每个数据量具有较少的开销,且这种差异很大。

【堆内存的重要性】为了能够每个节点存储尽可能多的数据,重要的是尽可能多地管理堆内存使用量并减少其开销。节点拥有的堆空间越多,它可以处理的数据和分片越多。

因此,索引和分片从集群的角度看待不是空闲的,因为每个索引和分片都有一定程度的资源开销。

提示 1:小分片会导致小分段 (segment),从而增加开销。目的是保持平均分片大小在几 GB 和几十 GB 之间。对于具有基于时间的数据的用例,通 常看到大小在 20GB 和 40GB 之间的分片。

提示 2:由于每个分片的开销取决于分段数和大小,通过强制操作迫使较小的段合并成较大的段可以减少开销并提高查询性能。一旦没有更多的数据被写入索引,这应该是理想的。请注意,这是一个消耗资源的(昂贵的)操作,较为理想的处理时段应该在非高峰时段执行。

提示 3:您可以在集群节点上保存的分片数量与您可用的堆内存大小成正比,但这在 Elasticsearch 中没有的固定限制。一个很好的经验法则是:确保每个节点的分片数量保持在低于 每 1GB 堆内存对应集群的分片在 20-25 之间。因此,具有 30GB 堆内存的节点最多可以有 600-750 个分片,但是进一步低于此限制,您可以保持更好。这通常会帮助群体保持处于健康状态。

4、分片的大小如何影响性能?

在 Elasticsearch 中,每个查询在每个分片的单个线程中执行。然而,可以并行处理多个分片,并可以在相同分片上执行多个查询和聚合。

【小分片的利弊】这意味着,在不涉及高速缓存时,最小查询延迟将取决于数据、查询的类型、分片的大小。查询大量小分片将使得每个分片的处理速度更快,但是随着更多的任务需要按顺序排队和处理,它不一定要比查询较小数量的更大的分片更快。如果有多个并发查询,则有很多小碎片也会降低查询吞吐量。

提示:从查询性能角度确定最大分片大小的最佳方法是使用逼真的数据和查询进行基准测试(真实数据而非模拟数据)。始终使用查询和索引负载进行基准测试,代表节点在生产中需要处理的内容,因为单个查询的优化可能会产生误导性的结果。

5、如何管理分片大小?

当使用基于时间的索引时,每个索引传统上都与固定的时间段相关联。每日索引非常普遍,经常用于持有时间区间短或每日量大的数据。这些允许数据期限期间以良好的粒度进行管理,并且可以方便地对每天更换调整 volumes。

时间周期长的数据,特别是如果每日不保存每天的索引数据,则通常会使用每周或每月的保存的碎片大小的增加。这减少了随着时间的流逝需要存储在群集中的索引和碎片数量大小(直译有点费劲此处)。

提示:如果使用固定期限的时间索引数据,可以根据时间周期和预期数据量调整所涵盖的时间范围,以达到目标分片大小。

【均匀更新 & 快速变化的索引数据对比】具有固定时间间隔的基于时间的索引在数据量合理预测并且变化缓慢的情况下工作良好。如果索引率可以快速变化,则很难保持均匀的目标分片大小。

为了能够更好地处理这种情况,推出了 Rollover 和 S**hrink API**。这些增加了如何管理索引和分片的灵活性,尤其适用于基于时间的索引。

此处省略了 Rollover 和 Shrink API 的介绍。(建议查询官网补齐概念再深入)

1) “我应该有多少个分片?”
答:每个节点的分片数量保持在低于每1GB堆内存对应集群的分片在20-25之间。
2) “我的分片应该有多大?”
答:分片大小为50GB通常被界定为适用于各种用例的限制。

二、Elasticsearch 单字段支持的最大字符数

在业务系统中,遇到过两个问题:问题 1:设置为 keyword 类型的字段,插入很长的大段内容后,报字符超出异常,无法插入。问题 2:检索超过 ignore_above 设定长度的字段后,无法返回结果。

1、ignore_above 的作用?

ES 中用于设置超过设定字符后,不被索引或者存储。
Strings longer than the ignore_above setting will not be indexed or stored.

2、ignore_above 用法

PUT ali_test
{
     "mappings": {
         "ali_type": {
             "properties": {
                 "url": {
                     "type": "keyword",
                     "ignore_above": 256
                 },
                 "url_long": {
                     "type": "keyword"
                 },
                 "url_long_long": {
                     "type": "keyword",
                     "ignore_above": 32766
                 }
             }
         }
     }
 }

3、当字符超过给定长度后,能否存入?

验证表名,对于以上 mapping 中设置的 url,url_long,url_long_long3 个字段。超过 256 字符的 url,都可以存入。

3.1 keyword 类型,普通长度验证

插入 url 长度为:1705 个字符,如下所示:

post ali_test/ali_type/1
{
  "url" : "1705个字符的url....省略"
}

检索:

GET ali_test/ali_type/_search
{
     "query": {
         "term": {
             "url": "1705个字符的url"
         }
     }
 }

返回结果:

{
     "took": 1,
     "timed_out": false,
     "_shards": {
         "total": 5,
         "successful": 5,
         "failed": 0
     },
     "hits": {
         "total": 0,
         "max_score": null,
         "hits": []
     }
 }

结论:1705 个字符,url、url_long、url_long_long 都可以存入,可以通过 head 插件查看结果。
但是 url term 检索无法检索返回结果,原因:url 字段设置了 "ignore_above":256,导致超出 256 个字符后不被索引。



es timestamp字段 范围查询 es字段数量限制_es timestamp字段 范围查询

3.2 对于 keyword 类型,临界长度验证

post 32767 个字符的文档,报错如下:

{
     "error": {
         "root_cause": [
             {
                 "type": "illegal_argument_exception",
                 "reason": "Document contains at least one immense term in field='url_long' (whose UTF8 encoding is longer than the max length 32766), all of which were skipped. Please correct the analyzer to not produce such terms. The prefix of the first immense term is: '[104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 103, 111, 111, 103, 108, 101, 46, 99, 111, 109, 47, 115, 101, 97, 114, 99, 104, 63, 104]…', original message: bytes can be at most 32766 in length; got 32767"
             }
         ],
         "caused_by": {
             "type": "max_bytes_length_exceeded_exception",
             "reason": "max_bytes_length_exceeded_exception: bytes can be at most 32766 in length; got 32767"
         }
     },
     "status": 400
 }

post 32766 个字符后,能提交成功,返回结果如下:

{
     "_index": "ali_test",
     "_type": "ali_type",
     "_id": "2000",
     "_version": 1,
     "result": "created",
     "_shards": {
         "total": 2,
         "successful": 2,
         "failed": 0
     },
     "created": true
 }

结论:keyword 类型的最大支持的长度为 ——32766 个 UTF-8 类型的字符。
也就是说 term 精确匹配的最大支持的长度为 32766 个 UTF-8 个字符。

4、引申问题:text 类型和 keyword 类型的存储字符数区别?

  • text 类型:支持分词、全文检索,不支持聚合、排序操作。适合大字段存储,如:文章详情、content 字段等;
  • keyword 类型:支持精确匹配,支持聚合、排序操作。
  • 适合精准字段匹配,如:url、name、title 等字段。

一般情况,text 和 keyword 共存,设置 mapping 如下:

{
     "mappings": {
         "ali_type": {
             "properties": {
                 "title_v1": {
                     "analyzer": "ik_max_word",
                     "type": "text",
                     "term_vector": "with_positions_offsets",
                     "fields": {
                         "keyword": {
                             "ignore_above": 256,
                             "type": "keyword"
                         }
                     }
                 }
             }
         }
     }
 }

5、小结

ES5.X 版本以后,keyword 支持的最大长度为 32766 个 UTF-8 字节数(至于多少个字符数需要根据业务场景定,建议参考最新版本的官方文档说明),text 对字符长度没有限制。

设置 ignore_above 后,超过给定长度后的数据将不被索引,无法通过 term 精确匹配检索返回结果。