为什么列式存储更适合OLAP?

列式存储是一种数据存储方式,其核心思想是将数据按列而非按行进行组织和存储。它与传统的行式存储在数据布局、适用场景和性能特征上有显著差异。

Clickhouse等OLAP数据存储系统正是由于其使用列式存储,很大程度的提高了数据查询和分析能力,这篇文章我就向大家分享一下行式存储和列式存储到底有何区别?为什么Clickhouse等OLAP系统更偏向使用列式存储?

先说一下基本概念

行式存储列式存储 是两种不同的数据存储方式。注意是底层的存储方式不同,对于我们上层使用而言都是差不多的表结构。

行式存储 将每一行的数据连续存储在一起,适合事务处理,可以快速读取整条记录;而列式存储将每一列的数据连续存储在一起,适合数据分析,查询特定列时只需读取该列数据,压缩效率更高。

简单来说,行式存储适合频繁的增删改查操作,列式存储则更适合大数据分析和聚合查询。

举例

行式存储(Row-Based),按行连续存储:

javascript 复制代码
| 用户ID | 姓名  | 年龄 | 地址 |
| ------ | ----- | ---- | ---- |
| 1      | Alice | 25   | 北京 |
| 2      | Bob   | 30   | 上海 |
| 3      | Carol | 28   | 深圳 |

磁盘存储结构(假设每行连续存放):

javascript 复制代码
1,Alice,25,北京 | 2,Bob,30,上海 | 3,Carol,28,深圳

列式存储(Column-Based),列式存储示例(按列独立存储):

javascript 复制代码
用户ID列 → [1, 2, 3]
姓名列   → [Alice, Bob, Carol]
年龄列   → [25, 30, 28]
地址列   → [北京, 上海, 深圳]

磁盘存储结构(每列独立存储为文件):

javascript 复制代码
用户ID.bin → 1,2,3
姓名.bin → Alice,Bob,Carol
年龄.bin → 25,30,28
地址.bin → 北京,上海,深圳

画个图可能更清楚一些:

列式存储与行式存储的核心差异

1)数据组织方式

行式存储 :数据按行连续存储。例如,表中一行数据的所有字段(如 用户ID、姓名、年龄、地址)在磁盘上连续存放,形成一个数据块。适用场景:事务型处理(OLTP),如频繁的单行读写、点查询。

列式存储 :数据按列连续存储。例如,所有用户的 用户ID 存储在一起,所有 年龄 存储在一起,形成多个独立的列数据块。适用场景:分析型处理(OLAP),如聚合统计、批量扫描。

2)I/O效率

行式存储 :读取单行数据时效率高,但若需统计某列的聚合值(如 SUM(年龄)),需要读取整行数据(包含无关字段),导致 I/O放大

列式存储 :仅读取目标列的数据,显著 减少I/O量 。例如统计 年龄总和 时,只需读取 年龄 这一列。

举例

行式存储的I/O过程:

javascript 复制代码
需要读取所有行的完整数据(包括无关字段):
1,Alice,25,北京 → 提取25
2,Bob,30,上海   → 提取30
3,Carol,28,深圳 → 提取28

总读取数据量为3行 × 4列 = 12个字段,有效数据占比为25%

列式存储的I/O过程:

javascript 复制代码
仅读取年龄列文件(年龄.bin → [25, 30, 28]):
直接获取所有年龄值,无需处理其他字段。

总读取数据量为3行 × 1列 = 3个字段,有效数据占比为100%

3)数据压缩

行式存储:单行内不同列的数据类型差异大(如字符串、数值混杂),压缩率低。

列式存储 :单列数据具有相同类型和语义,可通过 高效压缩算法(如LZ4、ZSTD、Delta编码)大幅降低存储空间(通常压缩比可达5-10倍),同时减少磁盘到内存的数据传输量。

4)CPU计算优化

针对列式存储而言,现代CPU支持单指令多数据流(SIMD),可对连续的同类型列数据批量计算(如一次处理128个数值),显著提升聚合计算速度。其次,连续存储的列数据更易被CPU缓存命中,减少内存访问延迟。

ClickHouse为何选择列式存储?

ClickHouse专为OLAP场景设计,其核心需求是 高速处理海量数据的聚合查询,而列式存储的以下特性完美契合这一目标:

减少数据扫描量:OLAP查询通常仅涉及少数列(如统计某几列的SUM/AVG)。列式存储仅需读取相关列,避免加载无关数据。

最大化压缩效率:高压缩率不仅节省存储成本,还减少磁盘到内存的数据传输时间,提升查询吞吐量。

向量化执行引擎:ClickHouse的查询引擎针对列式数据设计,可批量处理列数据,利用SIMD指令加速计算。

预聚合与索引优化 :列式存储便于实现稀疏索引、跳数索引(如GRANULARITY),快速定位数据块,减少扫描范围。

场景对比

操作 行式存储 列式存储
单行读写(如INSERT) 快(顺序写) 慢(需更新多列文件)
全表扫描(如COUNT) 快(仅读一列)
聚合查询(如SUM) 慢(读所有行) 快(仅读一列)
压缩率

小总结

列式存储通过按列组织数据、减少I/O、高效压缩和向量化计算,在OLAP场景中实现了比行式存储高1-2个数量级的查询性能。ClickHouse等OLAP系统正是基于这些原理,选择列式存储作为其高性能分析引擎的核心基础,使其在海量数据实时分析领域表现出色。

相关推荐
章豪Mrrey nical2 分钟前
前后端分离工作详解Detailed Explanation of Frontend-Backend Separation Work
后端·前端框架·状态模式
写写闲篇儿1 小时前
微软面试之白板做题
面试·职场和发展
派大鑫wink1 小时前
【JAVA学习日志】SpringBoot 参数配置:从基础到实战,解锁灵活配置新姿势
java·spring boot·后端
程序员爱钓鱼1 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
xUxIAOrUIII2 小时前
【Spring Boot】控制器Controller方法
java·spring boot·后端
Dolphin_Home2 小时前
从理论到实战:图结构在仓库关联业务中的落地(小白→中级,附完整代码)
java·spring boot·后端·spring cloud·database·广度优先·图搜索算法
zfj3212 小时前
go为什么设计成源码依赖,而不是二进制依赖
开发语言·后端·golang
weixin_462446232 小时前
使用 Go 实现 SSE 流式推送 + 打字机效果(模拟 Coze Chat)
开发语言·后端·golang
JIngJaneIL2 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
敲敲了个代码2 小时前
隐式类型转换:哈基米 == 猫 ? true :false
开发语言·前端·javascript·学习·面试·web