分享

单集群日搜索请求超 4 亿,开源搜索和分析引擎 Elasticsearche 对 Bay 的性能优化...

 万皇之皇 2018-01-17

摘要:Elasticsearch 是基于 Apache Lucene 的开源搜索和分析引擎,允许用户以近乎实时的方式存储,搜索和分析数据。虽然 Elasticsearch 专为快速查询而设计,但其性能在很大程度上取决于用于应用程序的场景,索引的数据量以及应用程序和用户查询数据的速率。这篇文章概述了挑战和调优过程,以及 Pronto 团队以战略方式构建应对挑战的工具。它还以各种图形配置展示了进行基准测试的一些结果。以下是正文。

Elasticsearch 是基于 Apache Lucene 的开源搜索和分析引擎,允许用户以近乎实时的方式存储,搜索和分析数据。Pronto 是在 eBay 网站上托管 Elasticsearch 集群的平台,该平台使得 eBay 网内部客户易于部署,操作用于全文搜索,实时分析和日志 / 事件监控的大规模的 Elasticsearch。当前 Pronto 平台管理着 60 多个 Elasticsearch 集群和 2000 多个节点,日采集量达到 180 亿份文档,日均搜索请求达到 35 亿份。平台提供从条款,补救以及安全到监控,报警和诊断的全方位的信息。

虽然 Elasticsearch 专为快速查询而设计,但其性能在很大程度上取决于用于应用程序的场景,索引的数据量以及应用程序和用户查询数据的速率。这篇文章概述了挑战和调优过程,以及 Pronto 团队以战略方式构建应对挑战的工具。它还以各种图形配置展示了进行基准测试的一些结果。

挑战

迄今为止所观察到的 Pronto / Elasticsearch 使用案例面临的挑战包括:

  1. 高吞吐量:一些集群每天摄取高达 5TB 的数据,一些集群每天的搜索请求超过 4 亿。如果 Elasticsearch 无法及时处理这些请求,那么这些请求将在上游累积。

  2. 搜索延迟低:对于性能关键的集群,尤其是面向站点的系统,低搜索延迟的特性是必须具有的,否则用户体验将会受到影响。

  3. 由于数据或查询是可变的,所以最佳设置总是在变化。所有情况都没有最佳设置。例如,将索引拆分成更多的分片(代表索引分片,Elasticsearch 可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。)对于耗时的查询是很有好处的,但是这可能会损害其它查询性能。

解决方案

为了帮助客户应对这些挑战,Pronto 团队从用户案例开始入手并持续整个集群生命周期,构建性能测试、调优和监控的战略方法。

  1. 评估集群大小:在一个新的用户案例部署之前,收集客户提供的信息,诸如吞吐量,文档大小,文档数量和搜索类型,以评估 Elasticsearch 集群的初始大小。

  2. 优化索引设计:与客户一起评审索引设计。

  3. 调优索引性能:根据用户场景调优索引性能和搜索性能。

  4. 调优搜索性能:使用用户真实数据 / 查询运行性能测试,用 Elasticsearch 配置参数的组合比较和分析测试结果。

  5. 运行性能测试:在案例启动以后,集群将受到监控,每当数据发生变化,查询更改或者流量增加时,用户都可以自由地重新运行性能测试。

评估集群大小

Pronto 团队为每种类型的机器和每个支持的 Elasticsearch 版本运行基准测试,以收集性能数据,然后将其与客户提供的信息一起用于评估集群的初始大小,这些信息包括:

  • 索引吞吐量

  • 文档大小

  • 搜索吞吐量

  • 查询类型

  • 热索引文档计数

  • 保留策略

  • 响应时间要求

  • SLA 级别

优化索引设计

在开始摄取数据并运行查询之前,请三思而后行。索引代表着什么?Elastic 的官方回答是“具有相似特征的文档集合”。那么下一个问题是“应该使用哪些特征来对数据进行分组?应该把所有文件放入一个索引还是多个索引呢?”答案是,这取决于所使用的查询。下面是关于如何根据最常用的查询分组索引的一些建议。

  • ** 如果查询有一个过滤字段并且它的值是可枚举的,那么把数据分成多个索引。** 例如,有大量的全球产品信息被摄取到 Elasticsearch 中,大多数查询都有一个过滤子句“region”(区域),并且很少有机会运行跨区域查询。查询主体可以优化为:

{

'query': { 'bool': { 'must': { 'match': { 'title': '${title}' } }, 'filter': { 'term': { 'region': 'US' } } } }}

在这种情况下,如果索引按照美国,欧洲等地区分成几个较小的索引,就可以获得更好的性能。然后可以从查询中删除过滤子句。如果需要运行一个跨区域查询,可以将多个索引或通配符传递给 Elasticsearch。

  • 如果查询具有过滤字段并且其值不可枚举,请使用路由。可以通过使用过滤字段值作为路由键来将索引拆分成多个分片,然后删除过滤条件。关于 ElasticSearch 里的路由功能请参见这篇文章。

    例如,Elasticsearch 有数以百万计的订单,大多数查询需要通过买家 ID 查询订单。为每个买家创建索引是不可能的,所以不能通过买家 ID 将数据拆分成多个索引。一个合适的解决方案是使用路由将具有相同买家 ID 的所有订单放入同一个分片中,然后几乎所有的查询都可以在匹配路由键的分片内完成。

  • 如果查询具有日期范围过滤条件,则按日期分组数据。这适用于大多数日志记录或监控场景。可以以每天,每周或每月分组索引,然后可以在指定的日期范围内获得索引列表。Elasticsearch 只需要查询一个较小的数据集而不是整个数据集。此外,当数据过期时,很容易缩小 / 删除旧的索引。

  • 明确地设置映射。Elasticsearch 可以动态地创建映射,但可能并不适用于所有场景。例如,Elasticsearch 5.x 中默认的字符串字段映射是“关键字”和“文本”类型,这在很多场景下是没有必要的。

  • 如果文档使用用户定义的 ID 或路由索引,请避免不平衡分片。 Elasticsearch 采用随机 ID 生成器和哈希算法来确保文档均匀地分配给分片。当使用用户定义的 ID 或路由时,ID 或路由键可能不够随机,并且一些分片可能明显比其它分片更大。在这种情况下,在这个分片上的读 / 写操作将比在其它分片上慢得多。可以优化 ID / 路由键或使用 index.routing_partition_size (在 5.3 和更高版本中可用)。

  • 使分片均匀分布在节点上。如果一个节点比其它节点有更多的分片,则会比其它节点承担更多的负载,并很有可能成为整个系统的瓶颈。

调优索引性能

用于索引诸如日志和监控之类的重场景,索引性能是关键指标。这里有一些建议:

  • 使用批量请求

  • 使用多个线程 / 工作来发送请求

  • 增加刷新间隔。每次刷新事件发生时,Elasticsearch 都会创建一个新的 Lucene 段,并在稍后进行合并。增加刷新间隔将降低创建 / 合并的成本。请注意,只有在刷新事件发生后才能进行文件搜索。

性能和刷新间隔之间的关系

从上图可以看出,随着刷新间隔的增大,吞吐量增加,响应时间变快。可以使用下面的请求来检查有多少段以及刷新和合并花费了多少时间。

Index/_stats?filter_path= indices..refresh,indices..segments,indices.**.merges

  • 减少副本数量。Elasticsearch 需要为每个索引请求将文档写入主要和所有副本分片。显然,一个大的副本数会减慢索引速度,但另一方面,增加副本数量将提高搜索性能。这个话题将在本文后面讨论。副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复;二是提高 Elasticsearch 的查询效率,Elasticsearch 会自动对搜索请求进行负载均衡

    性能和副本数量之间的关系

从上面的图中,可以看到随着副本数量的增加,吞吐量下降,响应时间也变慢。

  • 如果可能,使用自动生成的 ID。 Elasticsearch 自动生成的 ID 可以确保是唯一的,以避免版本查询。如果客户真的需要使用自定义的 ID,建议选择一个对 Lucene 友好的 ID,比如零填充顺序 ID,UUID-1 或者 Nano time。这些 ID 具有一致的顺序模式,压缩良好。相比之下,像 UUID-4 这样的 ID 本质上仍旧是随机的,它提供了较差的压缩比,并降低了 Lucene 的速度。

调优搜索性能

使用 Elasticsearch 的主要原因是其支持通过数据进行搜索。用户应该能够快速地找到所需要查找的信息。搜索性能取决于很多因素:

  • 如果可能的话,使用过滤语境而不是查询语境。一个查询子句用于回答“这个文档如何与查询子句匹配?” ,过滤子句用于回答“这个文档是否匹配这个过滤子句?”。Elasticsearch 只需要回答“是”或“否”。它不需要计算过滤子句的相关性得分,并且可以高速缓存过滤结果。有关详细信息,请参阅查询和过滤语境。

