数据库查询优化中的混合存储格式(Hybrid Storage Formats)原理与实践
字数 2617 2025-12-15 03:57:33

数据库查询优化中的混合存储格式(Hybrid Storage Formats)原理与实践

一、 概述:什么是混合存储格式?

在传统的数据库存储中,我们通常面临一个经典的选择:行存储还是列存储?两者各有优劣:

  • 行存储(Row Store):将一行数据的所有列值连续存放。擅长OLTP(在线事务处理)场景,如按主键快速检索、插入、更新、删除整行数据。
  • 列存储(Column Store):将每一列的数据独立、连续存放。擅长OLAP(在线分析处理)场景,如对少数列进行聚合、过滤,因为只需要读取相关列,I/O效率高。

混合存储格式的核心理念是:打破行存和列存的二元对立,在一种物理存储格式中同时融合两者的优点,以更好地应对日益普遍的HTAP(混合事务/分析处理)负载。

二、 为什么需要混合存储格式?

现代应用负载日益复杂,一个典型的例子是:

  • 需要实时处理用户订单(OLTP)。
  • 同时需要对所有订单进行即时分析,生成实时报表(OLAP)。

传统方案(如分别使用行存OLTP库和列存数据仓库)会带来数据延迟、架构复杂、维护成本高等问题。混合存储格式的目标是在一个数据库引擎内同时高效地服务两类负载,其挑战在于如何巧妙地组织数据物理布局。

三、 核心设计原理:数据块的内部组织

混合存储格式的本质是对“数据块”内部的物理组织方式进行创新。一个基本思路是:将逻辑上的“表”在物理上分割成块,在每个块内部,采用混合布局

常见的设计模式:

  1. PAX(Partition Attributes Across)布局

    • 在一个物理数据页(Page)内部,先按行进行分片(例如,每页存储N行)。
    • 关键步骤:在每一页内部,再将这N行数据按列重新组织。即,将页内所有行的第一列值连续存放,然后是所有行的第二列值,以此类推。
    • 优点:保持了页作为I/O和内存管理的基本单位(利于OLTP的页面读写),同时在页内获得了类似列存的局部性,提高了CPU缓存利用率和扫描效率(利于OLAP的列式扫描)。
    • 直观比喻:一页书(数据页)上有多行记录。PAX的做法是将这一页上所有人的“姓名”抄在一起,所有人的“年龄”抄在一起...但在书的总目录上,它还是一个页一个页地管理。
  2. 行列混合块(Row-Column Hybrid Block)

    • 将表水平分区成多个“块”(Block)。
    • 每个块可以独立选择存储格式。例如:
      • 热数据块(近期频繁更新的数据)采用行存储。
      • 冷数据块(历史只读数据)采用列存储。
    • 系统可以自动或手动(通过策略)将数据从“行存块”迁移到“列存块”。
    • 优点:在时间维度上优化,新数据用行存保证事务性能,旧数据转列存提升分析性能。
  3. 宽列存储(Wide-Column Store)的变体

    • 在一些NoSQL系统(如Cassandra、HBase)中,概念模型是“列族”。一个行键下的数据被分组到不同的列族,每个列族内的数据按列独立存储。这可以看作一种混合:行键访问是“行式”的,而列族内的查询是“列式”的。

四、 关键技术挑战与解决方案

  1. 更新处理
    • 纯列存对更新(尤其是单行更新)不友好,因为一列数据分散在多个位置。
    • 混合格式的解法
      • Delta Store:为行存块或列存块维护一个小的、行格式的“增量存储区”,用于暂存最近的插入、更新和删除操作。后台进程定期将增量区与主存储合并。
      • 标记删除与延迟合并:对于删除,先在行存区域或位图中标记,定期进行物理清理。
  2. 索引支持
    • 混合格式需要支持高效的点查询(通过主键或索引找到一行)。
    • 解法:为行存块或整个表维护传统的B+树索引。对于列存块,可以为关键列建立单独的稀疏索引或区域映射(Zone Map),以快速定位数据块。
  3. 查询优化与执行
    • 优化器需要知道数据的物理布局。例如,一个查询如果只涉及少数列且扫描大量历史数据,优化器应优先选择访问列存块;如果一个查询通过主键检索单行,则应访问行存块或增量存储区。
    • 解法:在表的元数据中记录每个数据块的存储格式、统计信息和数据范围。优化器基于此选择访问路径,执行引擎也需要适配,能够无缝处理来自行存块和列存块的数据流。

