资讯动态

ES深度分页性能陷阱:原理剖析与压测实战指南

发布时间:2026/10/18 4:58:38 来源:尧图企业网站定制
做性能测试这么多年我踩过不少搜索类的“深坑”其中最典型的一个就是Elasticsearch的深度分页。很多测试同学测分页接口测到第五页、第十页觉得没问题就收工了结果上线后被用户投诉说“翻到后面越来越慢”甚至把集群CPU打满。其实症结不在代码逻辑而在ES处理深度分页的机制本身。这篇内容我想从测试从业者的视角把ES深度分页的性能陷阱讲透它到底慢在哪、为什么慢、怎么用压测手段把问题量化出来以及测试结论怎么传达给开发同学才最有说服力。EE、日志平台、商品搜索这类场景只要用了ES深度分页就是一个绕不开的测试重点。适合刚接触ES性能测试的同学也适合正在排查线上分页慢问题的测试老手按这篇文章的思路可以直接落地你的测试方案。1. 先把深度分页的问题本质搞清楚1.1 ES分页的“翻车原理”你以为只查一页其实查了全部ES最常见的分页写法是from size比如{ from: 10000, size: 20 }这行代码看起来很简单——你要第10001条到第10020条数据。但ES内部执行的动作和你想象的完全不一样协调节点会把请求转发到每个分片每个分片都会先取出 from size 条即每个分片都要取出10020条数据到一个优先队列里然后返回给协调节点协调节点再合并所有分片的结果排序后截取第10001到10020条。打个比方你去图书馆找第1001页到第1020页的内容正常情况下只需要翻到那个位置就行了。但ES的做法是每个分区管理员都先把各自的10020份资料全部搬出来堆在面前再统一汇总到你这里你最后才拿走20份剩下的一万份全白干了。分片越多、from越大、数据量越大这种徒劳搬运就越夸张。我在模拟项目X中测试过单索引5个主分片每分片约200万文档from10000时协调节点单次请求需要合并排序约50000条文档元数据。这个开销不是线性的越往后越离谱。官方文档默认限制index.max_result_window为10000其实就是在说别指望我把前面所有数据都给翻出来。1.2 测试人员为什么要关心这个问题很多测试同学习惯用功能测试的思路来看分页只验证“第N页数据正确性”。但深分页的性能影响只有在压力下才会暴露它体现为几个容易被忽略的测试盲区一是响应时间虚高。平时单用户操作from5000可能也就二三百毫秒测试同学会觉得“能接受”。但压测到50并发时协调节点内存和CPU瞬时飙升响应时间可能直接拖到秒级。二是偶发性超时。ES的深分页会触发大量GC尤其是很多测试框架和业务代码大量使用大对象分配导致JVM Old区回收频繁。GC一停顿请求就超时。这类问题在低并发下根本不会复现。三是集群整体拖垮。深分页不只是影响当前接口它会占用大量堆内存和CPU做排序合并同一个集群上其他查询接口的P95可能也跟着恶化。如果测试只盯着分页接口本身忽略全局影响很容易漏掉这种“连带伤害”。1.3 测试人员必须关注的关键指标清单深分页性能测试不能只看接口响应时间需要一套多维指标。我自己的经验是至少盯住下面这五类指标类别具体项为什么看它接口RT平均RT、P95、P99反映用户实际体验深分页的延迟通常伴随严重长尾吞吐量QPS、TPS低并发下可能看不出问题高并发下吞吐衰减非常明显节点CPU协调节点CPU、数据节点CPU深分页主要耗CPU排序合并和网络IOJVM内存Young GC频率、Old GC次数、堆使用率GC频繁是深分页的“标配症状”集群网络协调节点到数据节点的网络流量深分页会把大量数据拉到协调节点流量陡增我测试时习惯在压测过程中同时用指标采集工具和ES的_cat/nodes接口做对照每隔5秒采一次。一旦发现某个节点的堆内存曲线持续爬坡不回落基本就可以判定分页请求在堆里“堆积”了。2. 压测方案设计不准备好这些测了也白测2.1 测试数据怎么准备才贴近真实深分页压测最怕数据失真。很多测试库只有几万条数据怎么压都很快结论“性能没问题”实际上毫无参考价值。我在实际项目中有一条经验公式测试数据量至少是业务期望产线数据量的十分之一以上。比如某公司在ES里存的是用户行为日志产线预估半年后单索引会有2亿条数据那测试环境至少要造2000万条。否则排序、合并、GC那些开销根本压不出来。造数的时候要注意两点一是字段类型必须和产线保持一致尤其是keyword和text的分词方式会影响ES倒排索引和doc values的构建方式二是数据分布要尽量模拟真实场景比如“热门商品”集中在少部分词上别全部均匀分布否则排序成本测不准。我用Python脚本压测前现造数据写一个批量bulk接口每条doc包含时间戳、用户ID、操作类型、详情描述四个字段这样既能覆盖分页测试需求也能模拟后续可能做的搜索条件组合。2.2 压测工具选型与线程模型设置压测工具我比较常用的是两款简单场景用命令行压测工具复杂场景用可视化压测平台。命令行工具胜在轻量一个脚本就能跑出RT分布适合快速摸底。比如模拟20个并发、每个用户连续翻50页脚本核心大概这样# 每10轮递增并发观测不同压力下的P95 for concurrency in 10 20 50 80; do 压测命令 \ --concurrency $concurrency \ --duration 5m \ --query-file query.json done每个query.json里放一组不同深度的分页请求从浅到深依次打第1页from0第20页from380第100页from1980第300页from5980第500页from9980可视化平台适合做长时间稳定性验证配置线程数、递增步长、断言阈值都更直观。我个人建议先命令行摸清“拐点在哪”再用可视化平台跑一轮长稳确认性价比最高。2.3 压测过程中的“旁路观测”怎么做压测的同时一定要做旁路观测不然拿到一个RT数据根本解释不了。我自己的做法是起三个采集通道第一个通道盯ES节点指标。通过ES的监控API轮询节点CPU、堆内存、GC次数。重点看协调节点和负载最高的数据节点。第二个通道盯操作系统层。用系统监控命令看进程CPU、内存、句柄数、TCP连接数。有的深分页问题其实是连接数被打满反倒不是ES本身的毛病。第三个通道盯压测机本身。压测机如果出现CPU瓶颈测出来的RT就不准。我遇到过压测机单机跑高并发时本机CPU先到100%RT数据惨不忍睹结果排查半天发现是压测端瓶颈。所以一般会任务分配里预留一台机器专门跑监控不和压测机混用。3. 实测演练从浅分页到深分页的指标变化3.1 压测用例设计一个“翻页深度递增”的矩阵我用一组真实的模拟测试数据来展示完整的测试过程大家可以照着这个思路设计自己的用例。索引配置为5主分片、1副本单分片约200万文档总数据量约1000万测试机的配置和产线节点对齐。用例设计成一个二维矩阵横轴是并发数纵轴是分页深度。这样既能看出不同深度下的RT差异也能看出高并发和深分页叠加后的放大器效应。用例编号并发数from/size预计翻页位置P1100/20首页P210380/20第20页P3101980/20第100页P4105980/20第300页P5109980/20第500页P6509980/20第500页P71009980/20第500页之所以要同时变化并发和深度是因为我曾经遇到过只在单并发下测深分页RT看着还行一上50并发直接超时的案例。深分页和高并发是“互相放大”的关系分开测容易漏问题。3.2 典型结果解读拐点出现在第几页以我那次实测的数据为例单并发10个用户的场景下P1首页平均RT约15msP99约40msP2第20页平均RT约45msP99约110msP3第100页平均RT约180msP99约400msP4第300页平均RT约520msP99约1.2sP5第500页平均RT约1.35sP99约3.8s从这组数据能明显看到从第100页到第300页之间有一个“性能拐点”。第100页RT还在180ms到了第300页直接跳到520ms之后基本是线性恶化。再看并发叠加的效果。同样是from998010并发时P99约3.8s50并发时P99到了8.9s100并发时部分请求已经触发了客户端的5s超时甚至出现连接池耗尽。为什么会有拐点我用JVM监控和_cat/nodes对比后发现当from超过一定深度后协调节点堆内存中需要维护的优先队列条目数暴增Old GC开始频繁触发。GC时间一旦混入RT响应时间就不再是平滑上升而是跳跃式恶化。这就是拐点的来源。3.3 怎么判断测试结论是不是“真的有问题”光看RT数字还不够要判断这是ES机制导致的必然问题还是集群配置有问题可以做一个对照测试把同样的请求打到不同规模的测试环境上。如果两条环境的拐点位置明显不同说明和数据量、分片数高度相关基本可以归因于深分页机制如果两个环境都表现很差那可能是查询语句本身有高开销的sort或script。另外一个判断技巧是抓一条慢请求日志。把?search参数里的耗时详情拿出来查看took的构成特别是total_shards的合并耗时占比。深分页典型特征是“协调节点合并耗时占比随from增大而上升”如果你发现占比超过50%那基本实锤了。提示不要只看客户端RT一定要结合ES自身的took时间。客户端RT包括了网络开销和连接等待ES的took才是服务端真实处理耗时两者差值过大时要先排查网络和客户端线程配置。4. 实战过程中最容易踩的坑4.1 想当然地把“深分页”和“慢查询”划等号这是测试人员最容易犯的认知错误。ES中真正慢的查询通常是没有走倒排索引优势的查询、大范围聚合、或者带有高开销script的查询。深分页是另一种维度的问题——它的慢是“资源使用不经济”的慢。举个例子我用同样1000万数据量测过一个带过滤条件的查询过滤后结果集只有2000条就算from10000因为每个分片最多也就取2000条并不会“爆炸”。所以做测试判断的时候一定要结合查询条件筛选后的结果集大小来看深分页的影响别一刀切认为“from大就一定慢”。4.2 压测数据“太干净”导致结果虚好这是我最常提醒测试新人的坑。很多测试库的文档非常短字段少甚至很多空值。这种数据跑出来的排序开销远低于真实数据。举个例子按商品销量排序的分页查询真实产线的商品标题可能有几十个分词描述有几段长文本这些都会占用更多的内存和CPU参与排序和合并。但测试库如果字段几乎为空ES加载doc values的成本就大大降低压测数据自然“好看”。我的做法是在造数时和开发确认产线数据的字段长度分布特别关注大字段、高基数字段的比例再按近似分布造数。宁可多花一小时造数据也别省这一步。4.3 测试结论不会表达开发不买账很多测试同学测出了问题但汇报的时候只说“分页第500页很慢平均1.3秒”开发一问“哪里慢为什么慢是查询慢还是GC慢”就答不上来了。这类结论对推动问题修复毫无帮助。我的经验是测试结论一定要包含三个要素一是定位。明确是协调节点合并开销还是数据节点Fetch开销。可以通过看ES的took和观察协调节点CPU占比来区分。二是量化。给出不同深度下的RT递增曲线、GC频率变化、堆内存走势用数据说话。三是对比。给出替代方案比如search_after、scroll、或者限制最大翻页深度的实测对比数据让开发知道改了之后会有什么收益。5. 深分页替代方案的效果对比测试5.1 用search_after替代fromsize的实测对比在真实场景中用户逐页翻到底的需求其实很少。更多的需求是“在结果集里往下滚动加载更多”这种场景下search_after是非常好的替代方案——它不通过计算偏移量来翻页而是记住上一次结果的排序值直接从那个位置往下查。我用相同的数据集和并发条件测了一组对比方案第300页平均RT第500页平均RT协调节点CPUfrom5980/size20520ms1350ms78%search_after第300页32ms35ms22%可以看到search_after的RT几乎不随“翻页深度”增加而增加这是机制决定的它不需要每个分片都取一遍前面所有数据再合并。注意search_after要求排序字段保证唯一性否则会出现数据丢失或重复。测试时我用的是_id作为tiebreaker加上业务排序字段一起排序实测结果稳定。5.2 测试视角下的方案选型建议测过多轮之后我的整体判断是如果业务场景是后台管理类的列表最多翻十几页fromsize完全够用没必要过度设计。如果业务场景是用户连续滚动加载结果集比如搜索商品、翻看消息记录首选search_after但要验证排序字段的唯一性。如果场景是导出全量数据那应该用scroll或者更底层的批量拉取方案而不是scroll的上面一层再套分页。测试人员在给出建议时可以明确说明“哪种方案适配哪种场景”这样开发的接受度会高很多。不要只说“你有问题”要给可执行的对比证据。5.3 性能测试报告的呈现技巧压测报告别只贴一堆数字。我习惯做两张图一张是不同分页深度的RT曲线图直观显示拐点另一张是GC频率随from变化的柱状图说明性能和稳定性问题。研发看第一张图感知“用户体验”看第二张图理解“技术原因”两张图放一起问题就清楚了。另外报告里附上复现步骤和压测脚本开发自己也能快速验证。我发现这种“给脚本给图表给结论”的方式问题得到修复的周期最短。6. 一些额外想分享的测试心得6.1 测试环境尽量贴近产线的“数量级”我在前面反复强调数据量这里再多说两句。ES深分页的性能和分片内数据量强相关数据量翻10倍合并排序的开销可能翻几十倍。测试环境如果凑不齐和产线完全一致的数据量至少要把数据量级拉到“能触发临界效应”否则测出的拐点和产线完全对不上。实际操作过程中我甚至遇到过测试环境数据量只有产线1/50测出的RT看着挺好一上线立刻出问题的案例。从那以后我的测试方案里总会加一条压测前先确认测试环境文档数和产线文档数在同一个数量级。6.2 别忘了处理“缓存热”的影响深分页测试里有一个隐藏变量是ES的节点查询缓存query cache和文件系统缓存。同一个查询反复压测文件系统缓存会把热数据都加载到内存里导致后续测试RT异常偏低掩盖真实性能问题。我的做法是压测前先执行一轮“洗缓存”操作可以用_cache/clear接口清理查询缓存或者直接重启测试节点让文件系统缓存失效。另外压测时要记录是第几轮开始出现“虚低”的RT如果发现规律性下降多半是缓存生效了要适当打散查询条件再压。6.3 建议把“分页性能检查”纳入常规回归最后想说一点长期建议。把分页性能测试固化成日常回归的一部分尤其是涉及搜索、列表、日志查询类的项目。不需要每次全量压测只要在核心接口变更、数据量显著增长时跑一轮预设的“分页深度冒烟用例”就能第一时间发现性能退化。我在自己团队里就维护了这样一套轻量级分页压测脚本从造数到出报告全流程半小时内能跑完。每次ES集群节点配置变更、索引mapping调整都会顺手跑一轮。这套习惯帮我拦下了好几次潜在的线上事故真心建议大家都建一个。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