比较查询和过滤

  • 增加刷新间隔。正如在调优索引性能部分所提到的,Elasticsearch 每次刷新时都会创建一个新的段。增加刷新间隔将有助于减少段数并降低搜索的 IO 成本。而且一旦发生刷新并且数据改变,缓存将无效。增加刷新间隔可以使 Elasticsearch 更高效地利用缓存。

  • 增加副本数量。Elasticsearch 可以在主分片或副本分片上执行搜索。拥有的副本越多,搜索中涉及的节点就越多。

性能和副本数量之间的关系

从上图可以看出,搜索吞吐量几乎与副本数量成线性关系。注意在这个测试中,测试集群有足够的数据节点来确保每个分片都有一个独占节点,如果这个条件不能满足,搜索吞吐量就不会那么好。

  • 尝试不同的分片数量。“应该为索引设置多少分片?” 这可能是最常见的问题。不幸的是,所有场景都没有标准的数字,这完全取决于当时的实际情况。

太小的分片数量会使搜索无法扩展。例如,如果分片数量设置为 1,则索引中的所有文档都将存储在一个分片中。对于每个搜索,只能涉及一个节点。如果有很多文件,那是很耗费时间的。另一方面,创建索引的分片太多也会对性能造成危害,因为 Elasticsearch 需要在所有分片上运行查询,除非在请求中指定了路由键,然后将所有返回的结果一起取出并合并。

根据经验来说,如果索引小于 1G,可以将分片数设置为 1。对于大多数情况,可以将分片数保留为默认值 5,但是如果分片大小超过 30GB,应该增加分片数量将索引分成更多的分片。创建索引后,分片数量不能更改,但是可以创建新的索引并使用 reindex API 转移数据。

在这里测试了一个拥有 1 亿个文档,大约 150GB 的索引,使用了 100 个线程发送搜索请求。

性能和分片数量之间的关系

从上图中可以看出,优化后的分片数量为 11 个。开始的时候,搜索吞吐量增加(响应时间减少),但随着分片数量的增加,搜索吞吐量减少(响应时间增加)。

请注意,在这个测试中,就像在副本数量测试中一样,每个分片都有一个独占节点。如果这个条件不能满足,搜索吞吐量就不会像上图所示那样好。

在这种情况下,建议尝试一个小于优化值的分片数,因为如果使用大分片数,并且使每个分片都有一个独占数据节点,那么就需要很多个节点。

  • 节点查询缓存。 节点查询缓存只缓存正在过滤语境中使用的查询。与查询子句不同,过滤子句是“是”或“否”的问题。Elasticsearch 使用一个位设置机制来缓存过滤结果,以便后面的查询使用相同的过滤条件进行加速。请注意,只有保存超过 10,000 个文档的分段(或文档总数的 3%,以较大者为准)才能启用节点查询缓存。有关缓存的更多详细信息,请参阅关于缓存。

可以使用下面的请求来检验一个节点查询缓存是否有效。

GET index_name/_stats?filter_path=indices.**.query_cache

{

“indices”: {

'index_name': { 'primaries': { 'query_cache': { 'memory_size_in_bytes': 46004616, 'total_count': 1588886, 'hit_count': 515001, 'miss_count': 1073885, 'cache_size': 630, 'cache_count': 630, 'evictions': 0 } }, 'total': { 'query_cache': { 'memory_size_in_bytes': 46004616, 'total_count': 1588886, 'hit_count': 515001, 'miss_count': 1073885, 'cache_size': 630, 'cache_count': 630, 'evictions': 0 } }}

}

}

分片查询缓存。如果大多数查询是聚合查询,应该看看分片查询缓存,它可以缓存聚合结果,以便 Elasticsearch 直接以低成本提供请求。有几件事情需要注意:

  • o 设置“size”:0。分片查询缓存只缓存聚合结果和建议。它不会缓存操作过程,因此如果将大小设置为非零,则无法从缓存中获益。

    o 有效负载 JSON 必须相同。分片查询缓存使用 JSON 主体作为缓存键,因此需要确保 JSON 主体不会更改,并确保 JSON 主体中的键具有相同的顺序。

    o Round 日期时间。不要直接在查询中使用像 Date.now 这样的变量,Round 它。否则,每个请求都会有不同的有效负载主体,从而导致缓存始终无效。建议 Round 日期时间为小时或天,以便更有效地利用缓存。

