列式存储详解

行式存储

传统的关系型数据库,如 Oracle、DB2、MySQL、SQL SERVER 等采用行式存储法(Row-based),在基于行式存储的数据库中, 数据是按照行为基础逻辑存储单元进行存储的, 一行中的数据在存储介质中以连续存储的形式存在。

适用场景:

  • 适合随机的增删改查操作;
  • 需要在行中选取所有属性的进行查询操作;
  • 对事务要求较高的系统;
  • 需要频繁插入或更新的操作,其操作与索引和行的大小更为相关

列式存储

列式存储(Column-based)是相对于行式存储来说的,比如:Hbase、Doris、Clickhouse等分布式数据库均采用列式存储。在基于列式存储的数据库中, 数据是按照列为基础逻辑存储单元进行存储的,一列中的数据在存储介质中以连续存储形式存在。

适用场景:

  • 在线分析处理系统(OLAP),如数据仓库、商业智能(BI)系统、报表系统等
  • 大数据分析
  • 只读和数据归档,数据在写入后不经常修改,主要用于读取和分析

特点

行式存储

列式存储

存储结构

每行数据存储在一起

每列数据存储在一起

优点

快速插入、更新、删除操作

快速扫描和聚合操作

缺点

对大规模扫描和聚合操作不利

对频繁的插入、更新、删除操作不利

适用场景

OLTP系统、实时查询、复杂事务

OLAP系统、大数据分析、数据归档

 

行式数据库在读取数据的时候会存在一个固有的“缺陷”,比如,所选择查询的目标即使只涉及少数几项属性,但由于这些目标数据在各行数据单元中,而行单元往往又特别大,应用程序必须读取每一条完整的行记录,从而使得读取效率大大降低。对此,行式数据库给出的优化方案是加“索引”。在OLTP类型的应用中,通过索引机制或给表分区等手段,可以简化查询操作步骤,并提升查询效率。

但针对海量数据背景的OLAP应用(例如分布式数据库、数据仓库等等),行式存储的数据库就有些吃力了,行式数据库建立索引和物化视图,需要花费大量时间和资源,因此还得不偿失,无法从根本上解决查询性能和维护成本等问题,也不适用于数据仓库等应用场景,所以后来出现了基于列式存储的数据库。

对于数据仓库和分布式数据库来说,大部分情况下它会从各个数据源汇总数据。然后进行分析和反馈。其操作大多是围绕同一列属性的数据进行的,而当查询某属性的数据记录时,列式数据库只需返回与列属性相关的值,在大数据量查询场景中,列式数据库可在内存中高效组装各列的值,最终形成关系记录集,因此可以显著减少IO消耗,并降低查询响应时间,非常适合数据仓库和分布式的应用。

列式存储查询过程

1. 数据定位

首先,列式存储引擎会根据查询条件定位需要读取的列。例如,查询 SELECT Name, Salary FROM employees WHERE Age > 30

2. 读取所需列

引擎只读取 NameSalary 列的数据,不读取其他列。这显著减少了读取的数据量,提高了查询效率。

3. 应用查询条件

引擎应用查询条件 Age > 30,仅筛选出符合条件的行。例如,在列式存储中,读取 Age 列并找到满足条件的行号(或偏移量)

4. 合并结果

引擎将读取到的数据按照查询条件进行合并,返回最终结果。

列式存储高效的关键点

有几大关键的优化点:

  • 向量化查询处理(vectorized query processing)、
  • 压缩(compression)、
  • 隐式连接(invisible join)、
  • 延迟物化(late materialization)
延迟物化

要理解延迟物化(Late Materialization), 首先解释一下什么是物化:为了能够把底层存储格式(面向Column的), 跟用户查询表达的意思(Row)对应上,在一个查询的生命周期的某个时间点,一定要把数据转换成Row的形式,这在Column-Store里面被称为物化(Materization)。

物化的时机尽量的拖延到整个查询生命周期的后期。

压缩技术

列式存储通常使用高效的压缩算法,因为同一列的数据类型相同,重复数据较多,压缩率高,

数据占用的硬盘空间越小,查询引擎花在IO上的时间就越少(不管是从硬盘里面把数据读入内存,还是从内存里面把数据读入CPU)。同时要记住的是数据压缩之后,要进行处理很多时候要需要解压缩(不管是Column-Store还是Row-Store), 因此压缩比不是我们追求的唯一,因为后面解压也需要花时间,因此一般会在压缩比和解压速度之间做一个权衡。

向量化查询

向量化查询引擎处理多个数据元素,可以利用 CPU 指令集中的 SIMD(Single Instruction, Multiple Data)特性,大幅提升查询速度。

有一点不太理解:age列是单独存储的,当查询Age > 30时,会在age列上查询,那如何对其他列如name、salary根据age做筛选查询的?

答:

在列式存储中,虽然每一列的数据是单独存储的,但列与列之间的数据是通过相同的行索引(或行号)关联在一起的。查询过程中,数据引擎会利用这些行索引来合并结果。下面是详细的步骤解释,说明如何在列式存储中根据一个列的条件(如 Age > 30)筛选其他列(如 NameSalary)。

  1. 扫描条件列: 首先,扫描条件列 Age。在列式存储中,这一列的数据是连续存储的,因此可以高效地进行扫描。例如,找到所有 Age > 30 的行。

  2. 获取匹配行的索引: 记录所有满足条件的行的索引(或行号)。例如,如果第 1 和第 3 行的 Age 大于 30,则记录这些行号 [1, 3]

  3. 扫描目标列: 然后,扫描目标列 NameSalary。同样,这些列的数据是连续存储的。

  4. 应用行索引过滤: 使用前面记录的行索引,从 NameSalary 列中提取对应的行数据。例如,提取 NameSalary 中第 1 和第 3 行的数据。

  5. 合并结果: 最后,将提取的数据合并成最终的结果集。


 

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值