从 10 分钟到 1 秒:ES 深度分页任意跳页的三轮优化实战
作者vivo 互联网服务器团队- Chen Yifan本文介绍了一种基于 search_after Redis 多级锚点缓存的 Elasticsearch 深度分页跳页方案。针对 Elasticsearch 原生不支持随机跳页的限制通过三轮优化——分段预热缓存、最近锚点定位 Elasticsearch 查询提速禁用 _source/关闭 track_total_hits、大区间预热 小分页精细锚点——将 50 万数据量下的任意跳页响应时间从 10 分钟级优化至 1 秒以内。1 分钟看图掌握核心要点一、背景悟空活动系统目前支撑了一系列大型的摄影比赛活动在赛事评审环节管理后台需要对数十万级参赛作品进行集中展示、筛选与评审尤其在筛选环节涉及到对作品进行多维度条件筛选评审等级、作者信息、拍摄单元等。该业务场景本质上是一个“高数据量 多维组合筛选 深度分页”的后台检索系统。后台页面列表查询最初通过 MySQL 多表关联查询实现。随着赛事规模逐年扩大参赛作品量级持续攀升列表查询需要关联多张业务表并进行复杂条件筛选。在数据规模和并发请求不断增长的情况下MySQL 查询逐渐暴露出以下问题多表关联查询复杂SQL 执行成本较高数据量持续增长查询耗时明显上升高并发场景下数据库压力较大影响系统整体稳定性。为降低数据库查询压力并提升检索性能我们对系统进行了重构优化将原有的 MySQL 列表查询逻辑迁移至 Elasticsearch利用 Elasticsearch 在大规模数据检索与多维条件组合过滤方面的优势来承载后台的页面查询能力。在实际业务场景中用户可能会在评审结束以及评审过程中进行随机跳页查询来查看评审数据以及作品信息。但是 Elasticsearch 自身目前的查询能力只提供了用于顺序翻找的 scroll API 以及 search after无法直接跳转至任意页。为了不影响用户的使用体验因此进行 Elasticsearch 深度分页的优化。二、基础方案选择目前 Elasticsearch 所提供的深度查询的方案主要有三种fromsize、scroll API 以及 search after。对于用户点击深度分页的场景Elasticsearch 官方更推荐使用 search after。而 scroll API 则更适合应用于后台大规模数据扫描的应用场景来对整批索引数据进行稳定地的遍历查询。为了直观对比三种方案的流程和优劣我们绘制了以下的流程图。如图所示方案一有着最大数量的限制方案二会占用大量的 Elasticsearch 内存给集群造成压力。最终我们选择方案三相对于方案一该方案不需要计算大量的偏移数据而相比于方案二该方案采用无状态查询方式不需要在 Elasticsearch 节点上维护额外的查询上下文更适合用户的在线查询场景。三、跳页解决方案演进3.1 阶段一基础方案在确定使用 search_after 作为深度分页的基础查询方式后我们首先实现了一套基于 缓存 search_after 的分页方案用于在一定程度上支持用户的深度分页查询。由于 search_after 只能基于上一页的排序位置进行顺序翻页而无法直接跳转至任意分页位置因此需要通过额外的缓存机制记录部分分页位置对应的 search_after 值从而减少顺序查询的次数。在该阶段的方案中我们设计了一种分段预热的缓存策略。异步预热当用户首次进入列表页面进行查询时系统会启动一个异步线程在后台按照 1000 条数据为一个步长对查询结果进行预热处理。多粒度缓存在每个步长位置系统会继续按照当前系统预设的分页大小10、20、50、100分别记录当前查询结果最后一条数据对应的 sortValues。Redis 存储将该数据的位置和对应的 sortValues 缓存到 Redis 的 hash 结构中。当用户访问的分页位置超过 Elasticsearch 默认的深度分页限制10000之后系统会根据用户当前的分页大小计算出当前页面的开始位置from并从 10000 对应的缓存锚点位置重新开始执行 search_after 查询并按照用户当前查询的分页大小顺序向后推进。直到命中目标位置from。之后再进行一次 search_after 查询查询出当前页面的全部数据。同时在推进过程中系统会继续补充新的 search_after 锚点缓存以逐步完善缓存中的分页位置数据。该方案在命中缓存时会根据当前缓存的 search_after 值再进行一次 search_after 查出指定页面的相对数据查询效率相对较快在 1 秒钟内能够返回结果。然而该方案在实际运行过程中仍然存在一定局限性当用户第一次访问某个较深的分页位置时由于缓存尚未完全构建系统仍然需要执行大量顺序 search_after 查询锚点缓存依赖于预热过程若用户访问的分页区间分布较为随机缓存命中率可能较低在极端情况下用户直接跳转到较深的分页位置时查询延迟仍然较高。如下图所示对于八十万级的数据量级用户第一次进去直接跳转最后一页需要十分钟左右的预热时间。3.2 阶段二性能优化3.2.1 优化点一引入最近锚点定位在阶段一的方案中系统通过缓存部分 search_after 游标来辅助深度分页查询在一定程度上缓解了顺序翻页带来的性能问题。然而该方案仍然存在一个明显的不足当用户直接跳转到较深的分页位置时如果目标页附近没有可用的缓存游标系统仍然需要执行较多次顺序 search_after 查询导致查询耗时较长。我们认为既然已经在用户第一次访问时做了缓存预热的操作那在后续的操作中我们也想尽可能地最大程度地利用已经缓存的锚点提高查询效率。那么很容易就可以想到在用户每次的查询过程中找到距离当前数据最近的一次 searchAfter 记录并根据该记录动态计算出和目标页面所相差的数据条数再进行一次 searchAfter 操作来跳到目标页数。为了实现该想法我们引入了 Redis ZSet 作为偏移量索引。不再生硬地从头扫描而是在数据链路上钉上“锚点”。1.动态锚点预热步长机制用户首次进入时系统以 1000 条为步长自动向后预热。缓存结构将查询位置存为 score将 Elasticsearch 的 sortValues 存为 value维护在 Redis ZSet 中。2. “跳板式”精准命中坐标换算根据 pageNum * pageSize 计算目标位置 from。快速定位利用 ZSet 的 reverseRangeBy-Score 命令毫秒级锁定小于且最接近目标位置的那个“锚点”。区间收缩将原本几十万量级的扫描瞬间收缩到 1000 条以内的微小区间。3. 远程跳跃保护设置最大缓存间隔。若最近锚点距离目标过远系统先进行大区间的预热构建快速逼近目标区间后再切换到小步长精确命中。详细流程如下图所示效果同等数据量下跳到最后一页的时间从 10 分钟降到约 6 分钟预热完成后任意跳页约 3 秒。有进步但还不够。3.2.2 优化点二优化 Elasticsearch 查询效率目前来说最耗时的部分还是在于缓存的预热和大幅度跳页之后根据就近锚点深度查找的过程中的 Elasticsearch 查找操作对于五十万级别的数据量级按照 1000 的步长进行搜索构造需要在预热过程中进行 500 次查询操作如果每次的查询时间耗时较长总和加起来耗时会非常庞大。而预热阶段我们根本不需要完整的文档数据只需要 sortValues。于是我们做了两个关键调整1禁用_source 字段Elasticsearch 查询默认会返回完整的 _source 文档内容这涉及大量磁盘 I/O 和反序列化开销。在预热阶段我们只需要排序值完全可以通过_source: false 跳过文档内容的读取。2关闭 track_total_hitsElasticsearch 查询默认会计算匹配文档的总数total_hits这需要扫描所有匹配文档。在 search_after 预热场景中我们并不关心总数关闭后可以省去这部分开销。下图是未禁用相关参数的耗时而下图则是在禁用_source 以及关闭掉 track_total_hits 之后的同等耗时每次查询大概在 500ms 左右相较于默认查询耗时有了显著减少。此外在查询开销降低后我们进一步调整了分页预热的步长策略。相比于原先较小的分页步长在减少返回数据量的前提下可以适当增大每次查询获取的数据量从而减少与 Elasticsearch 的交互次数提高整体预热效率。经过多次测试与调优后我们将预热步长调整为 5000 条数据。在该配置下系统在用户首次跳转至最后一页时整体查询时间已经能够稳定控制在一分钟以内相比初始方案有了明显提升。但在实际使用过程中如果用户直接跳转至任意较深的分页位置系统仍然需要通过已有锚点执行少量 search_after 顺序推进查询因此整体查询耗时仍可能达到 1 秒以上。因此当前方案在随机跳页场景下仍然存在进一步优化的空间。3.2.3 优化点三分割预热区间第二版解决了预热速度的问题但仍然有一个痛点预热后跳页仍需约 1 秒。原因是锚点粒度仍然是 5000用户请求的页码大概率不会刚好落在锚点上还需要做一段距离的顺序推进。因此我们进一步思考如果我们能把锚点粒度做到和最小分页大小一致比如 10 条那用户跳到任何页都能直接命中缓存只需要一次 search_after 查询就能拿到结果。为了实现这个构想同时又不拖慢主预热链路我们设计了一套“大区间同步 小区间异步”的分层架构系统维持原有的逻辑以 5000 条数据为一个大区间进行预热查询确保主进度的覆盖。在获取到大区间数据后后台同步启动一个异步线程,以最小分页大小如 pageSize 10为基础单位,将 5000 条的大结果集切割成 500 个细小的分页区间并构建缓存。当用户发起跳页请求时会直接定位到该页码对应的细粒度锚点。取出对应的 search_after 值。最终的流程如下图所示通过这种“大区间预热 小分页锚点缓存”的策略系统在保证缓存覆盖范围的同时将随机跳页场景下的顺序查询次数降至最低从而有效提升了深度分页查询的响应速度并进一步提高了缓存命中率。在测试环境的实际测试中可以发现对于七十万级的数据量级预热完成后随机点击页面的 RT 也基本能够保证在 1s 以内。线上模拟的五十万级数据级别的响应时间也都在理想范围内四、目前问题在上述优化方案中为了减少深度分页时的顺序查询次数我们通过 Redis 对 search_after 锚点进行了缓存从而在用户跳页时能够快速定位到接近目标位置的数据区间。然而在实际运行过程中也暴露出了一个新的问题数据漂移具体表现为新数据写入后原来最后一页的锚点可能不再是真正的尾部跳到最后一页可能返回空页数据删除后某些锚点对应的位置可能出现数据重复或缺失。该问题产生的根本原因在于 Elasticsearch 索引数据与 Redis 中缓存的分页锚点之间并非强一致关系。在当前方案中分页锚点是在预热阶段通过查询 Elasticsearch 生成并缓存到 Redis 中的这些锚点本质上记录的是某一时刻 Elasticsearch 索引数据在特定排序条件下的位置。当上述情况发生时Elasticsearch 中的真实数据顺序可能已经发生变化而 Redis 中缓存的锚点仍然对应的是旧的数据位置。此时当系统使用 Redis 中的锚点作为 search_after 的起点继续执行查询时实际的分页起始位置就可能与用户期望的分页位置产生偏差从而出现数据漂移现象。应对策略该方案本质上更适用于数据相对稳定的查询场景——在我们的业务中深度分页的使用高峰主要在数据趋于稳定的后期阶段数据大量变化的概率较小对于可能出现的空页问题系统会清除缓存并基于最新的 Elasticsearch 数据重新预热如果你的场景中数据变更非常频繁可能需要考虑其他方案如基于时间戳或版本号的增量更新策略。五、思考回顾整个优化过程核心思路可以提炼为这次优化的过程本质上不只是一次 Elasticsearch 性能调优更是一次面对多目标约束时如何拆解问题、逐步逼近最优解的工程实践。希望这些经验对遇到类似问题的同学有所帮助。

