提升 Elasticsearch 性能的关键优化技巧,50ms提升到1ms!!!

开发 架构
在 Trendyol(阿里巴巴旗下土耳其电商平台),我们始终关注卖家和买家,努力使国际销售尽可能顺畅高效。我的团队在这个过程中发挥着关键作用,使卖家只需点击一下就能接触到国际买家。

在微服务架构中,速度和效率至关重要,每一毫秒都可能产生巨大影响。最近,我们在一个使用 Elasticsearch 来查找商品列表的微服务上深有体会。起初,每个 Elasticsearch 查询 大约需要 50-60 毫秒才能完成——在某些情况下还算可以,但在处理大量请求和频繁更新时,这就成了瓶颈。即使是微小的延迟也会影响系统性能和用户满意度。

认识到需要改进后,我们开始进行重大更改。我们的目标是减少延迟,同时确保服务能够高效地处理大量请求。经过一系列调整和优化 Elasticsearch 查询 后,我们将延迟降低到 1 毫秒以下。这一重大改进不仅使服务速度大幅提升,还增强了其处理大量请求的能力。

在本文中,我们将逐步介绍实现这一性能提升的步骤。我们将涵盖从调整查询到架构更新的具体更改和策略。

1、背景介绍

在 Trendyol(阿里巴巴旗下土耳其电商平台),我们始终关注卖家和买家,努力使国际销售尽可能顺畅高效。我的团队在这个过程中发挥着关键作用,使卖家只需点击一下就能接触到国际买家。这意味着卖家无需手动更新新市场的价格或库存水平。他们可以轻松地向全球客户销售商品,以最小的努力开拓新的机会。

这一操作的核心是一个 微服务,负责更新国际销售的产品价格。系统中的每个商品列表都属于一个“内容(content)”。当产品在其他国家销售时,必须考虑多个因素来设定正确的价格,例如卖家的货币和运费。一个详细的算法会计算出一个“系数率(coefficient rate)”,该系数率与价格相乘以计算新价格,针对每个内容与相应的“店面(storefront)”(即国家),然后将此信息发送到一个 Kafka 主题。

图片图片

我们的微服务监听该 Kafka 主题 上的更新,处理传入的数据,并使用 Elasticsearch 查找相关的商品列表。为此,我们创建了一个 Elasticsearch 查询 来找到需要根据新系数调整的相关商品列表。找到这些列表后,服务将包含系数率的修订列表发布到另一个 Kafka 主题,在那里进一步处理以设置新价格。

如果该系统出现任何延迟,价格更新可能无法立即应用。这意味着价格调整可能需要更长时间才能反映出来,可能会影响一致性。因此,系统中的每一毫秒都很重要。

2、性能测试方法

在深入探讨性能调优细节之前,了解我们如何测试和评估微服务和 Elasticsearch 查询 的性能非常重要。为了测试,我们使用了公司内部开发和维护的工具 Ares。Ares 使我们能够对应用程序进行全面的负载测试,包括 Elasticsearch 查询 和整体系统性能。

铭毅备注:关于性能测试工具,咱们可以使用 Elasticsearch 平替的开源方案 esrally,或者我们通用的方案:JMeter 等。

JMeter 如何实现 Elasticsearch 8.X 性能测试?

首先,我们从生产环境中选择大量样本。通常,我们会检索一份包含 10,000 个内容的列表,代表我们需要测试的数据。然后,我们在测试工具中创建一个 Elasticsearch 任务,使用这个内容列表。此设置有助于我们模拟真实世界的条件,并有效地对 Elasticsearch 索引施加压力。

以下是我们用于测试的查询示例:

{
    "query": {
        "bool": {
            "filter": [
                {
                    "term": {
                        "contentId": 10863010
                    }
                },
                {
                    "terms": {
                        "storefrontId": [
                            "50",
                            "35",
                            "36",
                            "43",
                            "48",
                            "49"
                        ]
                    }
                }
            ]
        }
    },
    "_source": [
        "storefrontId",
        "listingId"
    ],
    "sort": [
        {
            "storefrontId": "asc",
            "listingId": "asc"
        }
    ]
}

该查询基于特定的 contentId 和一组 storefrontId 检索文档。它使用 bool 查询 和 filter 子句 来选择匹配给定内容 ID 的文档。此外,它过滤 storefrontId 以确保结果与目标市场相关。

3、性能优化策略

3.1. 减少分片数量

在 Elasticsearch 中,分片是存储的基本单位,将索引拆分为更小的部分,使系统能够在多个节点上分配数据和查询。我们在 Elasticsearch 集群中进行的第一个优化是减少过多的分片数量。

最初,我们的集群有超过 100 个分片,导致系统资源的低效使用。

为了解决这个问题,我们将分片数量减少到与节点数量相匹配,这不仅降低了资源开销,还显著提高了查询速度和集群稳定性。

以下是减少分片数量后集群的分片分布情况:

图片图片

3.2. 限制段数量

我们的第二个优化是解决随着索引操作而增加的段(segment)数量。

段是分片内更小的不可变数据单元,随着段的累积,搜索延迟会增加,因为 Elasticsearch 需要搜索更多的段。

为了解决这个问题,我们实施了一个段合并策略来控制并逐步减少段的数量,优化搜索性能。