五、 实际应用与举例

以一个电商订单表 orders(order_id, customer_id, product_id, amount, order_date) 为例,说明混合存储格式的工作流程:

  1. 数据写入与存储

    • 系统配置策略:订单产生后7天内为“热数据”,存储于行存块;7天后自动转为“冷数据”,迁移至列存块
    • 新订单 order_id=1001 插入时,直接进入一个行存格式的数据页。
    • 当后台合并任务触发时,会将满足条件的行存块(如日期超过7天)转换为列存格式(例如PAX布局),压缩后存储。
  2. 查询处理

    • 查询A(OLTP)SELECT * FROM orders WHERE order_id = 1001;
      • 优化器通过主键索引,直接定位到该行所在的数据块(大概率在行存块),高效读取整行返回。
    • 查询B(OLAP)SELECT customer_id, SUM(amount) FROM orders WHERE order_date >= '2023-01-01' GROUP BY customer_id;
      • 优化器分析谓词 order_date >= '2023-01-01',发现绝大部分数据位于列存块。
      • 它决定只扫描 customer_idamount 这两列在相关列存块中的数据。由于列存数据是连续且按列组织的,I/O量大大减少,且利于向量化执行。
      • 对于近期可能存在的、仍在行存块中的少量数据,查询可能会并行地同时扫描行存块(读取整行但只提取所需两列),或等待增量合并,最终将两部分结果合并。

六、 总结

混合存储格式不是一种单一的技术,而是一类旨在统一行存与列存优势的存储架构设计思想。其核心在于:

  • 在微观层面(如页内),通过PAX等布局改善缓存利用率和扫描性能。
  • 在宏观层面(如表内),通过行列混合块、自动分层等策略,让数据在其生命周期内动态选择最合适的物理格式。

它代表了数据库存储引擎发展的一个重要方向,使单一数据库系统能够在不牺牲事务性能的前提下,原生地支持高效的分析查询,是HTAP架构得以实现的重要基石。

