01 列式存储
在列式存储下,列 C1 的数据在磁盘上聚簇地存储在数据块(或者页),但是一列可以由多个数据块或者数据文件存储。
在行式存储下,行 R1 的数据在磁盘上聚簇地存储在数据块上。
OLTP 场景
数据一般以行的形式进行处理与查询,一行数据通常被一起插入、一起查询、一起更新,这样,使用行式存储就能够以最少的磁盘读写代价处理一行业务数据的读写。
关系型数据库需要支持事务,一行数据或者多行数据需要能够一起持久化成功或者一起失败,按行存储能够简化读写的 I/O,提升性能。
MySQL 这种关系型数据库数据的写入并不是直接发生在磁盘上的。它的更新操作首先是将数据写入内存,并同时生成一条 Redo 日志记录。这种日志记录机制保证了数据的持久性和可恢复性,用于故障时重做来恢复数据。当达到特定的刷新阈值时,如事务提交或日志缓冲区满,内存中的数据才会被写入磁盘。
因此,即使在事务的上下文中,数据的写入或更新也不是立即映射到磁盘上的行式存储结构中的。这个过程是由 InnoDB 存储引擎内部的缓冲和日志刷新机制决定的,能够有效地提高事务处理的性能。
OLAP场景
同样的订单表,我们关注的是销售额怎么样,哪些商品品类卖得比较好等。这时候通常是按日期统计一下订单金额字段的总和。
如果使用列式存储,同一列的值存储在一起,假设某天的销售额有 10000 个订单需要统计,那么可能 5000 个订单的订单金额列存储在一起,订单金额这一列就只占用 2 个数据块,这样只需要扫描这 2 个数据块,将扫描到的数据直接累加,就得到了销售额。
反之,如果使用行式存储,一个数据块即使能够存储 1000 行数据,那么也要扫描 10000/1000=10 个数据块,扫描完全部数据,从数据行中挑出订单金额的列,才能累加得到订单金额的汇总。
分析型数据库一般都是批量写入数据,同一列数据一起批量写入,这一列的数据类型相同,所以具备更高的压缩率,可以加快数据的读写速度。
OLAP 场景一般需要扫描大量数据行,但是基本是对一列或者多列进行统计分析、聚合等,列式存储可以只读取所需的列,从而避免加载整个行的数据,这大大减少了 I/O 操作,提高了查询效率。
OLTP 像单个业务执行者,快速的处理单个事务以及有限条数据,确保业务顺畅进行;而 OLAP 就像是战略分析家,深入剖析数据,为决策提供支持。
Parquet 一种存储格式
我们将要介绍的几个数据库也都与 Parquet 有千丝万缕的关联,比如 HBase 数据可以导出为 Parquet,然后用 Spark 等工具来进行批量分析。ClickHouse 与 StarRocks 都支持直接读取与写入 Parquet 数据。
实际上,Parquet 不单单是一个数据库的存储格式,它同样能够用于大规模的数据分析与处理,比如与 Spark、Flink 等集成,也支持直接用 Python、Java 等语言来读写,甚至你可以将自己的数据写成一个 Parquet 文件,然后使用支持 Parquet 格式的引擎来分析你的数据。