跳转至

Vertica 分区表常见问题

原文:Vertica Partitions: The FAQs 说明:中文翻译 + 原文要点完整保留 + SQL 原文

概述

Vertica 的分区(Partitioning)功能根据一列或多列的值将大表分割成小块。分区可以有效简化数据生命周期管理,并加速包含分区键谓词的查询性能。

本文涵盖以下主题:

  1. 分区基础(Partition Basics)
  2. 分区与存储裁剪(Partitions and Storage Pruning)
  3. 分区与 ROS 文件及 ROS 容器(Partitions and ROS Files and ROS Containers)
  4. 重分区与重组(Repartitioning and Reorganizing)
  5. 分区数量限制(Partition Limit Considerations)

一、分区基础

分区工作原理

假设需要实现数据保留策略(如仅保留 5 年),传统方式需要逐行删除旧数据。Vertica 分区让这一过程变得高效。

示例:创建一个按年份分区的 trade 表

=> CREATE TABLE trade (
    tdate DATE NOT NULL,
    tsymbol VARCHAR(8) NOT NULL,
    ttime TIME)
    PARTITION BY EXTRACT(year FROM tdate);

加载数据时,Vertica 根据分区表达式(此处为日历年 EXTRACT(year FROM tdate))自动将数据分离到不同 ROS 容器中。

分区创建过程:

  1. 首次加载 2008 年数据 → Vertica 创建第一个分区(称为活跃分区 active partition),数据存储在 ROS 容器中
  2. 首次加载 2009 年数据 → Vertica 创建新分区作为活跃分区,旧分区变为非活跃分区(inactive partition)
  3. Tuple Mover 对非活跃分区合并所有 ROS 容器为一个 ROS 容器;对活跃分区使用 strata 算法合并

分区与分段(Segmentation)的区别

  • 分段(Segmentation):将数据均匀分配到集群各节点,利用 MPP 架构并行处理。通常用 HASH(trans_id) 进行分段。
  • 分区(Partitioning):在每个节点内部将数据组织到不同存储容器中,用于减少 I/O 和提升查询性能。

实践中,大表应同时使用分段和分区:

  • 分段:SEGMENTED BY HASH(trans_id) ALL NODES
  • 分区:PARTITION BY EXTRACT(month FROM trans_date)

活跃分区(Active Partitions)

活跃分区最后创建的分区(而非最后更新的分区),通常存放频繁加载的新数据。

可通过配置参数 ActivePartitionCount 调整活跃分区数量(默认为 1):

  • ActivePartitionCount=1:Tuple Mover 对非活跃分区合并为单个 ROS 容器,对活跃分区使用 strata 算法
  • ActivePartitionCount=2:最后两个创建的分区都被视为活跃分区,全部使用 strata 算法

增加 ActivePartitionCount 可减少 Tuple Mover 操作次数,但可能导致每个投影的 ROS 容器过多。

查询活跃分区:

=> SELECT DISTINCT partition_key FROM strata
   WHERE projection_name ILIKE '%sktest%'
   AND schema_name ILIKE '%public%';
partition_key
---------------
            5
            8
            9
(3 rows)

哪些表应该分区

Vertica 建议仅对大事实表(large fact tables)分区。不要对小表或维度表分区,否则会产生大量 ROS 容器,导致 catalog 膨胀并影响查询性能。

如何归档冷数据

可使用存储策略(Storage Policy)将历史分区迁移到低速廉价存储,为热分区腾出高速存储空间。参考 Vertica 文档 Creating Storage Policies

分区方案设计考虑

定义分区表达式时应考虑: 1. 数据保留策略 2. 常用的查询谓词 3. 分区粒度对每节点每投影的 ROS 容器数和总 ROS 文件数的影响

二、分区与存储裁剪

分区如何影响数据生命周期管理

Vertica 提供以下能力支持数据生命周期管理:

  • 删除分区(DROP_PARTITION)
  • 移动冷分区到归档表(MOVE_PARTITIONS_TO_TABLE)
  • 移动冷分区到廉价存储(Storage Policy)
  • 从归档恢复分区

什么是存储裁剪(Storage Pruning)

分区数据被隔离到独立的存储容器中。查询计划阶段,优化器根据每个容器的分区列 min/max 值,只扫描包含相关数据的容器,大幅减少 I/O。

四种场景分析:

查询条件 存储裁剪效果
查询特定某周/月的数据 裁剪剩余 35 个分区的存储容器(以按月分区、3 年数据为例)
查询一个季度(3 个月)的数据 仅使用 3 个分区的存储容器
谓词列与分区列无关(如 transaction_date 分区 + zip_code 过滤) 无效裁剪,数据可能跨所有分区
谓词列与分区列相关(如 transaction_date 分区 + ship_date 过滤) 有效裁剪,可缩小到相邻分区范围

如何确认查询利用了分区裁剪

Step 1:Profile 查询获取 transaction_id 和 statement_id

=> PROFILE SELECT * FROM <table_name> WHERE <column_name> BETWEEN 5 AND 7;
NOTICE 4788:  Statement is being profiled
HINT:  Select * from v_monitor.execution_engine_profiles where
transaction_id=54043195528458555 and statement_id=1;

Step 2:查询 QUERY_EVENTS 系统表

=> SELECT node_name, event_details FROM query_events
   WHERE event_type = 'PARTITIONS_ELIMINATED'
   AND transaction_id = 54043195528458555
   AND statement_id = 1;
    node_name     |                       event_details
------------------+-----------------------------------------------------------
v_vmart_node0003  | Using only 1 stores out of 3 for projection public.tab_b0
v_vmart_node0002  | Using only 1 stores out of 5 for projection public.tab_b0
v_vmart_node0001  | Using only 1 stores out of 2 for projection public.tab_b0
(3 rows)

Using only 1 stores out of N 表示存储裁剪生效。

三、分区与 ROS 文件及 ROS 容器

ROS 文件 vs ROS 容器

  • ROS 文件:执行 COPY DIRECT 时 Vertica 为每列创建一个 ROS 文件
  • ROS 容器:ROS 文件的逻辑分组,由 COPY DIRECT 或 Tuple Mover 操作创建

每投影每节点的 ROS 容器数量问题

Vertica 将分区数据隔离到不同存储容器中,且不跨分区合并数据。过多小分区可能导致存储容器数接近上限(1024),此时加载数据将失败并报 "Too many ROS containers"。

解决方案:

  1. 使用 ALTER_PARTITION 改为粒度更粗的分区方案
  2. 使用 MOVE_PARTITION 将冷分区移出

总 ROS 容器数对数据库的影响

