Elasticsearch 9.5 列存引擎的性能实测与原理解读
假设你是一位数据分析师,想回答一个看似简单的问题:"地球上每个国家的人口总数分别是多少?"
数据库里有上千万条城市记录,每条记录除了人口数,还有城市名、经纬度、海拔、时区等十几个字段。同一个问题、同一批数据、同一条查询语句,在两套 Elasticsearch 集群上的表现却是:
一套集群平均 0.38 秒 返回结果;
另一套集群平均 20.67 秒 才算完——慢了 50 多倍。
这不是魔法,也不是硬件差距。两套集群跑在同样的网络环境里,唯一的区别是:一套采用了 列式存储(Columnar) 引擎,另一套仍是传统的 行式存储。2026 年 8 月,上海悦高软件的测试团队对 Elasticsearch 9 的列存引擎与传统 ES8 行存集群做了一次严谨的对照实验,本文将用通俗的方式带你读懂这次实验:列存到底快在哪,为什么快,以及它是不是"万能药"。
认识主角:Elasticsearch与ES|QL
Elasticsearch(简称 ES)是目前最流行的分布式搜索与分析引擎之一,广泛用于日志检索、网站搜索、指标监控等场景。你可以把它想象成一个能装下海量数据、又能秒级响应查询的超级账本。
ES|QL是Elasticsearch推出的新一代查询语言。它长得有点像SQL,写起来像流水线。比如开头那条"各国人口统计"查询,用ES|QL写出来是这样:
FROM geonames| STATS total_pop = SUM(population) BY country_code| SORT total_pop DESC| LIMIT 100
意思是:从geonames数据表中,按国家代码分组,把人口加总,再按总数倒序取前100名。竖线|就像工厂流水线,数据从上一个工序流向下一个工序。
这种"聚合分析"类查询,正是本次实验考察的重点——也是列式存储最擅长的战场。
行存与列存:图书馆的两种藏书方式
要理解列存为什么快,先看数据在磁盘上是怎么"摆"的。
行式存储像一本装订好的百科全书:一个城市的名字、人口、经纬度、海拔……所有字段紧挨着存在一起,读一条记录就把整行都读出来。这很适合"查详情"——比如搜出某条日志的完整内容。
列式存储则像图书馆的分类卡片柜:所有城市的"人口数"放在一个抽屉里,所有"国家代码"放在另一个抽屉。同一列的数据类型相同、往往还大小相近,紧紧挤在一起。
这个差别看似只是"摆放方式"不同,但在统计分析场景里是决定性的:
统计往往只用少数几列。 算各国人口总和,只需要"人口"和"国家代码"两个抽屉;而行存却要把整本"百科全书"都搬出来,绝大部分搬运是无效劳动。
同列数据可以打包压缩。 一万个连续的人口数字挤在一起,可以用极高的压缩比存储,磁盘读取量进一步骤减。
一列数据可以整体送进 CPU 批量计算(这叫"向量化执行"),而不是一条一条地逐行处理。
实验是怎么做的:一次严谨的对照实验
好的结论来自好的实验设计。测试团队的方案堪称"控制变量法"的教科书示范:
两组被试。 两套集群唯一的差异是版本与存储引擎——es9-columnar(Elasticsearch 9,列存索引优化)与es8-logsdb(Elasticsearch 8,传统行存日志库)。
统一考题。固定两条ES|QL统计分析语句,覆盖两类典型场景:
简单维度聚合:geonames数据集,按国家汇总人口并排序取前100;
多字段时间复合聚合:纽约出租车(nyc_taxis)数据集,按"天+支付方式"分组,同时计算车费、小费、里程三个平均值,返回1000行。
统一规则。 单并发串行执行,每条查询固定跑 10 次取全量样本;记录客户端全链路耗时与ES引擎内部耗时(took),统计最小值、平均值、最大值、P95与P99分位数;同时采集节点CPU与内存指标。
排除干扰。查询文本、迭代次数、超时参数完全一致;两套集群同网段部署,排除网络差异。全程由Python自动化脚本驱动,自动汇总横向对比报表。
也就是说,最终成绩单上的任何差距,都只能归因于那一件事:存储引擎不同。
结果揭晓:数字会说话
先看"各国人口汇总"这条简单聚合查询的平均耗时对比:
集群 |
平均耗时 |
引擎内部耗时 (均值) |
P95 |
P99 |
ES9列存 |
0.38秒 |
241毫秒 |
0.45秒 |
0.47秒 |
ES8行存 |
20.67秒 |
20532毫秒 |
25.13秒 |
25.91秒 |
ES9的平均时延只有ES8的1.83%——引擎内部计算速度提升约85倍。原本要泡一杯咖啡等待的查询,现在一次眨眼就完成了。
再看更复杂的出租车"按天+支付方式"复合聚合:
集群 |
平均耗时 |
引擎内部耗时 (均值) |
P95 |
P99 |
ES9列存 |
6.28秒 |
5871毫秒 |
7.79秒 |
8.03秒 |
ES8行存 |
17.79秒 |
17364毫秒 |
19.36秒 |
19.60秒 |
ES9平均时延约为ES8的35.31%,内部计算提速约3倍。依然是明显优势,但远不如简单聚合那样"碾压"。
这里藏着一个重要规律:查询越简单、越聚焦于少数列的聚合(如SUM、AVG),列存的优势越大;涉及多字段复合分组时,优势会收窄但仍显著。
另一个值得注意的细节是稳定性:ES9的P99与平均值非常接近,说明每次执行耗时波动极小;而ES8的最大耗时与P99都远高于均值,单次请求忽快忽慢。对线上系统而言,"稳定的慢"尚可规划,"随机的慢"才是事故之源。
为什么列存这么快:拆开引擎盖看三处优化
借助查询计划(Query Plan)分析,性能差距可以归结为三个层面:
第一,按需读取,不做无用功。列存引擎只加载聚合真正需要的字段列,直接跳过无关数据。而行存必须读取完整文档行——为了算一个人口数,把海拔、时区、经纬度统统搬进内存。
第二,向量化计算,批量处理。ES|QL在列存上采用原生向量化执行:CPU一次对整块列数据做批量运算,像流水线冲压零件,而不是像行存那样逐条文档"手工打磨"。现代CPU的并行能力被充分释放。
第三,计算下推,就近结算。分组统计的预计算被"下推"到存储层就地完成,避免把海量明细数据搬运到上层再汇总,大幅减少了内存中的数据搬运(shuffle)开销。
反过来,ES8的瓶颈也正在于此:整行读取带来大量无效IO、逐条文档计算聚合、大数据量分组时内存shuffle开销高企、CPU被长期占满。
更省"电"的引擎:CPU 开销对比
快,只是好处的一半;省,是另一半。测试结束后节点CPU稳态占用如下:
ES9列存集群:平均15%
ES8行存集群:平均39%
完成同样的查询任务,ES9的CPU消耗只有ES8的约38%。这意味着:同样规格的集群,ES9可以承载更高的并发查询、留出更多资源给写入与其他任务;而ES8在行存模式下,文档遍历、逐行解析与内存分组shuffle会持续吞噬CPU,并发一上来就容易"打满"。
内存方面,两套集群测试后占用均接近零——这并不说明两者内存效率相同,而是因为单并发、10次迭代的负载总量太低,查询结束后JVM内存迅速回收。真正的分水岭要等高并发、大结果集场景下观察GC频率与堆内存峰值才能显现。
冷静看待:这次实验不能说明什么
科普的最后一步,是诚实地划清结论的边界:
这是单并发基线测试。它衡量的是"冷启动+单请求"的水平,高并发下的表现(尽管ES9优势预计进一步放大)尚待压测验证;
样本量为10次。足以看出量级差距,但精细的分位数统计意义有限;
内存维度未拉开差距。高并发大结果集场景需补充JVM监控,才能完整评估资源开销。
结语:给技术选型者的三句话
把这实验翻译成决策语言:
1)面向报表、指标统计、多维分析类业务,优先升级ES9并启用Columnar 列存索引——简单聚合时延可降低98%以上,复杂时间聚合也快约3倍;
2)不要让ES8行存集群硬扛统计分析——它适合简单检索与少量过滤,不是聚合分析的舞台;
3)快和省可以兼得——列存+向量化让同等负载的CPU占用降至原来的约四成,集群容量天花板随之抬高。
从20秒到0.4秒,改变的不是数据本身,而是数据的"摆放方式"与"计算方式"。存储引擎的每一次范式演进,都在悄悄改写"大数据分析"的速度上限。
本文基于上海悦高软件股份有限公司2026年8月11日发布的《ES9.5 ESQL查询时延与资源开销对比报告》撰写。原始测试:两套Elasticsearch集群(ES9列存/ES8行存),两条ES|QL聚合查询,单并发串行各执行10次,数据由Python自动化脚本采集与汇总。
附:小词典
Elasticsearch(ES):分布式搜索与分析引擎,擅长海量数据的近实时检索与统计。
ES|QL:Elasticsearch 的新一代管道式查询语言,语法形如SQL,通过竖线串联处理步骤。
行式存储:按"行"连续存放数据,适合整行读取的检索场景。
列式存储(Columnar):按"列"连续存放数据,适合只取少数列做统计的分析场景。
向量化执行:CPU一次批量处理一整块数据而非逐条处理,充分利用 CPU 并行能力。
P95/P99:分位数指标。P95 = 95%的请求快于该值;数字越接近平均值,系统越稳定。
查询计划(Query Plan):引擎执行一条查询的内部步骤安排,优化器据此决定怎么读数据、怎么算。
过往文章


