Epic Games如何调优OpenSearch支撑Fortnite分析
译文 AI 逐段翻译
Epic Games 的一个团队如何为 Fortnite 分析调优 Amazon OpenSearch Service
自 2017 年 Fortnite 发布以来,Epic Games已覆盖全球数亿玩家。Fortnite 运行在 Amazon Web Services (AWS) 上,并利用诸如Amazon OpenSearch Service等服务来驱动某些内部分析,并在大规模决策中发挥作用。
Amazon OpenSearch Service 在理解游戏生态系统方面一直很有帮助。OpenSearch Service 支持两种类型的用例:搜索工作负载和分析工作负载。Epic Games 的一个团队有一个用例,用于存储和分析滑动窗口的游戏事件数据。这涉及到支持复杂的查询和多层聚合,这些聚合将分析结果反馈到其他内部系统,帮助他们推动不断发展的玩家体验。在像 Fortnite 这样拥有庞大玩家基础的游戏规模下,这些查询要处理大量的传入数据。
这些见解有助于识别新兴的游戏趋势,了解玩家如何与新内容互动,并揭示更多关于 Fortnite 生态系统的信息。它们为实时运营决策提供信息,并根据社区中的聚合活动向玩家展示相关内容。
随着 Epic Games 的基础设施处理数十亿遥测事件,该团队发现了优化其 OpenSearch Service 集群以提升性能和成本效率的机会。本文详细介绍了 Epic Games 如何与 AWS 合作改造其 OpenSearch Service 部署,在降低运营成本的同时,实现了查询延迟和资源利用率的显著改善。
挑战
Epic Games 运行着一个 OpenSearch Service 域,该域处理持续的高容量写入以及 CPU 密集型的批处理聚合作业。理想情况下,这些聚合作业应该更频繁地运行,以保持分析的新鲜度。较短的任务间隔意味着更新鲜的数据,用于识别游戏趋势、检测异常和指导实时运营决策。但现有的配置无法在不扩大域规模超出工作负载合理需求的情况下支持这一点,从而推高了成本。Epic Games 与 AWS 合作,以确定可以改进的地方,重点关注硬件利用率、分片策略、索引映射和查询行为等领域。
观察
该集群运行在 r7g 内存优化数据节点上,每个节点具有 48 个 vCPU 和 384 GiB 内存。每个节点的可用内存中,只有一小部分(32 GiB)分配给 Java 虚拟机(JVM)堆,设置为压缩普通对象指针的最大推荐值。压缩普通对象指针其余部分(堆外内存)用于文件系统缓存和操作系统。数据节点的系统内存并未充分利用(图 1)。
图 1:跨数据节点的系统内存利用率
如上图所示,在观察期间利用率远低于 100%,确认分配给这些节点的大量堆外内存未使用。多余容量可以安全地换取额外的计算资源。
JVM 内存压力如图 2 所示,相关的垃圾收集指标(计数和时间)如图 3 所示。
图 2:JVM 内存压力

图 3:JVM 垃圾收集指标,计数(上)和时间(下)
这些图表显示,JVM 内存压力保持在临界阈值以下,垃圾收集计数和时间都较低且稳定,表明整个域的 JVM 利用率健康。
虽然集群级 CPU 指标乍一看很健康(图 4),但放大节点级指标揭示了明显的节点热点。节点热点的根本原因是集群的分片策略。
图 4:集群级 CPU 利用率
该集群的数据节点分布在多个可用区。每个索引使用一定数量的主分片和副本,当分片达到一定大小后进行滚动。乍一看,配置似乎很均衡,分片副本分布在可用区之间,每个节点持有合理的数据份额。
然而,主分片数低于数据节点总数。这意味着针对最新数据的搜索(这是最常见的访问模式)只会在可用节点的子集上执行。结果,一些节点出现持续的 CPU 热点,而其余节点则利用不足(图 5)。
图 5:节点级 CPU 利用率显示热点
如上图所示,一些节点的 CPU 利用率高达 90%,而其他一些节点则低于 20%,突出了查询执行在集群中的不均匀分布。
建议和实施
基于这些观察,AWS 与 Epic Games 合作,实施了一系列有针对性的优化,涵盖硬件选择、分片策略、索引映射和查询行为。以下部分详细介绍了每项建议及其实现方式。
调整集群规模
由于聚合查询本质上是 CPU 密集型的,并且集群的 JVM 内存压力远在可接受范围内,AWS 建议从内存优化的 r7g 实例迁移到计算优化的 c7g 实例。c7g 系列提供更高的 vCPU 与内存比例,更适合处理能力而非内存容量是限制因素的工作负载。
提议的架构要求比现有 r7g 数量更多的 c7g 节点。此次迁移在仅使用原始内存三分之二的情况下,跨集群实现了约 33% 的聚合 CPU 容量提升。净效果是成本降低了约 10%,通过使实例类型与实际工作负载性质对齐,以更低的成本提供了更多的处理能力(表 1)。
| R7g(之前) | c7g(之后) | 净影响 | |
| 实例系列 | 内存优化 | 计算优化 | 更好的 CPU 与内存比例,适用于聚合工作负载 |
| 每个节点的vCPU数 | 相同 | 相同 | 每节点CPU相同。节点越多,总CPU越高 |
| 每个节点的内存 | 更高 | 更低 | 减少未使用的内存;JVM堆不变 |
| 总CPU | 基准 | 总vCPU增加33% | 在更高的节点数上更均匀地分布 |
| 成本 | 基准 | 约降低10% | 每美元获得更多性能 |
表1:实例迁移对比,r7g与c7g
分片策略
为了支持新的集群规模,Epic Games团队改变了分片策略,使主分片数量与数据节点数相匹配,并设置1个副本。这将写密集负载和批量聚合搜索查询负载均匀分布在所有可用数据节点上。
团队采用了ISM(索引状态管理)策略,通过滚动管理分片大小,使用min_primary_shard_size将分片大小控制在建议范围内。这保持了分片数量的有界和可预测性,提供了清晰的扩展模式:调整节点数,然后相应更新ISM策略。
实施后,节点级CPU利用率显示出更均匀的分布(图6)。
图6:分片优化后的节点级CPU利用率
如图6所示,域中的所有节点都在相似的CPU利用率水平上工作,确认数据和流量在集群中分布良好,没有节点热点。
映射优化
索引映射在许多字段上同时启用了text和keyword字段类型,即使访问模式显示这些字段仅用于聚合、排序或过滤上下文,从未用于全文匹配查询。删除冗余的text字段类型减少了存储开销,并通过消除索引时不必要的分析提高了查询性能。
对于高基数字符串字段,murmur3字段类型在索引时计算一次并存储,用于基数聚合。它不是每次查询时对关键字值进行哈希,而是murmur3在索引时计算一次哈希,并将其存储为数值doc_value,这样聚合可以跳过查询时昂贵的字符串哈希步骤(基数估计本身仍在查询时计算)。
以下示例说明了映射更改:
"some_field": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"another_field": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"cardinality_field": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},"some_field": {
"type": "keyword"
},
"another_field": {
"type": "keyword"
},
"cardinality_field": {
"type": "keyword",
"fields": {
"hash": {
"type": "murmur3"
}
}
},| 之前: | 之后: |
这些映射更改减少了总体存储,降低了分片数量(从而减少了CPU需求),并减少了集群管理器节点状态大小。
索引优化
还应用了额外的索引级优化以提高查询性能并减少开销。索引排序配置为默认为主日期字段,通过将物理数据布局与最常见的查询顺序对齐,提高了基于时间的访问模式的性能。ISM策略更新为在滚动后对索引进行force merge到1个段,减少了只读索引上的段开销。最后,调整了刷新间隔以平衡索引吞吐量与搜索新鲜度。
从OpenSearch Service 2.17升级到3.1
域从OpenSearch Service 2.17升级到3.1,减少了错误计数并提高了Amazon OpenSearch Ingestion管道级别的吞吐量。性能提升显著:仅升级后,sum聚合的p99延迟就下降了40-50%,大型96小时基数聚合的p95下降了超过40%。所有查询类型的通用查询性能都有所提高,线程池压力显著降低,导致429错误大幅减少(图7)。

图7:OpenSearch Service 3.1升级前后的查询性能
从Graviton 3升级到Graviton 4
实例类型从c7g(Graviton 3)升级到c8g(Graviton 4)。性能提升立竿见影:
- 所有查询的p99:380毫秒降至250毫秒。
- 所有查询的p95:245毫秒降至230毫秒。
- 所有查询的p90:225毫秒降至200毫秒。
- 所有查询的p50:100毫秒降至70毫秒。
日期窗口基数查询的p99从220毫秒降至98毫秒,总和聚合也有类似提升。总体吞吐量提高了16%。
分层缓存
随着升级到OpenSearch Service 3.1,团队启用了tiered caching。分层缓存扩展了默认的堆上请求缓存,增加了基于磁盘的层。当项目从堆上缓存中逐出时,它们会溢出到节点本地SSD上的更大磁盘缓存中,而不是被丢弃。这使集群能够为更大的查询集保留缓存结果,而不会增加JVM堆使用量。
Epic Games工作负载中的批量聚合作业会在重叠的时间窗口内重复查询。仅靠堆上缓存太小,无法在连续作业运行之间保留结果,因此每次都会重新计算昂贵的聚合。启用磁盘层后,较长时间窗口聚合(如96小时基数查询)的结果在运行之间得以保留。这为一些较大的聚合查询(尤其是跨越较长时间窗口的查询)带来了更一致和更快的结果。
结果总结
下表总结了每项优化的影响。
| 优化策略 | 影响 |
| 合理调整大小(r7g到c7g) | CPU增加33%,成本降低10% |
| 分片重新平衡 | 消除了节点上的CPU热点 |
| 映射优化 | 减少存储、分片数量和集群状态大小 |
| OpenSearch Service 2.17到3.1 | p99总和聚合减少40-50%,429错误减少 |
| Graviton 3到Graviton 4 | p99从380毫秒降至250毫秒,吞吐量提高16% |
| 分层缓存 | 大型聚合查询结果更一致 |
表2:结果总结
结论
通过优化其OpenSearch Service部署,Epic Games的一个团队将p99查询延迟从380毫秒降至250毫秒,吞吐量提高了16%,成本降低了10%。这些收益来自于将实例类型、分片策略、映射和引擎版本与实际工作负载需求对齐。
要了解更多关于为工作负载优化Amazon OpenSearch Service的信息,请参阅Amazon OpenSearch Service最佳实践。有关支持的实例类型的详细信息,请参阅Amazon OpenSearch Service 支持的实例类型。