news 2026/5/1 16:59:09

Elasticsearch Percolate Query使用优化案例-从2000到500ms

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Elasticsearch Percolate Query使用优化案例-从2000到500ms

原文地址:https://blog.fanscore.cn/a/67/

1. 前情提要

组内在某个项目中使用了ES较为冷门的Percolate Query能力,然后在数据累积到一定量级后遇到性能瓶颈,遂开始了漫漫的性能优化之路。优化过程中发现社区中针对Percolate Query优化的文章还是比较少见的,甚者全知全能的AI之神在我优化过程中都没有提供多少有建设性的指导意见,因此现将曲折的优化过程总结成本文分享给各位同学,希望对各位有帮助,也算给AI之神回馈一些训练数据了。

2. Percolate Query 简介

为了对齐认知这里还是先简单介绍下ES的Percolate Query能力,在Elasticsearch中,percolate query是一种特殊的查询类型,用于提前准备和匹配文档。它允许你将query存储在索引中,然后在新document到达时进行匹配,以查看哪些query与该document匹配。也就是说普通查询是以query查有哪些document匹配,而Percolate Query是以document查询哪些query匹配,两者正好相反。

2.1 使用场景

我们的使用场景是用户创建他们感兴趣的站内内容的查询,当新的内容发布或者更新时,使用Percolate Query查询到哪些用户对该内容感兴趣,然后分发给这批用户

2.2 实现步骤

  1. 创建查询索引

首先,我们创建一个索引来存储用户的查询。这个索引通常包含查询的结构和相关的元数据。

/* by 01130.hk - online tools website : 01130.hk/zh/pngcompression.html */ PUT /user_dsl { "mappings": { "properties": { "query": { "type": "percolator" }, "user_id": { "type": "keyword" } } } }
  1. 添加查询
    然后,将用户的查询添加到这个索引中。
/* by 01130.hk - online tools website : 01130.hk/zh/pngcompression.html */ PUT /user_dsl/_doc/1 { "query": { "bool": { "must": [{ "term": { "article_type": 3 } }, { "nested": { "path": "categorys", "query": { "terms": { "categorys.id": [1, 2, 3] } } } }, { "nested": { "path": "tags""query": { "terms": { "tags.id": [4, 5, 6] } } } }, { "nested": { "path": "brands", "query": { "term": { "brands.id": "1673" } } } }, { "bool": { "minimum_should_match": 1, "should": [{ "match": { "content.ik_smart": { "minimum_should_match": "100%", "query": "惠普笔记本电脑" } } }, { "match": { "title.ik_smart": { "minimum_should_match": "100%", "query": "惠普笔记本电脑" } } }, { "match": { "content.ik_smart": { "minimum_should_match": "100%", "query": "笔记本 惠普" } } }, { "match": { "title.ik_smart": { "minimum_should_match": "100%", "query": "笔记本 惠普" } } }] } }] } }, "user_id": "123456" }
  1. 使用percolate query

当新的内容发布或者更新时,使用percolate query来检查哪些用户的查询匹配这篇文章。

GET /user_dsl/_search { "size": "1000", "_source": { "includes": ["user_id"] }, "query": { "bool": { "must": [ { "percolate": { "field": "query", "document": { "article_type": "3", "brands": [{"id":1,"name":"DANISH CROWN/丹尼斯皇冠"}], "tags": [{"id":1,"name":"玩具"},{"id":2,"name":"宠物"}], "categorys": [{"id":1,"name":"宠物"},{"id":2,"name":"宠物玩具"}], "content": "作为猫主人,您一定希望您的猫咪每天都能快乐、健康地生活。猫咪天性好动,喜欢探索和玩耍,同时也需要舒适的环境来满足它们的日常需求。今天,我们向您推荐一款能够极大提升猫咪幸福感的产品——猫咪风车蹭痒玩具。这款玩具不仅能让您的猫咪玩得开心,还能满足它们的蹭痒需求,是每一位猫主人必备的神器。 一、什么是猫咪风车蹭痒玩具? 猫咪风车蹭痒玩具是一款专为猫咪设计的多功能玩具,结合了风车转动和蹭痒功能。它由高质量", "title": "什么是猫咪风车蹭痒玩具" } } } ] } } }

这个查询将返回所有匹配的query,包括用户ID,然后将这篇内容分发给这批用户即可

3. 问题与优化过程

随着用户创建的查询越来越多,我们发现站内内容分发越来越慢,并且在内容高频发布的时间段出现了大量积压,这严重影响了分发的实时性。通过trace平台我们发现分发就慢在ES percolate query上,如下图所示

  • /_search?scroll=1m查询(创建并得到ScrollID),这一步平均响应时间到达了2s多

  • /_search/scroll根据ScrollID查询继续往下迭代查询,这一步平均响应时间到了1s多

考虑到用户创建的查询很多,因此我们并不是通过一个_search查询直接拿到全部结果,而是通过scroll查询分批取结果

3.1 慢查询分析

找到一个慢查询后,在查询中增加"profile": true的选项后拿到了分析结果,如下所示

GET /user_dsl/_search { "profile": true, // 增加这个选项 "query": { "bool": { "must": [ { "percolate": { "field": "query", "document": { xxx } } } ] } } }

从结果中我们看到了这段失败的信息:

{ "_scroll_id": "xxxxxx", "took": 4336, "timed_out": false, "_shards": { "total": 6, "successful": 2, "skipped": 0, "failed": 4, "failures": [ { "shard": 0, "index": "user_dsl", "node": "xxxxxx", "reason": { "type": "too_many_scroll_contexts_exception", "reason": "Trying to create too many scroll contexts. Must be less than or equal to: [500]. This limit can be set by changing the [search.max_open_scroll_context] setting." } } ] }, ... }

在6个分片其中的4个分片上竟然失败了,失败的原因是无法创建更多的scroll contexts,查询项目代码后发现是最初的开发同学执行scroll查询后没有及时清理scroll context,导致了scroll context泄露,修复后我们发现响应时间明显降低:

  • 同一时间段/_search?scroll=1m查询,平均响应时间降低了700ms
  • 同一时间段/_search/scroll查询,平均响应时间也降低了1s

调用curl -X DELETE "http://{esHost}/_search/scroll" -d "{"scroll_id": "{scrollID}"}"来回收创建的scroll context

完成这步后虽然响应时间得到了提升,但只是修复了bug,从profile结果中也无法看出有什么其他可以提升的地方,因此下一步准备从percolate query 原理入手看下还能如何提升。

3.2 percolate query 原理

从ES的官方文档 Percolate query | Reference 可以分析出percolate query大概的底层运行原理

当写入document到已配置了percolator字段类型的索引时,document的查询部分会被解析,从中提取数据然后建立索引。查询时首先利用索引粗筛出一批候选集,然后在内存中再精准的判断是否确实符合筛选条件。

可以发现这个过程候选集越大,需要在内存中比对的数据量就越大,越耗CPU,因此优化的核心思想就是减小候选集。

3.3 优化方案

根据上面的原理,我快速整理出了以下优化方案

3.3.1 提取必要条件【短期方案】

从生产环境用户query索引中可以看到大量query是符合以下模版的简单查询

SELECT * FROM article WHERE article_type = 1 AND category_id IN (1, 2) AND brand_id = 3 AND content LIKE '%回音壁%' AND tag_id = 6

这里为方便阅读使用sql表示

我们接着做如下分析,假设现有一篇文章数据为

{ "article_type": 3, "article_id": 1, "category_ids": [1,2,3], ... }

有两个查询为

  • sql1
SELECT * FROM article WHERE category_id IN (4, 5, 6) AND ......
  • sql 2
SELECT * FROM article WHERE category_id IN (1, 2, 3) AND ......

对这篇文章进行percolate时因为分类不符合所以肯定无法匹配到sql1的因此无需对sql1进行匹配。

利用如上原理,我们为每个查询提取出它的必要条件包括文章类型、分类、品牌,然后增加前置filter即可降低percolate query查询的规模。

以上方案上线后响应时间明显降低

  • /_search?scroll=1m查询,平均响应时间降低了非常多
  • /_search/scroll查询,平均响应时间也降低了非常多

不加入其他条件比如标签的原因是加入这种条件的查询比较少,过滤率比较低

3.3.2 批量查询【长期方案】

ES 文档 Percolating multiple documents 提到了percolate query支持批量查询。可以预见的是,如果多篇文章比较类似,那么它们的候选集大概率重合度会非常高,假设文章A候选集为[1,2,3],文章B候选集[1,2,6],如果分两次查询的话,内存中需要比对3 + 3 = 6次,而如果合并批量查询的话只需要比对[1,2,3,6] = 4次,这样就降低了CPU使用率,同时也能缩短整体的耗时

这个方案对于我们来说代码层面改动比较大,准备作为长期方案后续优化,因此暂时没有优化效果数据

3.3.3 冷热分离【长期方案】

核心思想是对于长时间不活跃的用户我们没必要给Ta分发,因此可以给每个query增加一个最后在线时间的字段,执行percolate query的filter中前置过滤出最近N天活跃的数据,从而降低percolate query查询的规模。而且这个N可以做成动态的,根据负载动态变化,当负载大积压数据多时可以适当缩小N从而能获得更快的消费分发速度,可以在突增流量到来时保护我们的系统。

由于这个方案对业务有影响,因此可以在评估影响范围后作为一个长期方案来做。

4. 总结

性能优化的套路大体上是相通的,第一步一定要先分析问题的出现原因,不能想当然,也不能靠猜测,无法找到原因的情况下再根据经验做一些推测然后做小成本的实验来验证。另外就是要充分利用好各种profile工具,在本次优化过程中ES profile就成功帮我们找到了一个代码上的bug。

5. 参考资料

  • Percolate query | Reference
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 18:20:50

LosslessSwitcher终极指南:让Mac音频采样率自动匹配无损音乐

LosslessSwitcher终极指南:让Mac音频采样率自动匹配无损音乐 【免费下载链接】LosslessSwitcher Automated Apple Music Lossless Sample Rate Switching for Audio Devices on Macs. 项目地址: https://gitcode.com/gh_mirrors/lo/LosslessSwitcher 您是否曾…

作者头像 李华
网站建设 2026/5/1 11:38:00

音频桥接神器:快速部署多设备无线音频系统完整指南

音频桥接神器:快速部署多设备无线音频系统完整指南 【免费下载链接】AirConnect Use AirPlay to stream to UPnP/Sonos & Chromecast devices 项目地址: https://gitcode.com/gh_mirrors/ai/AirConnect 你是否曾经梦想过将家中的普通音响设备瞬间升级为支…

作者头像 李华
网站建设 2026/4/30 12:02:43

AI开发者必备:轻量级Conda环境如何提升开发效率?

AI开发者必备:轻量级Conda环境如何提升开发效率? 在现代AI研发的日常中,你是否曾遇到过这样的场景:刚为一个项目安装完PyTorch 2.0,结果另一个依赖旧版本的实验突然跑不起来了?或者,在复现一篇论…

作者头像 李华
网站建设 2026/5/1 10:58:10

ComfyUI-ReActor:快速实现高质量面部交换的终极指南

在当今AI图像处理技术飞速发展的时代,ComfyUI-ReActor为普通用户提供了一个简单易用的面部交换解决方案。这个基于ComfyUI平台的扩展节点让任何人都能在几分钟内实现专业级的面部交换效果。无论你是创意工作者、内容创作者,还是对AI技术感兴趣的爱好者&a…

作者头像 李华
网站建设 2026/4/24 20:24:58

阅读3.0书源配置终极指南:告别书荒的完整解决方案

阅读3.0书源配置终极指南:告别书荒的完整解决方案 【免费下载链接】最新1629个精品书源.json阅读3.0 最新1629个精品书源.json阅读3.0 项目地址: https://gitcode.com/open-source-toolkit/d4322 还在为阅读3.0中找不到心仪书籍而苦恼吗?&#x1…

作者头像 李华
网站建设 2026/4/27 20:30:29

信安毕业设计2026选题推荐

0 选题推荐 - 网络与信息安全篇 毕业设计是大家学习生涯的最重要的里程碑,它不仅是对四年所学知识的综合运用,更是展示个人技术能力和创新思维的重要过程。选择一个合适的毕业设计题目至关重要,它应该既能体现你的专业能力,又能满…

作者头像 李华