相关新闻

百度网盘Mac版破解终极指南:3步解锁SVIP高速下载

百度网盘Mac版破解终极指南:3步解锁SVIP高速下载

百度网盘Mac版破解终极指南:3步解锁SVIP高速下载 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘下载速度慢而烦恼吗&#x…

2026/7/3 14:20:45阅读更多 →
STM32L4A6RG与25CSM04 EEPROM的高速SPI数据检索实现

STM32L4A6RG与25CSM04 EEPROM的高速SPI数据检索实现

1. 项目概述:基于25CSM04与STM32L4A6RG的高速数据检索系统在嵌入式系统中,快速精确地检索存储在外部存储器中的数据是一个常见但具有挑战性的需求。25CSM04作为一款4Mbit容量的SPI接口EEPROM,与STM32L4A6RG这款低功耗高性能MCU的结合&#xf…

2026/7/3 14:15:45阅读更多 →
单片机IWIP MQTT实验

单片机IWIP MQTT实验

单片机 :STM32F407 开发板:DMF407电机开发板 平台:keil V5.31HSE 为8MHZ HSI为16MHZ一、Aliyun主函数int main(void) {HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */…

2026/7/3 14:15:45阅读更多 →
三轴运动追踪方案:WSEN-ISDS与TM4C129XKCZAD硬件实现

三轴运动追踪方案:WSEN-ISDS与TM4C129XKCZAD硬件实现

1. 项目概述:三轴运动追踪的硬件选型与实现 在工业自动化和消费电子领域,精确测量物体在三维空间中的运动和姿态一直是个经典需求。这次我选用Wrth Elektronik的WSEN-ISDS三轴加速度计搭配TI的TM4C129XKCZAD微控制器,搭建了一套完整的空间运动…

2026/7/3 16:01:05阅读更多 →
WSEN-ISDS与PIC24FJ64GB004运动追踪开发指南

WSEN-ISDS与PIC24FJ64GB004运动追踪开发指南

1. 项目背景与硬件选型解析 在运动追踪领域,同时捕捉角运动和线性运动的需求正变得越来越普遍。WSEN-ISDS(型号2536030320001)这款三轴MEMS惯性传感器与PIC24FJ64GB004微控制器的组合,为开发者提供了一个高性价比的解决方案。 WS…

2026/7/3 16:01:05阅读更多 →
如何快速搭建国标28181视频监控平台:5分钟掌握完整部署流程

如何快速搭建国标28181视频监控平台:5分钟掌握完整部署流程

如何快速搭建国标28181视频监控平台:5分钟掌握完整部署流程 【免费下载链接】wvp-GB28181-pro 基于GB28181-2016、部标808、部标1078标准实现的开箱即用的网络视频平台。自带管理页面,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。…

2026/7/3 16:01:05阅读更多 →
AsrTools终极指南:3步实现语音转文字的高效革命

AsrTools终极指南:3步实现语音转文字的高效革命

AsrTools终极指南:3步实现语音转文字的高效革命 【免费下载链接】AsrTools ✨ AsrTools: Smart Voice-to-Text Tool | Efficient Batch Processing | User-Friendly Interface | No GPU Required | Supports SRT/TXT Output | Turn your audio into accurate text i…

2026/7/3 16:01:05阅读更多 →
IS31FL3731 LED驱动芯片与STM32F405ZG集成方案详解

IS31FL3731 LED驱动芯片与STM32F405ZG集成方案详解

1. IS31FL3731 LED驱动芯片的核心特性解析 IS31FL3731是一款专为LED矩阵显示设计的I2C接口驱动芯片,其核心价值在于实现了144个LED(16x9阵列)的独立PWM控制。这款芯片采用Charlieplexing技术,仅需少量IO引脚即可驱动大量LED&#…

2026/7/3 16:01:05阅读更多 →
百考通AI用方法论思维,带你跨过科研第一道坎

百考通AI用方法论思维,带你跨过科研第一道坎

开题报告是学术研究的“第一张蓝图”,它不仅决定你的选题能否获批,更直接影响后续论文的逻辑框架与研究深度。然而,许多学生在撰写时常常陷入困境:问题意识模糊、文献堆砌无主线、研究方法空泛、结构松散不规范……这些问题不仅耗…

2026/7/3 15:56:05阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/3 14:18:39阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/3 14:38:35阅读更多 →
LV3296与PIC18F45K22的UART通信与USB扩展方案

LV3296与PIC18F45K22的UART通信与USB扩展方案

1. LV3296与PIC18F45K22的硬件搭档解析在嵌入式数据采集系统中,LV3296条形码扫描模块与PIC18F45K22微控制器的组合堪称经典搭配。LV3296作为一款工业级条码扫描头,其核心是一颗高性能CMOS图像传感器,配合专用解码芯片,能自动识别包…

2026/7/3 0:03:41阅读更多 →
AI初创生存指南:6个月完成可信度验证闭环

AI初创生存指南:6个月完成可信度验证闭环

1. 这不是“逆袭指南”,而是一份AI初创公司真实生存手记“How To Beat Odds As an AI Startup?”——这个标题乍看像一句热血口号,但在我带过7个从0到1的AI产品团队、亲手踩过融资失败、技术债崩盘、客户POC卡在最后一公里等23类典型坑之后,…

2026/7/3 0:03:41阅读更多 →
多模态+推理链+RAG 2.0+智能体:工业级AI系统落地四支柱

多模态+推理链+RAG 2.0+智能体:工业级AI系统落地四支柱

1. 这不是又一篇“AI趋势速览”,而是一份实操者手记:当多模态、推理链、检索增强与智能体协作真正撞进工程现场“LAI #73”这个编号本身就像一个暗号——它不属于某家大厂的白皮书,也不是学术会议的议程表,而是长期泡在模型训练集…

2026/7/3 0:03:41阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/3 1:12:46阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/3 1:36:36阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/3 2:08:15阅读更多 →