〇、前言
简单来说,行式存储(Row-based Storage)就是将同一行的所有字段连续存放;列式存储(Column-base Storage)则将同一列的数据集中存放。
它们代表了数据在磁盘或内存中组织的两种相反维度,现代数据库(如 ClickHouse、Hana、PostgreSQL 的部分插件)常根据业务需求选择其一,甚至支持混合存储引擎。
在日常开发中,它们非常重要。虽然普通业务代码可能不直接操作底层存储格式,但理解这一概念可以更清晰的判断如何选型数据库、设计表结构以及优化查询性能。如果选错存储模式可能导致分析查询慢几个数量级,或事务处理吞吐量大幅下降。
那么本文将就这两种存储方式分别进行简单的介绍和对比,先了解其基本信息,后续再进行简单实践,供参考。
一、什么是行式存储(Row-based Storage)?
行式存储是传统关系型数据库采用的存储方式,也是最常见的。
数据以行为基本逻辑存储单位,同一行内的所有列数据在存储介质上连续存放。如下示例:- // 如下三行人员信息数据
- 行1: [张三, 25, 北京, 工程师, 3000]
- 行2: [李四, 30, 上海, 产品经理, 3500]
- 行3: [王五, 28, 广州, 设计师, 3200]
- // 磁盘存储可以理解为连续的存储空间:
- 张三|25|北京|工程师|3000|李四|30|上海|产品经理|3500|....
复制代码 这样行式存储的特点:按记录/行连续存储,每行包含所有字段;数据整行读取,效率高;支持高效的单行数据操作,如插入、更新、删除等;天然支持 ACID 事务特性;压缩效率较低,因为每一列的字段类型大概率是不同的。
行式存储还可以通过索引来优化查询效率,例如 B+ 树等索引技术成熟,支持复杂查询优化。
但是,行式存储也有许多缺陷。首先是聚合查询慢,例如统计某列时需加载整行数据,造成 I/O 开销大;压缩率低,不同列数据类型混杂,难以高效压缩;扫描效率低,全表扫描时即使只需要少数列但也必须读取所有列。
典型应用场景:OLTP系统(在线事务处理)、银行交易系统、电商订单系统、企业ERP系统、在线支付系统等等。
代表数据库常见的有 MySQL、PostgreSQL、Oracle、SQL Server、DB2 等,都属于传统关系型数据库。
二、什么是列式存储(Column-Based Storage)?
列式存储是分析型数据库采用的存储方式。数据以列为基本逻辑存储单位,同一列的所有数据在存储介质上连续存放。- // 还以三个人员信息举例,如下
- 姓名列: [张三, 李四, 王五, ...]
- 年龄列: [25, 30, 28, ...]
- 城市列: [北京, 上海, 广州, ...]
- 职位列: [工程师, 产品经理, 设计师, ...]
- 薪资列: [3000, 3500, 3200, ...]
- // 磁盘上存储为:
- 张三|李四|王五|...|25|30|28|...|北京|上海|广州|...
复制代码
这样的列式存储的特点:按列连续存储,每列独立成块;读取单列或部分列效率极高;适合批量写入,单行写入效率较低;压缩效率极高(同列数据类型相同);可通过向量化执行、SIMD指令等优化查询。
由于是按照列存储,每个数据值类型都是一样的,因此相当于天然索引,查询效率直线飙升。相同类型数据集中存储,CPU缓存命中率更高。
缺点:单行查询慢,读取完整记录需要跨多个列文件组装;写入操作复杂,单行插入/更新需要修改多个列文件,写放大明显;事务支持弱:ACID 事务实现复杂,通常不支持或支持有限。
典型应用场景:OLAP 系统(在线分析处理)、数据仓库、商业智能(BI)分析、大数据统计分析、时序数据处理。
代表数据库常见的有 ClickHouse、Vertica、Greenplum、Apache Kudu、MonetDB、Apache Parquet、Apache Druid 等。
三、行式和列式存储的区别和联系
3.1 两种存储方式的区别
对比维度行式存储列式存储存储单位行(Row)列(Column)数据布局同一行数据连续存储同一列数据连续存储读取整行⭐⭐⭐⭐⭐ 极快⭐⭐ 较慢读取单列⭐⭐ 较慢⭐⭐⭐⭐⭐ 极快聚合查询⭐⭐ 效率低⭐⭐⭐⭐⭐ 效率高(10-100倍)点查询⭐⭐⭐⭐⭐ 高效⭐⭐ 较低效批量写入⭐⭐⭐ 中等⭐⭐⭐⭐⭐ 高效单行写入⭐⭐⭐⭐⭐ 高效⭐⭐ 较低效数据压缩⭐⭐ 压缩率低⭐⭐⭐⭐⭐ 压缩率高(5-10倍)事务支持⭐⭐⭐⭐⭐ 完整 ACID⭐⭐ 有限或无I/O 效率读整行少 I/O,读列多 I/O读列少 I/O,读整行多 I/O适用场景OLTP(联机事务处理)OLAP(联机分析处理)
| 操作 | 行式存储 | 列式存储 | | 查询单行 | 只需一次 I/O 定位到该行 | 只需读取该列数据块,I/O 最小化 | | 查询单列 | 需要读取所有行的完整数据,I/O 浪费严重 | 需要读取所有列文件并组装,I/O 开销大 | | 示例 | 1 亿行数据,每行 100 列,查询 1 列需读取 100 倍不必要数据 | 同样 1 亿行数据,查询 1 列只需读取 1/100 的数据量 |
压缩因素行式存储列式存储数据类型一致性低(混合类型)高(单一类型)重复值集中度低高典型压缩比2-3倍5-10倍压缩算法适配通用压缩字典编码、游程编码等为什么列式存储的压缩效率比较高?
- 同一列数据类型相同,便于采用针对性压缩算法;
- 同一列数据值分布相似,重复值集中;
- 可使用字典编码、游程编码(RLE)、位图编码等高效压缩。
据资料可得到大概的结果:
查询类型行式存储耗时列式存储耗时性能提升SUM(单列)1000ms50ms20 倍AVG(单列)950ms45ms21 倍COUNT800ms40ms20 倍单行点查5ms50ms行存快 10 倍批量插入1000 行/s10000 行/s列存快 10 倍3.2 行式存储和列式存储的联系
首先是互补关系,而非替代关系。
正因为两者不可互相替代,现代数据库架构倾向于将它们结合使用,形成互补:
行列混合存储引擎(Hybrid Storage):
如 SAP HANA,Oracle In-Memory,ClickHouse(部分特性),TiDB。
这些系统在同一份数据上维护行式和列式两种视图,或者允许用户指定某些表/列使用行存,某些使用列存。
热数据(频繁读写)走行存路径保证事务速度;冷数据或分析请求自动路由到列存路径进行加速。
链路分离(ETL/ELT):
前端:业务系统使用行存数据库(如 MySQL,PostgreSQL)处理交易。
后端:通过同步工具将数据实时/准实时地复制到列存数据仓库(如 Snowflake, Doris, StarRocks)进行分析。
这是最经典的互补模式:行存负责“生产数据”,列存负责“消费数据”。
四、小小的总结
行式存储和列式存储的关系就像螺丝刀和锤子:不能用锤子去拧螺丝(列存做不了高频事务);也不能用螺丝刀去钉钉子(行存做不了高效分析)。
它们在数据处理的生命周期中扮演不同的角色:行存保障了业务的实时性和事务一致性,列存保障了决策分析的效率和成本。因此,它们是构建完整数据生态系统中不可或缺的互补组件。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |