Vertica 分区表常见问题¶
原文:Vertica Partitions: The FAQs 说明:中文翻译 + 原文要点完整保留 + SQL 原文
概述¶
Vertica 的分区(Partitioning)功能根据一列或多列的值将大表分割成小块。分区可以有效简化数据生命周期管理,并加速包含分区键谓词的查询性能。
本文涵盖以下主题:
- 分区基础(Partition Basics)
- 分区与存储裁剪(Partitions and Storage Pruning)
- 分区与 ROS 文件及 ROS 容器(Partitions and ROS Files and ROS Containers)
- 重分区与重组(Repartitioning and Reorganizing)
- 分区数量限制(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 容器中。
分区创建过程:
- 首次加载 2008 年数据 → Vertica 创建第一个分区(称为活跃分区 active partition),数据存储在 ROS 容器中
- 首次加载 2009 年数据 → Vertica 创建新分区作为活跃分区,旧分区变为非活跃分区(inactive partition)
- 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"。
解决方案:
- 使用 ALTER_PARTITION 改为粒度更粗的分区方案
- 使用 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 容器,大幅降低容器数。
四、重分区与重组¶
为已有表分区或修改分区表达式¶
执行后,已有存储容器的分区键信息立即清除,需要使用 REORGANIZE 关键字按新表达式重建。
注意: 节点宕机时不要执行 ALTER TABLE PARTITION。
REORGANIZE 的作用¶
该操作:
- 删除现有分区键
- 按新表达式重分区
- 重组织表数据
REORGANIZE 是 Tuple Mover 的变体操作,在后台分批读取数据,按新分区方案写入 ROS 容器并添加分区键,减少对数据库运行性能的影响。
延迟 REORGANIZE 的后果¶
延迟后会导致:
- 无法在已修改分区表达式但未重组(reorganized)的表上执行分区管理函数
- 无分区键的 ROS 容器不参与 Tuple Mover mergeout,可能导致 ROS pushback
应尽快执行 REORGANIZE,并监控其进度直到完成。
监控 REORGANIZE 进度¶
查询以下系统表:
- VS_TUPLE_MOVER_OPERATIONS
- PARTITION_STATUS
- PARTITION_REORGANIZE_ERRORS
查询 CATALOG_EVENTS 表查看表的分区历史。
移除分区¶
移除后 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' |
参考链接¶
- Vertica 文档:Using Table Partitions
- Vertica 文档:Partition Management Functions
- Vertica 监控最佳实践(分区监控部分)
扩展阅读¶
- Vertica 表分区策略选择指南 — 分区决策框架与 ROS 容器数控制
- ROS 绑定最佳实践
- Tuple Mover 最佳实践
- 删除操作最佳实践