尧图精选

Elasticsearch Columnar 模式解析:列式存储与列式数据库的本质区别

🕒 发布时间:2026/9/20 20:39:50 📁 来源:尧图网络
1. 为什么“列式存储”这个词经常被用错1.1 一个被混淆了十年的概念我最早接触列式存储大概是在做日志分析平台的时候当时团队里有人说“我们上列式数据库吧”另一拨人说的是“我们用列式存储格式就行”。这两句话听起来差不多但实际落地的东西差了十万八千里。后来我发现这种混淆在 Elasticsearch 社区里更常见——很多人看到 Elasticsearch 推出了 Columnar 模式第一反应是“ES 变成列式数据库了”这个理解是有偏差的。先把概念掰开。列式存储Columnar Storage描述的是一种数据的物理组织方式同一列的数据连续存放在一起而不是按行一条条排列。列式数据库Columnar Database则是一整套系统它的存储引擎、执行引擎、优化器、索引结构都是围绕列式布局从头设计的典型代表是 ClickHouse、Doris 这类。两者的关系类似于“用了涡轮增压发动机”和“这是一台专门为赛道设计的赛车”——前者是技术手段后者是完整产品。Elasticsearch 的 Columnar 模式本质上是在原有 doc_values 列式结构的基础上把这种列式能力进一步暴露到查询执行层让 ES|QL 这类新查询引擎能够直接以列为单位做向量化处理。它没有把 Elasticsearch 变成一个列式数据库而是让 Elasticsearch 在特定场景下具备了列式数据库才有的那部分性能特征。1.2 这个区分为什么重要如果你把 Columnar 模式理解成“ES 变列库了”你可能会做出错误的架构决策。比如有人会想那我是不是可以把 ES 当 OLAP 引擎用直接跑大宽表聚合答案是——部分场景可以但你要清楚它的边界在哪里。理解这个区分直接影响三件事第一你知道什么查询会变快、什么查询不会第二你知道资源该怎么规划尤其是内存和磁盘的配比第三你知道什么时候该继续用 ES什么时候该把数据同步到真正的列式数据库里去。这三点如果搞错了轻则性能不达预期重则整个技术选型走弯路。我见过一个团队因为误以为 Columnar 模式能让 ES 扛住高并发的大规模 GROUP BY结果上线后查询延迟从秒级飙到分钟级最后不得不临时加了一层预聚合。这个坑的根源就是对“列式存储不等于列式数据库”没有清醒认识。1.3 本文适合谁看这篇内容适合几类人正在用 Elasticsearch 做日志分析或可观测性、对 ES|QL 感兴趣想试试 Columnar 模式的工程师正在做技术选型、纠结要不要引入独立 OLAP 引擎的架构师以及那些被“列式”这个词搞晕过、想彻底理清楚的人。我会从底层原理讲到实操配置再到踩坑经验尽量让不同基础的人都能拿到能用的东西。2. Columnar 模式到底改了什么2.1 从 doc_values 说起ES 其实一直有列式结构很多人不知道Elasticsearch 从 2.x 时代就引入了doc_values这本身就是一种列式存储结构。它的作用是把文档中每个字段的值按列的方式写到磁盘上用于排序、聚合和脚本访问。也就是说ES 的存储层早就有列式基因了。那为什么以前没人说 ES 是列式的因为 doc_values 主要服务于“按列读取少量值”的场景比如排序时取某个字段的所有值。它的读取模式还是偏行式的——查询引擎按文档 ID 去捞数据然后逐条处理。这种模式下列式存储的优势没有完全发挥出来因为 CPU 大部分时间花在了逐行遍历和函数调用上而不是高效的数据扫描上。Columnar 模式做的事情是把 doc_values 里的数据以批量的、向量化的方式喂给查询引擎。原来是一条一条处理现在是一批一批处理。这个变化听起来简单但对 CPU 的利用效率影响巨大。2.2 向量化执行让 CPU 少做无用功我用一个生活化的类比来解释向量化。假设你要从一摞发票里统计总金额。行式处理就像你一张一张拿起来看看完一张记一笔再拿下一张。向量化处理则是你把一叠发票摊开一次看十张心算十张的金额再加总。后者明显更快因为减少了“拿起来放下”这个动作的开销。在 CPU 层面这个“拿起来放下”的开销就是函数调用、分支预测失败、缓存未命中。向量化执行通过批量处理数据让 CPU 的流水线更少被打断同时更容易命中 L1/L2 缓存。Columnar 模式在 ES 里的核心价值就在这里——它让 ES|QL 的执行引擎能够以向量块通常一批几千行为单位处理数据而不是逐行。具体来说Columnar 模式下的数据读取会走一条专门的路径从 doc_values 里按列取出一个数据块转换成列式内存布局然后交给向量化的算子处理。过滤、聚合、排序这些操作都在这个列式布局上完成中间不需要反复在行式和列式之间转换。2.3 和真正列式数据库的差距在哪既然 Columnar 模式这么厉害为什么还说 ES 不是列式数据库差距主要在三个地方。第一是存储格式的彻底性。ClickHouse 这类系统从写入那一刻起就是纯列式存储每一列独立压缩、独立索引列与列之间物理隔离。ES 的 doc_values 虽然也是列式但它和倒排索引、_source、行存等结构是共存的一个文档的数据会分散在多个地方。这意味着 ES 在读取时可能还需要访问其他结构来补全信息。第二是执行引擎的成熟度。列式数据库的优化器会做大量列裁剪、谓词下推、运行时过滤甚至根据数据特征动态选择算法。ES|QL 的优化器还在演进中Columnar 模式目前主要解决的是执行层的向量化问题优化器的深度还有提升空间。第三是并发模型。列式数据库通常为高并发分析查询做了大量针对性设计比如 MPP 架构、算子级并行。ES 的查询并发模型更偏向搜索场景Columnar 模式改善的是单查询的执行效率而不是整个系统的并发吞吐架构。理解这三点差距你就知道 Columnar 模式的定位了它是 ES 在分析场景下的一次重要补强但不是要把 ES 变成 ClickHouse。3. 实操怎么用上 Columnar 模式3.1 版本与前提条件Columnar 模式不是所有 ES 版本都能用的。它最早在 8.x 后期版本中以技术预览形式出现到 9.x 逐渐成熟。如果你还在用 7.17.0 这种版本那 Columnar 模式跟你没关系先把版本升上去再说。我实测下来建议至少用 8.14 以上的版本9.x 的体验会更完整。安装方式上不管你是用 docker-compose 部署还是裸机安装核心是确认你的 ES|QL 功能是开启的。默认情况下 ES|QL 是启用的但如果你在 elasticsearch.yml 里手动关过相关配置需要检查一下。另外Columnar 模式对内存有一定要求因为它需要在内存里维护列式数据块建议单节点堆内存不低于 4GB生产环境按数据量往上加。关于 licenseES|QL 的基础功能在免费版就能用但部分高级特性可能需要更高等级的授权。如果你看到某些 Columnar 相关配置报权限错误先确认一下你的 license 等级。至于网上有人问的“elasticsearch 9版本 rrf 是企业版的怎么办”那是另一个话题跟 Columnar 模式不是一回事这里不展开。3.2 开启 Columnar 模式的关键配置Columnar 模式在 ES 里不是一个全局开关而是通过查询层面的设置来控制的。你可以在 ES|QL 查询里指定使用列式执行路径。下面是一个典型的配置示例# 在 kibana 的 Dev Tools 里执行 POST /_query?formattxt { query: FROM logs-* | STATS count(*) BY host.name | SORT count(*) DESC | LIMIT 10, columnar: true }这里的columnar: true就是关键。它告诉 ES|QL 的执行引擎尽量走列式向量化路径。注意这是一个提示性参数不是强制性的。如果查询本身不适合列式执行比如涉及大量逐行脚本引擎可能会回退到行式路径。在集群层面还有一些相关配置可以调整。比如控制列式批处理的大小# elasticsearch.yml esql.columnar.batch_size: 4096这个 batch_size 决定了每次向量化处理多少行。默认值通常在 2048 到 4096 之间。调大它能提升吞吐但会增加内存占用调小它内存友好但 CPU 利用率会下降。我一般建议先用默认值跑观察 CPU 和内存的实际表现再决定要不要调。3.3 哪些查询能从 Columnar 模式受益不是所有查询都能吃到 Columnar 模式的红利。根据我的实测以下几类查询提升最明显查询类型典型场景提升幅度实测参考大规模聚合STATS count/sum/avg BY 维度2-5 倍宽表扫描只取少数列做过滤1.5-3 倍排序取 TopNSORT LIMIT2-4 倍时间范围过滤按 timestamp 筛选1.5-2 倍逐行脚本复杂 painless 逻辑基本无提升甚至变慢从表里能看出来列裁剪越彻底、聚合越重、数据扫描量越大Columnar 模式的收益越明显。反过来如果你的查询需要访问很多列、或者依赖逐行脚本做复杂计算列式路径反而可能因为数据布局转换而变慢。这里有个经验判断一个查询适不适合 Columnar就看它是不是“读很多行但只用少数列”。如果是放心开如果不是先测再说。3.4 一个完整的对比测试我拿一份大约 5000 万行的日志数据做过对比测试硬件是 3 节点集群每节点 16C64G。查询是统计每个 host 的错误日志数量按数量降序取前 20。行式路径的执行计划显示它需要逐批读取文档然后在内存里做哈希聚合。整个过程 CPU 利用率在 40% 左右波动因为大量时间花在了等待和函数调用上。查询耗时稳定在 8.2 秒左右。开启 Columnar 模式后执行计划变成了列式扫描加向量化聚合。CPU 利用率拉到了 75% 以上因为向量化算子能持续喂饱 CPU。查询耗时降到了 2.6 秒提升大约 3.1 倍。内存占用方面列式路径的峰值内存比行式高了约 15%因为要维护列式数据块但完全在可接受范围内。这个测试说明一个事Columnar 模式的收益是实打实的但代价是更高的 CPU 和内存瞬时占用。如果你的集群资源本来就紧张开之前要评估一下。4. 踩坑记录与排查技巧4.1 开了 Columnar 反而变慢是怎么回事这是我最常被问到的问题。有人兴冲冲开了columnar: true结果查询比之前还慢然后就来问是不是配置错了。其实大概率不是配置问题而是查询本身不适合列式路径。最常见的三种情况第一种是查询里用了大量EVAL做逐行计算列式引擎处理这种逻辑时需要在列式块和行式之间来回转换开销比纯行式还大。第二种是查询访问的列非常多几乎覆盖了文档的所有字段这时候列裁剪没有意义列式布局的优势发挥不出来。第三种是数据量太小比如只有几万行列式路径的初始化开销反而占了主导。排查方法很简单用EXPLAIN看执行计划确认引擎实际走的是哪条路径。如果计划里显示的是行式算子说明引擎判断列式不划算自动回退了。这时候你要么改查询要么接受行式路径。提示不要盲目给所有查询加columnar: true。先跑基准测试确认有收益再固化到生产查询里。4.2 内存暴涨的排查思路Columnar 模式对内存的敏感度比行式高。我遇到过一次开了 Columnar 之后节点内存持续上涨最后触发了 circuit breaker。排查下来发现是 batch_size 设得太大加上并发查询多每个查询都在内存里维护大块列式数据累积起来就爆了。解决思路分三步。第一步先把 batch_size 调回默认值观察内存是否回落。第二步限制同时走列式路径的查询并发数可以通过查询队列或者应用层限流来做。第三步如果数据量确实大考虑给节点加内存或者把大查询拆成多个小查询分批跑。这里有个细节ES 的 circuit breaker 对列式路径的估算可能不够精确有时候内存还没到阈值就被拦了有时候又拦不住。所以生产环境一定要留足内存余量别卡着阈值跑。4.3 常见问题速查表现象可能原因处理方式查询报 circuit breaker列式块内存超限调小 batch_size降低并发开了 columnar 没变化查询不适合列式路径用 EXPLAIN 确认执行计划查询变慢逐行脚本或全列访问改写查询减少列访问节点 CPU 飙高向量化算子吃满 CPU正常现象评估资源是否够结果和行式不一致浮点聚合顺序差异检查精度要求必要时用行式部分查询报权限错误license 等级限制确认 license 覆盖范围这个表里的最后一条“结果不一致”值得多说一句。列式路径的聚合顺序和行式可能不同对于浮点数求和这类操作理论上结果会有微小差异。绝大多数场景下这个差异可以忽略但如果你做的是财务对账这种精度敏感的场景要么用行式路径要么在应用层做二次校验。4.4 几个我踩过的坑第一个坑是在混合负载集群上无差别开启 Columnar。我有个集群同时跑搜索和分析查询一开始图省事给所有 ES|QL 查询都加了 columnar 参数结果搜索查询的延迟明显上升。原因是列式路径占用了更多 CPU 和内存挤压了搜索查询的资源。后来改成只给分析类查询开问题就解决了。第二个坑是忽略了 doc_values 的配置。Columnar 模式依赖 doc_values如果你某个字段把 doc_values 关了那这个字段在列式路径里就没法高效读取。我遇到过有人为了省磁盘把 doc_values 全关了结果开 Columnar 完全没效果。所以用 Columnar 之前确认你要聚合和排序的字段都开着 doc_values。第三个坑是版本升级后行为变化。ES 的 ES|QL 和 Columnar 实现迭代很快不同版本之间的执行计划可能不一样。我有次升级后发现原来很快的查询变慢了查了半天发现是新版本优化器改了策略。所以升级前一定要在测试环境跑一遍关键查询的基准测试。5. 什么时候该用 Columnar什么时候不该用5.1 适合的场景画像Columnar 模式最适合的场景我总结成一个画像数据量大、查询以聚合和扫描为主、访问列数少、对延迟有一定容忍度。典型的例子包括日志分析里的错误统计、可观测性里的指标聚合、安全分析里的异常计数。这类场景的共同点是查询本身是“读多写少、读宽用窄”。数据可能有几十上百个字段但每次查询只关心其中三五个。Columnar 模式通过列裁剪和向量化把这种查询的效率拉到了接近列式数据库的水平同时又不用你额外维护一套系统。如果你的团队已经在用 Elasticsearch 做日志和可观测性而且查询模式符合上面这个画像那 Columnar 模式几乎是必开的。它带来的性能提升不需要你改架构、迁数据性价比很高。5.2 不适合的场景与替代方案反过来如果你的查询是高并发、低延迟、访问列多、涉及复杂逐行计算那 Columnar 模式帮不了你太多。比如实时风控场景要求单查询毫秒级返回而且每次要访问几十个字段做规则判断这种场景列式路径的优势发挥不出来甚至可能因为数据布局转换而变慢。这种时候正确的做法不是硬上 Columnar而是考虑把数据同步到真正的列式数据库里。ClickHouse、Doris 这类系统在高并发分析查询上的架构优势是 ES 的 Columnar 模式短期内追不上的。你可以用 ES 做日志存储和搜索用列式数据库做分析各司其职。还有一种情况是数据量不大但查询很复杂。比如几百万行数据但查询里有大量嵌套聚合和窗口函数。这种场景下列式路径的初始化开销可能超过收益不如直接用行式路径或者把计算逻辑放到应用层做。5.3 一个决策流程图文字版我平时判断要不要开 Columnar会走这么几个问题第一数据量是不是超过千万行如果不到先别折腾行式够用。第二查询是不是以聚合和扫描为主如果是逐行脚本为主别开。第三每次查询访问的列是不是远少于总列数如果不是收益有限。第四集群资源是不是有富余如果 CPU 和内存本来就紧张开了可能适得其反。第五延迟要求是不是在秒级以上如果要求毫秒级慎重。五个问题里如果有三个以上是“是”那就值得开。如果大部分是“否”那就先别开或者只给特定查询开。6. 关于 Columnar 模式的一些个人体会我用 Columnar 模式大概有一年多时间从最早的预览版到现在的稳定版感受最深的一点是它是一个“锦上添花”的能力不是“雪中送炭”的救星。如果你的 ES 集群本身就有性能问题指望开个 Columnar 就全解决了那大概率会失望。但如果你的集群运行良好只是某些分析查询偏慢Columnar 模式能给你带来很实在的提升。另一个体会是不要被“列式”这个词带偏。列式存储、列式数据库、列式执行这三个概念经常被混着用但含义完全不同。ES 的 Columnar 模式是列式执行层面的优化它借用了列式存储的结构但没有变成列式数据库。想清楚这一点你在做技术决策时就不会犯方向性错误。最后分享一个小技巧如果你不确定某个查询开 Columnar 有没有用别猜直接做 A/B 测试。同一个查询跑两遍一遍带 columnar 参数一遍不带对比执行时间和资源占用。数据不会骗人测出来的结果比任何理论分析都可靠。我现在的习惯是任何要固化到生产的 ES|QL 查询上线前都跑一遍这个对比确认有收益才加参数。至于后续还能怎么扩展我目前在看的是 Columnar 模式和 ES 的并行查询能力结合后的表现以及它在时序数据场景下的压缩效率。这两个方向如果做好了ES 在可观测性领域的竞争力会再上一个台阶。不过那是后话了等有实测数据再聊。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →