查询速度提升 98.9%,索引吞吐量提升 4 倍:系统化诊断 Elasticsearch 性能

查询速度提升 98.9%,索引吞吐量提升 4 倍:系统化诊断 Elasticsearch 性能
作者来自 Elastic Aleksandar Panov结合使用 AutoOps、 Profile API 和 ES Rally 来发现集群热点、慢查询和索引瓶颈并通过真实基准测试展示延迟降低 98.9%、索引吞吐量提升 4 倍。想体验一下 Elastic Cloud 吗订阅 AWS Marketplace 上的 Elastic Cloud 或 Microsoft Azure Marketplace 上的 Elastic Cloud即可获得最高 1,000 美元的账单抵扣额度。AutoOps、 Profile API 和 ES Rally 这三款 Elastic 工具能够在整个技术栈的各个层面系统化地诊断 Elasticsearch 的性能问题。在一个物流配送场景中它们发现了导致搜索延迟飙升至 30 秒的分片不均衡、一个将 98.9% 执行时间浪费在深度分页上的查询以及将批量写入吞吐量限制到可实现水平四分之一的索引设置。本文将介绍每种工具、它们能够发现的问题以及如何利用这些发现来解决问题。当有数十位用户持续连接并使用由 Elasticsearch 集群支撑的平台时快速了解潜在的性能瓶颈以及如何解决它们至关重要。首先要回答的问题是应该从哪里开始下面是一个可供参考的决策路径该路径首先从 AutoOps 开始用于检测集群级别的问题例如资源压力或不合理的分片分布。如果集群状态正常则使用 Profile API 来识别慢查询候选查询可以来自慢查询日志或应用侧日志。接下来使用 ES Rally 对可能限制吞吐量的索引设置进行基准测试。如果这三个工具都没有发现问题那么瓶颈就在客户端。如何使用 AutoOps 检测集群级别的性能问题首先要回答的问题始终都是问题是否出在集群本身AutoOps 的设计目标是提供实时集群诊断、给出针对性的建议并帮助你快速提升集群健康状况。它默认包含在 Elastic Cloud 中并且最近已作为一种https://www.elastic.co/blog/autoops-free。它的设置非常简单只需不到五分钟你就可以看到数据开始流入 AutoOps 提供的一整套综合图表。其背后的思路是在靠近集群的位置部署一个 Elastic Agent它会将数据上报到 AutoOps 平台而该平台与你的 Elastic Cloud 账户相连接。下面是安装指南。它的一个优点是能够立即提供那些难以通过现有集群监控技术发现的警告和建议。更有价值的是它还会直接展示这些问题的解决方案。在我们的物流配送场景中AutoOps 发现了一项能够解释用户反馈性能缓慢的问题集群负载严重不均衡。节点 es01 几乎承担了所有流量而另外三个节点几乎处于空闲状态搜索延迟飙升至 30 秒正如我们将在下一节的图表中看到的那样。AutoOps 如何发现节点热点和分片不均衡AutoOps 的节点性能图表显示es01 几乎承担了全部索引流量5.6 docs/sec而 es02、es03 和 es04 则基本处于空闲状态。如下图所示CPU 使用率集中在单个节点es01上该节点承载了负载更高的索引而另外三个节点大部分时间都处于空闲状态。异常行为的下一个信号出现在搜索延迟图中。通过使用 AutoOps 的节点级搜索延迟视图我们发现了一些出乎意料的结果。当其他节点几乎没有搜索延迟时负载最高的节点却表现出了影响搜索应用的延迟症状。它不仅承担了写入任务还负责处理所有搜索请求。分析过程中我们发现一个配送索引仅存储在一个节点上而没有分布到全部四个节点。这就是问题的根本原因。通过重新索引数据增加主分片数量我们平衡了查询负载并消除了这一主要问题。本文聚焦于 AutoOps 的一个发现以便使诊断流程更加清晰。如果你希望深入了解 AutoOps 能够发现的各种问题可以参考关于热点 hotspotting 、CPU 使用率过高以及长时间运行查询的专题文章。如何使用 Profile API 查找 Elasticsearch 慢查询当集群恢复健康之后下一个问题就是是否存在某些开销很高的查询第一步是找到这些候选查询。你可以通过 Elasticsearch 的慢查询日志或者通过应用侧日志记录每次搜索请求所花费的时间来进行分析。在我们的案例中问题出现在配送跟踪页面的无限滚动场景中 —— 随着用户不断向下滚动结果性能逐渐变差。Profile API 是一个用于分析长时间运行查询并查找搜索瓶颈的强大工具。使用起来非常简单只需在任意搜索请求中将profiletrue打开响应结果中就会包含一个 profile 部分提供详细的耗时拆解。它能够指出哪个阶段占据了主要执行时间例如查询阶段、聚合阶段或数据获取阶段。下面我们来看一个深度分页的案例。在移动应用的无限滚动场景中用户持续向下滚动应用则每次请求获取 100 条文档。随着用户持续滚动搜索请求会进入越来越深的分页而from参数也会不断增大GET delivery-records/_search { profile: true, from: 9000, size: 100, query: { bool: { filter: [ { term: { payment_type: 1 } }, { range: { tax_amount: { gte: 5 } } } ] } }, sort: [ { delivery_pickup_datetime: { order: desc } } ] }下面汇总的 Profile 数据已从纳秒转换为毫秒清楚地显示了瓶颈大部分时间都消耗在查询阶段和获取阶段之外由巨大的from偏移量导致的协调工作上。Elasticsearch 必须收集 9,100 个匹配文档对它们进行排序丢弃前 9,000 个文档然后只返回请求的 100 个文档。组件阶段时间ConstantScoreQuery 包含项查询34.4msBooleanQuery查询31.1msQueryPhaseCollector 包含项收集22.1msSimpleFieldCollector9,100 个文档优先级队列收集19.1msFetchPhase获取12.1ms其他步骤例如请求解析和反序列化、队列等待时间、响应构建1,885.4ms总计2,004.2ms使用 search_after 修复深度分页在这种情况下search_after方法最多可以快两倍因为它避免了扫描和丢弃之前的结果。相反它会从第 90 页返回的最后一个文档继续执行使用其delivery_pickup_datetime值以 epoch 毫秒表示作为游标并且只获取接下来的 100 条记录。GET delivery-records/_search { profile: true, size: 100, track_total_hits: false, query: { bool: { filter: [ { term: { payment_type: 1 } }, { range: { tax_amount: { gte: 5 } } } ] } }, sort: [ { delivery_pickup_datetime: { order: desc } } ], search_after: [ 1451604674000 ] }如下方对比所示使用search_after方法时性能有了显著提升。组件阶段时间ConstantScoreQuery 包含项查询6.8msBooleanQuery查询6.2msQueryPhaseCollector 包含项收集2.6msPagingFieldCollector收集2.0msFetchPhase获取2.2ms其他步骤例如请求解析和反序列化、队列等待时间、响应构建9.4ms总计29.2ms性能对比组件深度分页from:9000search_after时间节省百分比 已节省ConstantScoreQuery34.4ms6.8ms27.6ms80.2%BooleanQuery31.1ms6.2ms24.9ms80.1%QueryPhaseCollector总计22.1ms2.6ms19.5ms88.2%FieldCollect19.1ms2.0ms17.1ms89.5%FetchPhase12.1ms2.2ms9.9ms81.8%总计2,004.2ms29.2ms1,975ms98.5%查询总时间从 1,004 ms 降低到 29 ms提升 98.5%几乎完全是因为 Elasticsearch 不再需要在每次请求中构建并丢弃一个包含 9,000 个文档的优先队列。如何使用 ES Rally 对 Elasticsearch 索引设置进行基准测试在集群健康并且查询优化完成后最后的问题是索引设置本身是否成为瓶颈ES Rally 是由 Elastic 制作的官方基准测试工具。它的核心优势是可复现性你可以在相同的集群和硬件上针对两个配置运行相同的工作负载因此结果中的任何差异都完全取决于你更改的设置。ES Rally 能够在完全相同的条件下测量改进效果相同的集群、硬件和数据集。为什么默认 Elasticsearch 索引设置会限制批量索引吞吐量在很多情况下较慢的索引速度是由默认索引设置导致的这些设置适合开发环境但不适合生产环境。例如默认的refresh_interval为 1 秒会增加资源使用因为每次 刷新都会创建一个新的可搜索段而副本数量为 1 会使每个索引请求的写入操作数量翻倍。例如我们的交付物流平台每天批量导入数千条新记录这正是这些默认设置会造成影响的工作负载类型。将refresh_interval设置为 -1 可以在批量加载阶段禁用刷新操作而临时将副本数量降低到 0 可以减少一半的写入操作。导入完成后这两个设置都会恢复。在这个演示中我们不会重点介绍如何准备自定义数据用于基准测试但值得一提的是有一篇关于这方面的不错文章。为了进行基准测试你需要设置两个文件夹一个包含当前设置另一个包含候选设置。在这个案例中示例数据集包含 100 万条记录。下面描述的所有文件都可以在这个代码仓库中找到。两个 track 文件夹都包含一个index-settings.json文件。这些文件允许你调整映射、副本分片数量、分片设置以及字段类型例如将字段从double转换为scaled_float或者从text转换为keyword。因为 ES Rally track 可以快速重新运行所以在迭代过程中尝试不同配置并评估新的优化思路会非常容易。下一步是运行 race 命令用于当前配置和候选配置的 race以收集统计数据。esrally race \ --track-pathdelivery-records-current \ #(name of current setup folder) --target-hostses01:9200 \ #(location of es cluster) --pipelinebenchmark-only \ --report-formatcsv \ --report-filecurrent.csv \ --race-idrun-current \ --on-errorabortesrally race \ --track-pathdelivery-records-contander \ --target-hostses01:9200 \ --pipelinebenchmark-only \ --report-formatcsv \ --report-filecontander.csv \ --race-idrun-contander \ --on-errorabort快速提示使用有意义的--race-id值而不是自动生成的值会让比较命令更容易运行。在两个 race 都完成后比较结果esrally compare --baselinerun-current --contenderrun-contander此外报告会生成到 csv 文件中可以轻松进行处理以提取重要数据。指标当前设置候选设置变化累积索引时间0,8265170,7559678.54%平均吞吐量11,090 docs/s50,921 docs/s359 %中位数吞吐量10,821 docs/s51,304 docs/s374 %最小吞吐量10,335 docs/s41,387 docs/s300 %最大吞吐量13,006 docs/s55,342 docs/s326%候选配置仅通过在批量加载阶段调整refresh_interval和副本数量就实现了大约 3–4 倍更快的索引吞吐量。在实际应用中单次比较通常是不够的。常见的后续实验包括将 double 字段转换为 float 或 scaled_float将 text 字段更改为 keyword调整分片数量以及调优 refresh interval。因为 ES Rally track 可以快速重新运行所以迭代测试这些选项非常简单。当 Elasticsearch 不是瓶颈时应该做什么如果 AutoOps 显示集群健康Profile API 显示查询速度很快并且 ES Rally 确认索引设置不是限制因素那么瓶颈就在客户端。Elasticsearch 端已经不再是需要关注的地方。常见的应用层原因包括网络延迟存在于客户端和集群之间尤其是在跨区域、VPN 或代理环境中。客户端反序列化大型响应负载时产生的开销。使用 _source exclude 或 fields 参数仅返回客户端实际需要的内容。客户端队列等待发生在请求到达集群之前。Elasticsearch 客户端使用 HTTP 连接池。在并发负载下请求需要排队等待可用连接。Profile API 永远看不到这种等待因为请求还没有离开客户端。单次搜索调用而 _msearch 可以将独立查询批量处理为一次网络往返。单文档索引而 _bulk API 可以将每次请求的开销分摊到多个文档上。AutoOps、Profile API 和 ES Rally 的诊断价值正是在于它们可以让你在转向应用代码之前明确排除集群、查询和索引设置的问题。当这三个工具都没有发现问题时调查方向就会从 Elasticsearch 转移到客户端。要在应用侧调查这些问题Elastic APM 是自然的下一步工具它可以追踪客户端中的请求路径并准确显示 Profile API 无法看到的那类集群前等待时间。结论回到最初的问题即交付物流平台中的搜索缓慢问题我们从基础设施层一直追踪到了查询和设置层AutoOps 发现了不均衡的分片分布将所有查询负载集中到了单个节点上导致最终用户遇到最高达 30 秒的延迟峰值。Profile API 显示深度分页是慢查询的来源。从 from/size 切换到search_after消除了 98.9% 的延迟。ES Rally 确认在批量导入期间优化索引设置特别是refresh_interval和副本数量可以将吞吐量提升 3–4 倍。每个工具都回答不同的问题。AutoOps 提供“是否存在结构性问题”的视角。Profile API 回答“为什么这个特定查询很慢”ES Rally 验证“这些修改是否真的能改善性能以及改善多少”将这些工具结合使用并按照这个顺序执行它们可以覆盖应用 Elasticsearch 侧完整的诊断范围。当这三个工具都没有发现问题时下一步就是按照上一节所述检查客户端。原文Diagnose Elasticsearch performance with AutoOps and ES Rally - Elasticsearch Labs