可以使用下面的请求来检验分片查询缓存是否有效果。

GET index_name/_stats?filter_path=indices.**.request_cache

{

“indices”: {

'index_name': { 'primaries': { 'request_cache': { 'memory_size_in_bytes': 0, 'evictions': 0, 'hit_count': 541, 'miss_count': 514098 } }, 'total': { 'request_cache': { 'memory_size_in_bytes': 0, 'evictions': 0, 'hit_count': 982, 'miss_count': 947321 } }}

}

}

  • 仅检索必要的字段。如果文档很大,并且只需要几个字段,请使用 stored_fields 检索所需要的字段而不是所有字段。

  • 避免搜索停用词。诸如“a”和“the”这样的停用词可能导致查询命中结果计数爆炸。设想有一百万个文件,搜索“fox”可能会返回几十个结果,但搜索“the fox”可能会返回索引中的所有文件,因为“the”出现在几乎所有的文件中。Elasticsearch 需要对所有命中的结果进行评分和排序,导致像“the fox”这样的查询减慢整个系统。可以使用停止标记过滤来删除停用词,或使用“和”运算符将查询从“the fox”更改为“the AND fox”,以获得更精确的结果。

如果某些词在索引中经常使用,但不在默认停用词列表中,则可以使用截止频率来动态处理它们。

  • 如果不关心文档返回的顺序,则按 _doc 排序。Elasticsearch 使用“_score”字段按默认分数排序。如果不关心顺序,可以使用“sort”:“_doc”让 Elasticsearch 按索引顺序返回。

  • 避免使用脚本查询来计算不固定的匹配。在索引时存储计算的字段。例如,有一个包含大量用户信息的索引,需要查询以“1234”开头的所有用户。或许想运行一个脚本查询,如“source”:“doc [‘num’].value.startsWith(’1234’)。” 这个查询是非常耗费资源的,并且减慢整个系统。索引时考虑添加一个名为“num_prefix”的字段,然后只需要查询“name_prefix”:“1234”。

  • 避免通配符查询

运行性能测试

对于每一次改变,都需要运行性能测试来验证变更是否适用。因为 Elasticsearch 是一个 restful 服务(基于 RESTful web 接口),所以可以使用诸如 Rally,Apache Jmeter 和 Gatling 等工具来运行性能测试。因为 Pronto 团队需要在每种类型的机器和 Elasticsearch 版本上运行大量的基准测试,而且需要在许多 Elasticsearch 集群上运行 Elasticsearch 配置参数组合的性能测试,所以这些工具并不能满足需求。

Pronto 团队构建了基于 Gatling 的在线性能分析服务 ,帮助客户和我们运行性能测试并进行回归。该服务提供的功能使我们能够:

  1. 轻松添加 / 编辑测试。用户可以根据自己的输入查询或文档结构生成测试,而无需具有 Gatling 或 Scala 知识。

  2. 按顺序运行多个测试,无需人工干预。它可以检查状态并在每次测试之前 / 之后更改 Elasticsearch 设置。

  3. 帮助用户比较和分析测试结果分析。测试期间的测试结果和集群统计信息将保留下来,并可以通过预定义的 Kibana 可视化进行分析。

  4. 从命令行或 Web UI 运行测试。Rest API 还提供了与其它系统的集成功能。

下图是架构

性能测试服务架构

用户可以查看每个测试的 Gatling 报告,并查看 Kibana 预定义的可视化图像,以便进一步分析和比较,如下图所示。

Gatling 报告

Gatling 报告

总结

本文概述了索引 / 分片 / 副本设计以及在设计 Elasticsearch 集群时应该考虑的一些其它配置,以满足摄取和搜索性能的高期望。它还说明了 Pronto 团队如何在战略上帮助客户进行初始规模调整,索引设计和调优以及性能测试。截至今天,Pronto 团队已经帮助包括订单管理系统(OMS)和搜索引擎优化(SEO)在内的众多客户实现了苛刻的性能目标,从而为 eBay 的关键业务做出了贡献。

Elasticsearch 的性能取决于很多因素,包括文档结构,文档大小,索引设置 / 映射,请求率,数据集的大小,查询命中计数等等。针对一种情况的性能优化推荐不一定适用于另一种情况。彻底地测试性能,收集遥测数据,根据工作负载调整配置以及优化以满足性能要求非常重要。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多