数据库查询优化中的混合存储格式(Hybrid Storage Formats)原理与实践 一、 概述:什么是混合存储格式? 在传统的数据库存储中,我们通常面临一个经典的选择: 行存储 还是 列存储 ?两者各有优劣: 行存储 (Row Store):将一行数据的所有列值连续存放。擅长OLTP(在线事务处理)场景,如按主键快速检索、插入、更新、删除整行数据。 列存储 (Column Store):将每一列的数据独立、连续存放。擅长OLAP(在线分析处理)场景,如对少数列进行聚合、过滤,因为只需要读取相关列,I/O效率高。 混合存储格式 的核心理念是: 打破行存和列存的二元对立,在一种物理存储格式中同时融合两者的优点 ,以更好地应对日益普遍的HTAP(混合事务/分析处理)负载。 二、 为什么需要混合存储格式? 现代应用负载日益复杂,一个典型的例子是: 需要实时处理用户订单(OLTP)。 同时需要对所有订单进行即时分析,生成实时报表(OLAP)。 传统方案(如分别使用行存OLTP库和列存数据仓库)会带来数据延迟、架构复杂、维护成本高等问题。混合存储格式的目标是 在一个数据库引擎内 同时高效地服务两类负载,其挑战在于如何巧妙地组织数据物理布局。 三、 核心设计原理:数据块的内部组织 混合存储格式的本质是对“数据块”内部的物理组织方式进行创新。一个基本思路是: 将逻辑上的“表”在物理上分割成块,在每个块内部,采用混合布局 。 常见的设计模式: PAX(Partition Attributes Across)布局 : 在一个物理数据页(Page)内部,先按行进行分片(例如,每页存储N行)。 关键步骤 :在每一页内部,再将这N行数据按列重新组织。即,将页内所有行的第一列值连续存放,然后是所有行的第二列值,以此类推。 优点 :保持了页作为I/O和内存管理的基本单位(利于OLTP的页面读写),同时在页内获得了类似列存的局部性,提高了CPU缓存利用率和扫描效率(利于OLAP的列式扫描)。 直观比喻 :一页书(数据页)上有多行记录。PAX的做法是将这一页上所有人的“姓名”抄在一起,所有人的“年龄”抄在一起...但在书的总目录上,它还是一个页一个页地管理。 行列混合块(Row-Column Hybrid Block) : 将表水平分区成多个“块”(Block)。 每个块可以独立选择存储格式。例如: 热数据块 (近期频繁更新的数据)采用行存储。 冷数据块 (历史只读数据)采用列存储。 系统可以自动或手动(通过策略)将数据从“行存块”迁移到“列存块”。 优点 :在时间维度上优化,新数据用行存保证事务性能,旧数据转列存提升分析性能。 宽列存储(Wide-Column Store)的变体 : 在一些NoSQL系统(如Cassandra、HBase)中,概念模型是“列族”。一个行键下的数据被分组到不同的列族,每个列族内的数据按列独立存储。这可以看作一种混合:行键访问是“行式”的,而列族内的查询是“列式”的。 四、 关键技术挑战与解决方案 更新处理 : 纯列存对更新(尤其是单行更新)不友好,因为一列数据分散在多个位置。 混合格式的解法 : Delta Store :为行存块或列存块维护一个小的、行格式的“增量存储区”,用于暂存最近的插入、更新和删除操作。后台进程定期将增量区与主存储合并。 标记删除与延迟合并 :对于删除,先在行存区域或位图中标记,定期进行物理清理。 索引支持 : 混合格式需要支持高效的点查询(通过主键或索引找到一行)。 解法 :为行存块或整个表维护传统的B+树索引。对于列存块,可以为关键列建立单独的稀疏索引或区域映射(Zone Map),以快速定位数据块。 查询优化与执行 : 优化器需要知道数据的物理布局。例如,一个查询如果只涉及少数列且扫描大量历史数据,优化器应优先选择访问列存块;如果一个查询通过主键检索单行,则应访问行存块或增量存储区。 解法 :在表的元数据中记录每个数据块的存储格式、统计信息和数据范围。优化器基于此选择访问路径,执行引擎也需要适配,能够无缝处理来自行存块和列存块的数据流。 五、 实际应用与举例 以一个电商订单表 orders(order_id, customer_id, product_id, amount, order_date) 为例,说明混合存储格式的工作流程: 数据写入与存储 : 系统配置策略:订单产生后7天内为“热数据”,存储于 行存块 ;7天后自动转为“冷数据”,迁移至 列存块 。 新订单 order_id=1001 插入时,直接进入一个行存格式的数据页。 当后台合并任务触发时,会将满足条件的行存块(如日期超过7天)转换为列存格式(例如PAX布局),压缩后存储。 查询处理 : 查询A(OLTP) : SELECT * FROM orders WHERE order_id = 1001; 优化器通过主键索引,直接定位到该行所在的数据块(大概率在行存块),高效读取整行返回。 查询B(OLAP) : SELECT customer_id, SUM(amount) FROM orders WHERE order_date >= '2023-01-01' GROUP BY customer_id; 优化器分析谓词 order_date >= '2023-01-01' ,发现绝大部分数据位于列存块。 它决定只扫描 customer_id 和 amount 这两列在相关列存块中的数据。由于列存数据是连续且按列组织的,I/O量大大减少,且利于向量化执行。 对于近期可能存在的、仍在行存块中的少量数据,查询可能会并行地同时扫描行存块(读取整行但只提取所需两列),或等待增量合并,最终将两部分结果合并。 六、 总结 混合存储格式不是一种单一的技术,而是一类 旨在统一行存与列存优势的存储架构设计思想 。其核心在于: 在微观层面(如页内) ,通过PAX等布局改善缓存利用率和扫描性能。 在宏观层面(如表内) ,通过行列混合块、自动分层等策略,让数据在其生命周期内动态选择最合适的物理格式。 它代表了数据库存储引擎发展的一个重要方向,使单一数据库系统能够在不牺牲事务性能的前提下,原生地支持高效的分析查询,是HTAP架构得以实现的重要基石。