前两天一个做数据平台的朋友跟我吐槽,说他们集群上跑一个多维分析报表,两张几亿行的表关联聚合,在原来的行式存储引擎上要跑四十多分钟,业务方等得没脾气,天天催。后来换成了列式存储引擎,同样的SQL,六分钟出结果。他没有改任何业务逻辑,只是把存储底座换了,查询性能就翻了好几倍。
这个案例很有代表性。很多人听过"列式存储"这四个字,知道大数据领域离不开它,但真要问起来:它和行式存储到底差在哪、为什么快那么多、什么样的场景适合用、建表的时候该怎么设计排序键和分区字段,能讲清楚的人并不多。这篇文章就把这些事情掰开揉碎讲一遍,从底层原理到工程选型再到避坑经验,尽量做到看完就能用。
文章里提到的原理和方案,基本覆盖了市面上主流列式存储引擎(Parquet、ORC、ClickHouse、Doris等)的共通设计思路。掌握了这套底层逻辑,你再看任何一个具体的列式存储产品,都会轻松很多——因为它们的核心思想其实是同源的。
1. 为什么大家都要用列式存储:从数据布局说起的IO革命
1.1 行式和列式的本质差异:数据在磁盘上怎么摆
先说最基础的问题:行式存储和列式存储到底有什么区别。
想象一张员工表,里面有工号、姓名、部门、薪资、入职日期等20个字段,一共1亿行数据。用Excel打开长这样:
code复制工号 姓名 部门 薪资 入职日期
001 张三 技术部 20000 2020-03-15
002 李四 市场部 18000 2019-07-01
003 王五 技术部 25000 2021-11-20
...
在磁盘上,行式存储(比如MySQL的InnoDB、PostgreSQL、Oracle)是这样摆数据的:把每一行的所有字段连续写在一起,一行写完再写下一行。逻辑上可以理解为:
code复制[001|张三|技术部|20000|2020-03-15][002|李四|市场部|18000|2019-07-01][003|王五|技术部|25000|2021-11-20]...
列式存储(比如Parquet、ORC、ClickHouse的MergeTree)则是反过来:把每一列的数据连续写在一起。磁盘上的物理布局类似于:
code复制[001|002|003|...] [张三|李四|王五|...] [技术部|市场部|技术部|...] [20000|18000|25000|...] [2020-03-15|2019-07-01|2021-11-20|...]
这个布局差异,就是列式存储一切性能优势的源头。
1.2 一条聚合查询背后的IO账本
现在来算一笔IO账。假设业务方要查:每个部门的平均薪资。SQL长这样:
sql复制SELECT 部门, AVG(薪资) FROM 员工表 GROUP BY 部门;
这个查询最终只需要两列数据:部门、薪资。其余18个字段完全用不上。
在行式存储里,数据按行连续存放,数据库要把每一行的完整数据都读进内存,才能拿到部门和薪资两个字段。1亿行 × 每行20个字段,假设一行平均2KB,总共要读大约200GB的数据——即使实际只需要那两个字段,磁盘IO也得把整行都搬回来。
在列式存储里,每一列在磁盘上是独立连续存储的。查询引擎只需要读取"部门"和"薪资"两个列文件,假设每行这两个字段加起来平均100字节,总共只需要读1GB左右。同样的查询,IO量直接变成原来的二十分之一。
磁盘IO在大数据场景下是最昂贵的资源之一。动不动几十GB、几百GB的IO差距,直接决定了查询是秒级还是分钟级。
1.3 列式存储不只是"读得少":为什么压缩率也差这么多
除了IO量减少,列式存储在数据压缩上的优势同样巨大。
同一列的数据类型是一致的,而且相邻数据之间往往有很强的相关性。比如"部门"这一列,排在一起的可能大量都是"技术部"、"市场部",重复值非常多;"入职日期"这一列,按时间顺序写入的话,数值变化幅度很小。这种结构对压缩算法极其友好。
用字典编码举例:把"技术部"编码为0、"市场部"编码为1、"财务部"编码为2,存储的时候只存编码数字,再加一个字典映射表。这种方式的压缩比通常能达到10:1甚至更高。
相比之下,行式存储里一行数据包含各种类型的字段混在一起,字符串、数字、日期交错排列,压缩算法很难找到重复模式,压缩比自然就低。
我见过一个真实案例:同样一份100GB的原始数据,行式存储压缩后还剩60GB左右,转成Parquet列式格式后直接压到15GB。磁盘占用少了四分之三,查询时的IO开销也同步缩小,一箭双雕。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 列式存储核心机制拆解:为什么它天生适合分析型查询
2.1 谓词下推:先把没用的数据过滤掉再干活
列式存储之所以能在万亿级数据上做秒级查询,除了列裁剪(只读需要的列),还有一个关键机制叫谓词下推(Predicate Pushdown)。
先看行式存储的处理逻辑。假设要查:技术部薪资大于20000的员工有多少。SQL