总 ROS 文件数计算公式:(# storage containers) x (# columns per projection)

大量 ROS 文件会导致 catalog 膨胀(1M ROS 文件/节点约 3-4GB),消耗系统内存并拖慢以下操作:

  • 系统表查询
  • 数据库启动
  • 数据库备份
  • 故障恢复

Use Case 1:大型 Catalog

1,000 张表、每表 2 个投影、平均 50 列、50 ROS 容器/节点 → 约 500 万 ROS 文件/节点

若 300 张表按天分区(365 天),则增加约 1,090 万 ROS 文件。建议: - 仅少量大表按天分区,其余按周/月分区 - 合并相似表(避免从 OLTP 继承的按地理位置拆分设计)

Use Case 2:ROS 容器过多

100 张表、每表 2 投影、50 列、50 ROS 容器 → 50 万 ROS 文件

若 10 张表按天分区保留 3 年:1 表 x (365 x 3) = 1095 个非活跃分区 ROS 容器,超过 1024 上限,导致新数据无法加载。

若改为按周分区1 表 x (52 x 3) = 156 个非活跃分区 ROS 容器,大幅降低容器数。

四、重分区与重组

为已有表分区或修改分区表达式

=> ALTER TABLE <table_name> PARTITION BY <partition_expression>

执行后,已有存储容器的分区键信息立即清除,需要使用 REORGANIZE 关键字按新表达式重建。

注意: 节点宕机时不要执行 ALTER TABLE PARTITION。

REORGANIZE 的作用

=> ALTER TABLE <table_name> PARTITION BY <partition_expression> REORGANIZE;

该操作:

  1. 删除现有分区键
  2. 按新表达式重分区
  3. 重组织表数据

REORGANIZE 是 Tuple Mover 的变体操作,在后台分批读取数据,按新分区方案写入 ROS 容器并添加分区键,减少对数据库运行性能的影响。

延迟 REORGANIZE 的后果

延迟后会导致:

  1. 无法在已修改分区表达式但未重组(reorganized)的表上执行分区管理函数
  2. 无分区键的 ROS 容器不参与 Tuple Mover mergeout,可能导致 ROS pushback

应尽快执行 REORGANIZE,并监控其进度直到完成。

监控 REORGANIZE 进度

查询以下系统表:

  • VS_TUPLE_MOVER_OPERATIONS
  • PARTITION_STATUS
  • PARTITION_REORGANIZE_ERRORS

查询 CATALOG_EVENTS 表查看表的分区历史。

移除分区

=> ALTER TABLE <table_name> REMOVE PARTITIONING;

移除后 Vertica 将该表视为普通非分区表,使用 strata 算法合并 ROS 容器。

五、分区数量限制

单表分区数量上限

无显式分区数量限制,但 Vertica 限制每投影每节点最多 1024 个 ROS 容器,因此实际分区上限约为 1024。Vertica 也阻止单个 COPY DIRECT 语句加载超过 1024 个分区。

超过 365 个分区(约上限 1/3)时,应密切监控以下指标:

  • ROS 容器每投影计数
  • Tuple Mover mergeout 操作

必要时考虑:

  • 重新设计分区方案(如从按天改为按月/按周)
  • 将不常查询的分区移出到归档表

如何检查数据库 catalog 大小

=> SELECT node_name, MAX(ts) AS ts, MAX(catalog_size_in_MB) AS catalog_size_in_MB
   FROM (
       SELECT node_name,
           TRUNC((dc_allocation_pool_statistics_by_second."time")::TIMESTAMP, 'SS') AS ts,
           SUM((dc_allocation_pool_statistics_by_second.total_memory_max_value
               - dc_allocation_pool_statistics_by_second.free_memory_min_value))/(1024*1024)
               AS catalog_size_in_MB
           FROM dc_allocation_pool_statistics_by_second
           GROUP BY 1, TRUNC((dc_allocation_pool_statistics_by_second."time")::TIMESTAMP, 'SS')
   ) subquery_1
   GROUP BY 1 ORDER BY 1 LIMIT 50;

如何检查分区是否导致 catalog 过大

=> SELECT s.node_name, p.table_schema, s.projection_name,
   COUNT(DISTINCT s.storage_oid) storage_container_count,
   COUNT(DISTINCT partition_key) partition_count,
   COUNT(r.rosid) ros_file_count
   FROM storage_containers s
   LEFT OUTER JOIN PARTITIONS p ON s.storage_oid = p.ros_id
   JOIN vs_ros r ON r.delid = s.storage_oid
   GROUP BY 1,2,3 ORDER BY 4 DESC LIMIT 50;

该查询返回 ROS 容器数最多的前 50 个投影。如果结果中的表分区数很高,应考虑修改分区方案以减少分区数量。

常见操作命令速查

操作 SQL
创建分区表 CREATE TABLE ... PARTITION BY EXTRACT(year FROM col)
删除分区 SELECT DROP_PARTITION('table', 2008)
移动分区到归档 SELECT MOVE_PARTITIONS_TO_TABLE('trade', 2008, 2008, 'trade_archive')
查看分区信息 SELECT * FROM partitions WHERE table_name = 'trade'
修改分区 ALTER TABLE t PARTITION BY EXTRACT(month FROM tdate) REORGANIZE
移除分区 ALTER TABLE t REMOVE PARTITIONING
查询活跃分区 SELECT DISTINCT partition_key FROM strata WHERE ...
检查分区裁剪 查询 query_events WHERE event_type = 'PARTITIONS_ELIMINATED'

参考链接

扩展阅读