起初,我们尝试在段数量增加时强制段合并,但这种方法不足以限制段数量。为了解决这个问题,我们实施了一个段合并策略来控制并逐步减少段的数量,优化搜索性能。以下是我们应用的策略字段:

  • max_merge_at_once_explicit: "4":控制显式合并操作中一次可以合并的最大段数,限制为 4 可以防止在手动合并期间过度使用资源。
  • max_merge_at_once: "4":限制自动合并时一次可以合并的段数,保持在 4 以确保受控的合并,维持系统稳定性。
  • max_merged_segment: "30gb":定义合并段的最大大小,限制为 30GB 可以避免创建过大的段,导致内存和性能问题。
  • segments_per_tier: "2":限制每个合并层允许的段数,限制为 2 有助于保持较低的段数量,通过优化 Elasticsearch 必须搜索的段数来降低搜索延迟。
  • floor_segment: "20gb":设置有资格合并的最小段大小,小于 20GB 的段将首先被合并,防止大量小段的累积,可能会降低搜索性能。

图片图片

3.3. 类型转换优化

我们实施的下一个优化是将用于 term 查询 的字段类型更改为 keyword。

keyword 存储在倒排索引中,使查找速度极快,非常适合 term 或精确匹配查询。

鉴于我们只需要这些字段进行精确匹配,我们决定将其转换为 keyword 类型,并重新索引了所有文档。

在转换字段类型后,我们重新索引了所有文档,并再次进行了负载测试。

结果令人印象深刻:搜索速率飙升至每秒约 50,000 个查询,而延迟降至 1 毫秒以下。

类型转换前的集群性能:

图片图片

类型转换后的集群性能:

图片图片

这一优化不仅提升了查询性能,还展示了在 Elasticsearch 中为特定查询用例选择正确字段类型的重要性。

3.4. 启用请求缓存

我们实施的另一个性能改进是启用 Elasticsearch 集群中的 request_cache。此缓存对于处理重复查询非常有用,例如重试或从 Kafka 多次摄取同一事件的情况。通过在索引上启用请求缓存,我们确保了这些重复查询的响应时间更快。

Elasticsearch 的缓存特别有效之处在于,每当刷新间隔触发时,它会自动失效缓存数据,这意味着缓存的数据始终接近实时,避免了一致性问题。

尽管这可以显著提高查询速度,但需要考虑它可能导致的内存使用增加。因此,启用 request_cache 是一个强大的优化,但应与内存考虑保持平衡。

要在 Elasticsearch 中为索引启用请求缓存,可以使用以下命令:

PUT /your_index_name/_settings
{
  "index": {
    "requests.cache.enable": true
  }
}

3.5. 优化排序

在 Elasticsearch 中查询超过 10,000 个文档时,我们使用了 Point In Time (PIT)。

干货 | 全方位深度解读 Elasticsearch 分页查询

PIT 允许我们通过捕获索引在特定时刻的快照来执行一致的搜索,确保查询不受正在进行的索引操作影响。所有的 PIT 搜索请求都会自动包含一个隐式的排序断点字段 _shard_doc,有助于保持一致的分页。如果无法使用 PIT,确保在排序子句中包含一个唯一的断点字段至关重要,以防止分页结果中出现遗漏或重复。

在我们的案例中,原始查询按 listingId 和 storefrontId 对结果进行排序。然而,由于我们主要关注的是避免重复,而不是使用特定的排序字段,我们从查询中删除了这些排序字段。

取而代之的是,我们按照建议使用 _shard_doc 对结果进行排序。

搜索响应中,每个命中都会包含一个排序值数组。使用 PIT 时,每个命中的最后一个排序值包含断点 _shard_doc。该值在 PIT 的上下文中对每个文档都是唯一的,由分片索引和 Lucene 的内部文档 ID 组合而成。这种方法确保我们高效地管理文档分页,而不会引入重复。

4、总结

通过针对性的优化,我们将 Elasticsearch 查询 的延迟从 50-60 毫秒降低到 1 毫秒以下,显著提升了系统性能。

这些优化包括降低分片数量、有效管理 段合并、启用 请求缓存 和为精确查询优化 字段类型。

这些经验表明,在 Elasticsearch 中进行针对性的优化可以带来速度和整体系统响应能力的显著提升。

原文地址:https://medium.com/trendyol-tech/unlocking-speed-key-optimizations-for-elasticsearch-performance-20af2cb4ac87

原文作者:Mert Oz

责任编辑:武晓燕 来源: 铭毅天下Elasticsearch
相关推荐

2024-05-16 11:51:44

前端性能优化JavaScript

2023-09-26 12:02:34

C++循环

2023-08-08 08:36:52

Vue.js代码Pinia

2020-12-09 22:15:40

物联网IOT客户关系

2023-07-21 12:51:32

2016-07-19 09:35:34

云计算

2022-10-18 14:22:27

2023-04-11 16:28:31

人工智能AI

2021-05-28 11:02:11

VR

2023-12-14 12:56:00

MongoDB数据库优化

2018-02-23 11:34:31

苹果App开发者

2011-05-17 10:53:41

链路

2023-11-27 15:41:16

物联网数字孪生

2024-04-12 08:28:38

优化查询语句PostgreSQL索引

2024-03-19 13:52:05

NVIDIAQuantum全新网络交换机

2024-11-15 10:45:56

2024-03-14 10:10:03

MySQL优化事务

2011-10-22 11:31:23

UbuntuLTS

2009-11-07 22:29:41

2014-10-08 10:37:41

SQLite
点赞
收藏

51CTO技术栈公众号