Vertica 弹性伸缩功能介绍与配置¶
作者:JiangChong | 发布时间:2026-05-15
适用场景框:当你需要根据业务负载变化动态调整集群计算资源——包括增加节点应对高峰、缩减节点节约成本、或在不同子集群间隔离工作负载——本文提供从原理到实操的完整指南。
关联文章:
- Vertica Eon 模式中分片、节点和 Depot 选择的最佳实践 — Eon 模式分片与节点规划
- Vertica 集群 Rebalance 完全指南 — Enterprise 模式 rebalance 的原理与操作细节
- Vertica 节点与集群规模规划指南 — 硬件选型与初始规模确定
- Vertica 冷热数据管理与成本优化 — Eon 模式弹性缩减计算资源配合冷数据策略
- Vertica 客户端连接负载均衡配置 — 子集群级别的连接路由与负载均衡
理解全文脉络: 第 1 节解释两种模式(Enterprise vs Eon)下弹性伸缩的底层原理差异;第 2 节介绍监控手段;第 3 节讨论如何判断是否需要伸缩;第 4 节给出两种模式的具体操作步骤(这是本文核心);第 5 节通过真实案例展示生产环境实操,第 6 节提供快速诊断 SQL 工具箱,第 7 节总结最佳实践清单。如果你已有明确的操作目标,可直接跳到第 4 节按模式选择对应操作流程;如果你想先理解原理再动手,建议从头读起。
1. 原理理解¶
1.1 什么是弹性伸缩¶
弹性伸缩(Elastic Scaling) 指数据库集群根据负载需求动态调整计算资源(节点数量)的能力。与传统的「一次性规划硬件、长期固定配置」不同,弹性伸缩让集群可以:
- 横向扩展(Scale Out):增加节点以承载更大的数据量或更高的查询并发
- 横向缩减(Scale In):减少节点以节约成本(尤其适合云环境按需付费模式)
- 工作负载隔离:通过子集群(Subcluster)将不同负载路由到独立的计算资源组
在 Vertica 中,弹性伸缩的实现方式取决于数据库的运行模式:Enterprise 模式和 Eon 模式有根本性的架构差异,下面的表格总结了核心区别。
1.2 Enterprise vs Eon 模式:架构差异决定伸缩方式¶
| 维度 | Enterprise 模式 | Eon 模式 |
|---|---|---|
| 数据存储位置 | 每个节点本地磁盘 | 公共存储(S3/MinIO/HDFS),节点本地 Depot 做缓存 |
| 计算与存储 | 耦合(每节点既存数据也算数据) | 分离(公共存储存数据,节点只做计算) |
| 扩容机制 | 添加节点 → 配置弹性集群 → rebalance 重分布数据 | 添加节点到子集群 → 无需 rebalance(数据已在公共存储中) |
| 缩容机制 | rebalance 将数据迁走 → 移除节点 | 排空(drain)节点 → 移除 → 无需 rebalance |
| 扩容耗时 | 小时~天级(取决于数据量) | 分钟级 |
| 缩容耗时 | 小时~天级 | 分钟级(排空活跃会话后即可移除) |
| 弹性集群支持 | ✅ 通过 ENABLE_ELASTIC_CLUSTER() + SET_SCALING_FACTOR() |
✅ 原生支持:通过子集群机制实现,无需 rebalance |
| 停机要求 | rebalance 期间 DDL 受限制,生产环境通常需停服 | 零停机(节点排空期间新连接路由到其他节点) |
通俗比喻:Enterprise 模式就像一栋固定隔断的办公楼——每层(节点)有自己的办公室(数据),要增加工位必须重新砌墙(rebalance)。Eon 模式则像一个共享云盘办公空间——工位(节点)只是用来操作文件的窗口,文件都存在云端(公共存储),增加或撤走工位不需要移动文件。
1.3 Enterprise 模式的弹性伸缩原理¶
Enterprise 模式下,每个节点存储数据的一部分(通过 segmentation 分布)。添加节点时,新节点没有数据,必须通过 rebalance 过程将现有节点上的数据重分布。
核心概念:Scaling Factor(伸缩因子)
SCALING_FACTOR 决定了 rebalance 后每个 projection 被拆分为多少个存储容器(storage container)。取值 1~32。
SCALING_FACTOR = N表示将数据预先拆分为当前节点数的 N 倍份- 通用公式:
SCALING_FACTOR = T ÷ GCD(N, T),其中 N=当前节点数,T=扩容后节点数 - 快速规划方法(给定 N=当前节点数,M=可新增服务器数):
- 列出 N 的所有因数,从大到小排序
- 对每个因数 d,在 [N+1, N+M] 范围内找 d 的最小倍数 T = k·d
- 第一个命中的因数 d → T 即最优目标,factor = T÷d
- 本质:在范围内找 GCD(N, T) 最大的 T,等价于从 N 的最大因数开始匹配,一步到位,不需要遍历所有候选
- 小集群成倍扩容(最简单场景):
- 3→6:GCD(3,6)=3 → factor=6÷3=2
- 3→9:GCD(3,9)=3 → factor=9÷3=3
- 6→12:GCD(6,12)=6 → factor=12÷6=2
- 大集群规划示例:N=90,M=20
- 90 的因数:18, 15, 10, 9, 6, 5, 3, 2, 1
- 因数 18:18×6=108 ∈ [91,110] ✅ → 扩容到 108(用 18 台),factor=6
- 不成倍且 factor 过大:GCD(N,T) 小则 factor 大,本地分段文件数暴增。例:90→95(GCD=5,factor=19)虽然 factor 在 1~32 范围内可行,但比最优 90→108(factor=6)差了 3 倍。90→91 则 GCD=1 导致 factor=91 > 32 无法设置——这也说明找不到小 factor 时扩容方案本身就不成立
- 例如 3 节点集群、
SCALING_FACTOR = 2:数据在逻辑上被分为 3×2=6 段,物理上分布在 3 个节点 - 扩容到 6 节点时:6 段数据重新分布到 6 个节点(每节点 1 段),数据只需在现有节点间转移,无需再次拆分 segment
上限约束:存储容器数 = 分区数 × SCALING_FACTOR,不能超过
ContainersPerProjectionLimit(默认 1024)。例如一张表有 100 个分区,SCALING_FACTOR = 32时容器数 = 3200 > 1024,会触发 ROS pushback。设置 factor 时要同时考虑分区数——分区多的表,factor 不能太大。为什么 Scaling Factor 重要? segment 拆分是 rebalance 最耗时的阶段(可占 80% 总耗时,参见 Vertica 集群 Rebalance 完全指南)。设置合适的 scaling factor 虽然会在当前增加文件数,但能大幅减少未来扩容的 rebalance 耗时。
Elastic Cluster 工作机制:
ENABLE_ELASTIC_CLUSTER()激活弹性集群模式,允许数据库动态增减节点SET_SCALING_FACTOR(N)设定伸缩因子,决定每个 projection 拆分为多少个存储容器- 添加节点后,执行
REBALANCE_CLUSTER()将数据按 segment → node 的映射重新分布 - 可选:
ENABLE_LOCAL_SEGMENTS()让数据始终以容器形式存储(本地分段),使未来 rebalance 更快,代价是文件数增加
关键限制:Enterprise 模式的 rebalance 是一个 CPU、磁盘和网络密集型 操作,期间 DML 操作被锁阻塞。生产环境扩容通常需要 数小时到数十小时 的停服窗口。
1.4 Eon 模式的弹性伸缩原理¶
Eon 模式实现了真正的计算-存储分离,节点不持有数据,只持有计算能力。
- 所有数据持久化在公共存储(S3/HDFS/MinIO)
- 每个节点上有一个 Depot(本地 SSD 缓存),用于缓存常用数据以提高性能
- 添加新节点:节点加入子集群后,需执行
REBALANCE_SHARDS()将 shard 订阅重新分布到新节点,使各节点均匀分担。节点从公共存储读取数据填充自己的 Depot → 无需 rebalance - 移除节点:将其排空(等待活跃查询完成)后直接移除 → 无需 rebalance
子集群(Subcluster)机制:
Eon 模式支持创建多个子集群,每个子集群是一组节点的逻辑集合。这使得「弹性」不仅是加减节点,还包括工作负载级别的伸缩:
- 主子集群(Primary Subcluster):构成数据库核心,只有 primary 子集群的节点参与 K-safety 和 quorum 判定。数据库保持运行的条件:半数以上 primary 节点存活 且 每个 shard 至少有一个 primary 节点订阅。也负责 Tuple Mover mergeout 的计划调度。通常保持固定规模,不做动态伸缩
- 次子集群(Secondary Subcluster):弹性伸缩的主力,不参与 K-safety 和 quorum 判定——可以随时创建、启停、删除,不影响数据库稳定性。节点可以执行 mergeout(如果 primary 节点分配任务),但不主动计划。适合查询工作负载的弹性伸缩
- 沙箱子集群(Sandbox Subcluster):从 secondary 子集群分离出的独立环境。创建时继承主集群的 catalog 和数据状态,之后各集群独立演进、互不影响(删除表、加载库等操作不会跨集群传播)。典型用途:版本升级测试、新功能实验、向其他团队共享数据。完成 unsandbox 后可重新加入主集群
双主子集群的注意事项:如果集群有两个主子集群,不能直接关闭其中一个——这会导致整个数据库进入只读状态。正确做法是先
DEMOTE_SUBCLUSTER_TO_SECONDARY()降级,再关闭。详见 Eon 双主子集群的注意事项。
Eon 弹性伸缩的两种目标:
| 目标 | 策略 | 原理 |
|---|---|---|
| 提升查询吞吐(更多并发) | 新增子集群 | 每个子集群独立处理查询,子集群间不共享计算。通过连接负载均衡将客户端分发到各子集群 |
| 提升查询性能(单查询更快) | 向现有子集群增加节点 | 当节点数超过 shard 数时,触发 Elastic Crunch Scaling(ECS):多个节点订阅同一 shard,优化器将 shard 数据处理责任拆分给各订阅节点,所有节点均可参与查询 |
最佳实践:
- 子集群节点数应为 shard 数的因子或相等。例如 12 shard → 子集群配 3、4、6 或 12 节点
- ECS 场景(节点数 > shard 数):节点数应为 shard 数的倍数,确保均匀分布。例如 3 shard → 6、9 或 12 节点
- K=1 时子集群至少需要 3 个节点才能维持冗余 shard 覆盖
- 利用 secondary 子集群做弹性伸缩:高峰期启动,低谷期关闭,不影响数据库稳定性
- ECS 效率低于原生 shard:6 节点在 3-shard 数据库(ECS)中的性能不如 6 节点在 6-shard 数据库中的性能。如果长期需要更高并行度,考虑
RESHARD_DATABASE()增加 shard 数
1.4.1 Elastic Crunch Scaling(ECS)详解¶
ECS 在节点数 > shard 数时自动触发,有两种底层策略决定如何拆分 shard 数据处理责任:
| 策略 | 原理 | 效果 |
|---|---|---|
IO_OPTIMIZED |
将 shard 中的 ROS 容器列表分给各订阅节点,每个节点只从公共存储拉取自己负责的容器 | 不保留数据 segmentation → 不支持 local join 等优化 |
COMPUTE_OPTIMIZED |
使用数据 segmentation 划分,节点扫描整个 shard 但通过 sub-segment 过滤只处理自己的部分 | 保留 segmentation → 支持 local join 等优化 |
默认行为 AUTO:
默认情况下配置为 AUTO,优化器按以下逻辑自动选择:
- 查询依赖数据 segmentation(有 JOIN / GROUP BY) → 选
COMPUTE_OPTIMIZED - 查询不依赖 segmentation(简单扫描、聚合) → 选
IO_OPTIMIZED
可用
EXPLAIN查看当前查询实际使用的策略: -「Crunch scaling strategy does not preserve data segmentation」→ IO_OPTIMIZED -「Crunch scaling strategy preserves data segmentation」→ COMPUTE_OPTIMIZED
什么时候需要手动选择?
大多数情况下 AUTO 就够了。但在以下场景,优化器的选择可能不是最优:
| 情况 | AUTO 的选择 | 问题 | 应手动改为 |
|---|---|---|---|
| 查询有 JOIN,但数据不在 Depot 中 | COMPUTE_OPTIMIZED | 节点需要从 S3 拉取全部 shard 数据再做 sub-segment 过滤,网络开销大 | IO_OPTIMIZED |
| 查询有 JOIN,数据已在 Depot 中 | 通常选对(COMPUTE) | — | 无需改动 |
| 简单扫描,但数据已在 Depot 中 | IO_OPTIMIZED | 白白放弃了 local join 优化(虽当前查询不需要,但某些场景仍有影响) | COMPUTE_OPTIMIZED |
| ECS 对此查询反而拖慢 | 任意 | ECS 增加了协作开销,收益却很小 | NONE(禁用 ECS) |
手动配置方式(三级,按优先级高→低):
-- 1) 查询级 hint(优先级最高,只影响当前查询)
SELECT /*+ECSMode(COMPUTE_OPTIMIZED)*/ ... FROM t;
SELECT /*+ECSMode(IO_OPTIMIZED)*/ ... FROM t;
SELECT /*+ECSMode(NONE)*/ ... FROM t; -- 仅 hint 级可用
-- 2) 会话级(覆盖数据库级设置)
ALTER SESSION SET ECSMode = 'COMPUTE_OPTIMIZED';
ALTER SESSION SET ECSMode = 'AUTO'; -- 恢复默认
-- 3) 数据库级(全局默认,谨慎使用)
ALTER DATABASE SET ECSMode = 'IO_OPTIMIZED';
⚠️ 数据库级设置影响所有用户和所有子集群,只在确认大多数查询都受益于同一策略时才设置。
验证 ECS 是否生效:
-- collaborating = TRUE 表示该节点通过 ECS 参与计算
SELECT node_name, shard_name, is_collaborating, is_participating
FROM V_CATALOG.SESSION_SUBSCRIPTIONS;
1.5 弹性伸缩触发条件总结¶
| 场景 | 信号 | 触发动作 |
|---|---|---|
| 数据量持续增长 | 磁盘使用率 > 70%、AHM 落后、mergeout 无法完成 | 扩容节点(Enterprise 需 rebalance) |
| 查询并发激增 | RESOURCE_REJECTED 频发、队列长度持续 > 10 |
扩容节点或新增计算子集群(Eon) |
| ETL 与查询互相干扰 | ETL 时段查询延迟飙升 | 创建独立 ETL 子集群(Eon)或路由规则隔离 |
| 业务低谷(夜间/周末) | CPU/内存利用率 < 10% | 缩容节点或关闭冗余子集群(Eon 模式尤其适用) |
| 定期报表/批处理高峰 | 特定时段(月末/季末)查询延迟恶化 | 临时扩容 Subcluster,任务完成后回收 |
2. 系统级监控:了解当前集群伸缩状态¶
在动手伸缩之前,需要先了解集群的弹性伸缩配置现状。以下 SQL 覆盖了关键的监控维度。
2.1 查看弹性集群全局状态¶
SELECT
is_enabled AS "弹性集群已启用",
scaling_factor AS "伸缩因子",
maximum_skew_percent AS "最大倾斜百分比",
is_local_segment_enabled AS "本地分段启用",
is_rebalance_running AS "Rebalance进行中",
segment_layout AS "分段布局",
version AS "弹性集群版本"
FROM elastic_cluster;
-[ RECORD 1 ]---+---------------------------------------------------------------------------
弹性集群已启用 | t
伸缩因子 | 4
最大倾斜百分比 | 15
本地分段启用 | f
Rebalance进行中 | f
分段布局 | v_vmart3_node0001[33.3%] v_vmart3_node0002[33.3%] v_vmart3_node0003[33.3%]
弹性集群版本 | 2
如何解读:
is_enabled = false:弹性集群未启用。Enterprise 模式下添加节点前必须先启用。如果这是 Eon 模式,此值可能为 false 但仍支持原生伸缩(Eon 模式不依赖此开关)scaling_factor:当前伸缩因子。例如值为 4 意味着数据已被拆分为节点数 × 4个逻辑段。Enterprise 模式建议设置 ≥ 2maximum_skew_percent:段数据在各节点间的最大倾斜百分比。如果此值持续 > 30%,说明数据分布不均,可能需要在 rebalance 时检查is_rebalance_running = true:rebalance 正在进行中,此时不要执行 DDL 操作segment_layout:显示各节点当前承载的 segment 比例。例如「v_node0001[33.3%] v_node0002[33.3%] v_node0003[33.3%]」表示 3 节点均匀分布
本文验证环境(Vertica v26.1.0-2,Enterprise 模式 3 节点)中 is_enabled = true,scaling_factor = 4,maximum_skew_percent = 15,is_local_segment_enabled = false。
2.2 查看节点与子集群分布¶
SELECT
node_name AS "节点名称",
node_state AS "节点状态",
node_type AS "节点类型",
is_primary AS "主节点",
is_ephemeral AS "临时节点",
subcluster_name AS "所属子集群",
is_readonly AS "只读",
node_address AS "IP地址"
FROM nodes
ORDER BY CASE WHEN subcluster_name IS NULL THEN 0 ELSE 1 END, subcluster_name, node_name;
节点名称 | 节点状态 | 节点类型 | 主节点 | 临时节点 | 所属子集群 | 只读 | IP地址
-------------------+----------+-----------+--------+----------+------------+------+--------------
v_vmart3_node0001 | UP | PERMANENT | t | f | | f | 10.211.55.6
v_vmart3_node0002 | UP | PERMANENT | t | f | | f | 10.211.55.11
v_vmart3_node0003 | UP | PERMANENT | t | f | | f | 10.211.55.12
(3 rows)
如何解读:
subcluster_name = NULL:Enterprise 模式没有子集群概念,所有节点属于默认集群is_ephemeral = true:该节点是临时节点(Eon 模式的弹性节点),移除时不需要 rebalancenode_state != 'UP':节点异常,需排查后再进行伸缩操作node_type = 'PERMANENT'vs'EPHEMERAL':永久节点存储元数据和数据(Enterprise),临时节点仅作弹性计算(Eon)
2.3 查看 Eon 模式的子集群配置¶
-- 已验证:v26.1.0-2(Enterprise 模式返回空集)
SELECT
subcluster_name AS "子集群名称",
is_primary AS "主子集群",
is_default AS "默认子集群",
node_name AS "包含节点"
FROM subclusters
ORDER BY subcluster_name, node_name;
如何解读:
is_primary = true:该子集群是 primary 类型,构成数据库核心。只有 primary 子集群的节点参与 K-safety 判定:半数以上 primary 节点存活且每个 shard 至少有一个 primary 节点订阅者时,数据库才能继续运行。primary 子集群也负责 Tuple Mover 等系统操作is_default = true:添加新节点时如果不指定子集群,默认加入此子集群。注意:客户端连接路由到子集群是通过连接字符串的subcluster参数控制,与is_default无关- 如果表中出现多个
is_primary = true的子集群,说明是双主配置,操作时需格外注意(见 Eon 双主子集群的注意事项)
2.4 监控 Rebalance 进度(Enterprise 模式)¶
当 Enterprise 模式正在执行 rebalance 时,Vertica 提供两个级别的进度监控:
2.4.1 表级别进度:REBALANCE_TABLE_STATUS¶
SELECT
table_schema,
table_name,
rebalance_method,
separated_percent AS "分离进度%",
transferred_percent AS "传输进度%",
separated_bytes AS "已分离bytes",
to_separate_bytes AS "待分离bytes",
transferred_bytes AS "已传输bytes",
to_transfer_bytes AS "待传输bytes",
is_latest,
start_timestamp,
end_timestamp
FROM rebalance_table_status
WHERE is_latest = true
ORDER BY separated_percent ASC;
关键列解读:
| 列 | 含义 |
|---|---|
separated_percent |
第一阶段(segment 分离)完成百分比。通常是 rebalance 最慢的阶段 |
transferred_percent |
第二阶段(数据传输)完成百分比。分离完成后才开始推进,整体进度需同时看两个指标 |
to_transfer_bytes |
尚待传输的总字节数。所有表求和 = rebalance 剩余工作量 |
rebalance_method |
ELASTIC_CLUSTER(弹性集群)/ REFRESH / REPLICATE |
is_latest |
只保留最近一次 rebalance。历史 rebalance 的行此值为 false |
elastic_cluster_version |
集群拓扑版本号,可区分不同次 rebalance |
进度停滞排查:如果
separated_percent在某个值停滞超过 30 分钟,可能的原因:锁竞争、磁盘 IO 瓶颈、或节点间网络问题。
2.4.2 投影级别进度:REBALANCE_PROJECTION_STATUS¶
与 REBALANCE_TABLE_STATUS 结构一致,但粒度更细——显示每个 projection 的分离/传输进度。适合定位到底是哪个 projection 拖慢了整体 rebalance:
SELECT
projection_name,
anchor_table_name,
separated_percent,
transferred_percent,
to_transfer_bytes
FROM rebalance_projection_status
WHERE is_latest = true
AND to_transfer_bytes > 0
ORDER BY to_transfer_bytes DESC;
2.4.3 整体进度估算¶
-- 估算 rebalance 剩余工作量
SELECT
COUNT(*) AS "剩余表数",
SUM(to_separate_bytes) AS "待分离总量",
SUM(to_transfer_bytes) AS "待传输总量"
FROM rebalance_table_status
WHERE is_latest = true
AND transferred_percent < 100;
2.5 节点排空状态监控¶
SELECT
node_name,
is_draining,
subcluster_name,
count_client_user_sessions AS "活跃会话数",
oldest_session_id,
oldest_session_user,
oldest_session_login_timestamp
FROM draining_status;
如何解读:
is_draining = true:节点正在排空中,拒绝新连接,等待现有会话完成count_client_user_sessions:当前仍活跃的会话数。当此值降为 0 时,节点可以安全移除oldest_session_login_timestamp:最早建立连接的会话时间。如果此会话持续超过数小时不释放,可能需要手动排查长查询或僵死连接
2.6 Linux 层面检查节点可用资源¶
在决定扩容或缩容前,检查节点实际资源利用情况有助于量化需求:
# 检查各节点 CPU 核心数和负载
lscpu | grep -E '^CPU\(s\)|^Model name' && uptime
# 检查内存使用
free -h
# 检查磁盘使用率(重点关注 data 和 catalog 目录)
df -h /vertica/data
# 检查网络接口(先找到实际接口名,验证环境为 enp0s5)
ip -s link show | grep -E '^[0-9]+:' | grep -v lo | awk -F': ' '{print $2}'
# 然后检查对应接口速率
ethtool enp0s5 2>/dev/null | grep -E 'Speed|Duplex' || echo "虚拟网卡无法获取速率信息(VM 环境正常)"
3. 判断是否需要伸缩:从指标到决策¶
伸缩不是免费的——Enterprise 模式 rebalance 有停机代价(根据存量数据规模以及扩容节点数量,数据重分布时间可能多达几十个小时),Eon 模式增加节点也有成本。以下决策树帮助判断。
3.1 通过磁盘使用率判断扩容需求¶
做什么:检测磁盘是否将成为瓶颈。这是最直接的扩容信号。
SELECT
node_name,
storage_usage,
disk_space_used_mb,
disk_space_free_mb,
disk_space_used_mb + disk_space_free_mb AS disk_space_total_mb,
ROUND(100.0 * disk_space_used_mb / NULLIF(disk_space_used_mb + disk_space_free_mb, 0), 1) AS "使用率%"
FROM disk_storage
WHERE storage_usage LIKE '%DATA%' OR storage_usage = 'CATALOG'
ORDER BY node_name, storage_usage;
node_name | storage_usage | disk_space_used_mb | disk_space_free_mb | disk_space_total_mb | 使用率%
-------------------+---------------+--------------------+--------------------+---------------------+------------------------
v_vmart3_node0001 | CATALOG | 24269 | 3711 | 27980 | 86.7000000000000000000
v_vmart3_node0001 | DATA,TEMP | 24269 | 3711 | 27980 | 86.7000000000000000000
v_vmart3_node0002 | CATALOG | 20630 | 7350 | 27980 | 73.7000000000000000000
v_vmart3_node0002 | DATA,TEMP | 20630 | 7350 | 27980 | 73.7000000000000000000
v_vmart3_node0003 | CATALOG | 22416 | 5564 | 27980 | 80.1000000000000000000
v_vmart3_node0003 | DATA,TEMP | 22416 | 5564 | 27980 | 80.1000000000000000000
(6 rows)
如何解读:
- 使用率 > 70%:应开始规划扩容。继续增长可能导致 mergeout 无法完成(ROS container 无法合并),引发性能螺旋式恶化
- 使用率 > 85%:紧急,mergeout 可能已经受阻。立即执行数据清理(drop 旧分区、purge 已删除数据)作为临时止血措施
- 各节点使用率差异 > 15%:存在数据倾斜,需排查投影设计或数据分布策略
如果磁盘不是瓶颈 → 进入 3.2 检查 CPU/内存压力。
3.2 通过资源拒绝事件判断并发瓶颈¶
做什么:RESOURCE_REJECTED 是查询被拒绝执行的事件,说明资源池配置不足以满足并发需求。
SELECT
node_name,
pool_name,
resource_type,
reason,
rejection_count,
last_rejected_timestamp
FROM resource_rejections
WHERE last_rejected_timestamp > sysdate - INTERVAL '1 day'
ORDER BY rejection_count DESC;
如何解读:
resource_type = 'MEMORY'且持续增长:内存不足,优先调整资源池的内存参数(详见 Vertica 内存压力诊断与调优)。如果调整后仍然不足,考虑增加节点resource_type = 'THREAD'且持续增长:CPU 并发能力不足,增加节点可以线性提升总并发能力- 单小时拒绝次数 > 100:集群已严重过载,弹性扩容是短期最有效的手段
如果资源拒绝不严重 → 进入 3.3 检查查询延迟趋势。
3.3 通过查询性能趋势判断是否需要伸缩¶
SELECT
DATE_TRUNC('HOUR', start_timestamp) AS "小时",
COUNT(*) AS "查询数",
ROUND(AVG(request_duration_ms)/1000, 1) AS "平均耗时(秒)",
ROUND(MAX(request_duration_ms)/1000, 1) AS "最大耗时(秒)",
SUM(CASE WHEN is_executing THEN 1 ELSE 0 END) AS "当前执行中"
FROM query_requests
WHERE start_timestamp >= CURRENT_TIMESTAMP - INTERVAL '24 hours'
GROUP BY DATE_TRUNC('HOUR', start_timestamp)
ORDER BY 1 DESC;
如何解读:
- 平均耗时在特定时段突增 2 倍以上,同时查询数变化不大 → 该时段存在资源争抢,可通过独立子集群隔离工作负载
- 平均耗时和查询数同时增长 → 集群整体能力不足,增加节点
当前执行中持续接近资源池 MAXCONCURRENCY → 查询排队严重,扩容或创建额外子集群
4. 解决方案:从 Enterprise 到 Eon 的完整操作流程¶
4.1 Enterprise 模式弹性伸缩¶
核心原则:按 GCD 方法规划扩容/缩容,保持 factor 尽可能小。 小集群优先做成倍伸缩(3→6→9→12),大集群按 GCD 找最优目标(如 90→108,factor=6)。避免 GCD=1 或 factor 过大的方案(如 90→91:factor 超 32 无法设置,3→4:factor=4 文件数暴增)。
Enterprise 模式伸缩的完整流程取决于数据量:
| 数据量 | 扩容流程 | 缩容流程 |
|---|---|---|
| 小数据量(< 10TB) | 添加节点 → rebalance | rebalance → 移除节点 |
| 大数据量(几十TB+) | 调整 scaling_factor → 本地分段 → 添加节点 → rebalance | 评估存储空间 → 本地分段 → rebalance → 移除节点 |
4.1.1 扩容:小数据量(简便流程)¶
适用于数据量较小的场景,流程简单直接:
前置条件检查:
如果 is_enabled = false:
设置伸缩因子后添加节点:
SELECT SET_SCALING_FACTOR(4);
-- admintools -t db_add_node -d <dbname> --hosts=<new_host1>,<new_host2>
本地分段默认禁用——此时 rebalance 需要同时完成「拆分 segment」+「传输数据」。数据量小时总耗时可控,无需启用。
4.1.2 扩容:大数据量(精细化流程)¶
当数据量达几十 TB 以上,直接 rebalance 将极其缓慢(生产环境实测:228TB 数据 rebalance 约 10 小时)。正确流程是先启用本地分段提前拆分数据,再添加节点 rebalance:
本地分段的作用:默认情况下数据按普通 segment 存储,rebalance 时需同时「拆分 segment → 容器」+「传输容器」。启用本地分段后,数据始终按 scaling_factor 拆好的容器形式存储,rebalance 时只需传输容器——数据拆分阶段被提前到了平时,rebalance 窗口大幅缩短。
代价是存储容器数增加,表中分区多时可能触发 ROS pushback(ContainersPerProjectionLimit 默认 1024)。分区多的表要权衡。
-- 步骤1:确认当前集群状态
SELECT is_enabled, scaling_factor, segment_layout FROM elastic_cluster;
-- 步骤2:调整为合适的 scaling_factor(按目标节点数的倍数规划)
SELECT SET_SCALING_FACTOR(2); -- 例如 3→6 节点扩容
-- 步骤3:启用本地分段,数据在后台拆分为容器
-- 此阶段最耗时,数据库性能会明显下降,建议在业务低谷期执行
SELECT ENABLE_LOCAL_SEGMENTS();
-- 手动对每张大表触发 mergeout 加速拆分:
-- SELECT do_tm_task('mergeout', '<schema.table_name>');
-- ...
-- 步骤4:等待本地分段是否全部完成(将所有的大表提前进行本地分段)
-- 步骤5:分段全部完成后,添加新节点
admintools -t db_add_node -d <dbname> --hosts=<new_host1>,<new_host2>,<new_host3>
-- 步骤6:执行 rebalance(数据已拆分好,仅传输容器,速度极快)
SELECT REBALANCE_CLUSTER();
-- 步骤7:rebalance 完成后可关闭本地分段,减少日常存储容器数
SELECT DISABLE_LOCAL_SEGMENTS();
4.1.3 缩容¶
缩容比扩容更需要谨慎。节点上有数据时数据库不允许移除——必须先通过 rebalance 将数据全部迁走,节点清空后才能执行 db_remove_node。
-- 步骤1:评估缩容后剩余节点的存储是否充足
SELECT node_name,
ROUND(100.0 * disk_space_used_mb / NULLIF(disk_space_used_mb + disk_space_free_mb, 0), 1) AS "使用率%"
FROM disk_storage
WHERE storage_usage LIKE '%DATA%'
ORDER BY 2 DESC;
-- 步骤2:调整 scaling_factor 适配缩容后的节点数
SELECT SET_SCALING_FACTOR(2);
-- 步骤3:启用本地分段,将数据重新拆分为适配缩容后的段数
SELECT ENABLE_LOCAL_SEGMENTS();
-- 等待拆分完成后执行 rebalance,将数据从待移除节点迁出
SELECT REBALANCE_CLUSTER();
-- 步骤4:确认目标节点上已无数据(segment_layout 中不再出现该节点)
SELECT segment_layout FROM elastic_cluster;
-- 步骤5:移除节点
admintools -t db_remove_node -d <dbname> --hosts=<host_to_remove>
⚠️ 缩容同样按 GCD 方法规划:12→10(GCD=2,factor=5)可行但容器增加 5 倍;12→8(GCD=4,factor=2)则仅增加 2 倍,接近最优。与其 12→6→3 分两次操作,不如直接用 GCD 算最优目标一次完成(12→3,factor=1)。
4.1.4 Rebalance 进度监控¶
REBALANCE_CLUSTER() 是异步操作——函数立即返回,实际 rebalance 在后台执行。通过以下 SQL 追踪进度:
-- 整体进度
SELECT is_rebalance_running, segment_layout, local_segment_layout
FROM elastic_cluster;
-- 表级进度
SELECT table_schema, table_name, separated_percent, transferred_percent
FROM rebalance_table_status
WHERE is_latest = true
ORDER BY transferred_percent ASC;
详细监控方法见 2.4 监控 Rebalance 进度(Enterprise 模式)。
Rebalance 期间的性能影响(来自生产环境实测经验):
在 20 节点扩容至 40 节点(数据量 228TB)的实战中:
- 数据本地拆分阶段:文件数增多,Catalog 增大,数据库性能明显下降
- 数据重分布阶段:需停服约 10 小时
- 总人天:43 人天(包含升级、清理、拆分、rebalance、验证全套流程)
关键认知:Enterprise 模式扩容不是轻量级操作。 它是「计划性」而非「弹性」的——通常配合业务版更窗口执行,而不是像云原生数据库那样随时伸缩。
4.2 Eon 模式弹性伸缩¶
Eon 模式的弹性伸缩是分钟级、零停机的操作,这是其核心架构优势。
4.2.1 扩容:创建子集群并添加节点¶
场景一:为特定工作负载创建独立计算子集群
-- 步骤1:创建新的次子集群(不参与 K-safety 判定,可随时启停/删除)
-- 在 Linux shell 中执行:
admintools -t db_add_subcluster -d <dbname> \
-c <new_subcluster_name> \
--hosts=<host1>,<host2>,<host3> \
--is-secondary
场景二:向已有子集群添加节点
-- 步骤1:向子集群添加节点(数据在公共存储中,无需 rebalance)
admintools -t db_add_node -d <dbname> \
--subcluster=<existing_sc_name> \
--hosts=<new_host>
添加节点后验证:
-- 确认新节点在线且加入目标子集群
SELECT node_name, node_state, subcluster_name, is_ephemeral
FROM nodes
WHERE subcluster_name = '<target_subcluster>'
ORDER BY node_name;
为什么 Eon 模式扩容不需要 rebalance? 新节点从公共存储直接读取所需数据填充自己的 Depot 缓存,数据本身不需要在节点间移动。这就是存算分离架构带来的核心优势。
注意区分
REBALANCE_CLUSTER()和REBALANCE_SHARDS():前者是 Enterprise 模式的数据在计算节点间重分布,Eon 模式不需要。后者是 Eon 模式中调整 shard 订阅在节点间的分配——向已有子集群添加节点后,需执行REBALANCE_SHARDS()让新节点均匀分担 shard 订阅,从公共存储上下载订阅的数据文件,否则新节点可能空闲。
4.2.2 缩容:安全排空并移除节点¶
Eon 模式缩容的正确流程是排空 → 移除,而不是直接关闭节点。
DRAIN_NODE() 的作用:
- 该节点拒绝新连接
- 等待现有活跃会话自然完成
- 完成后节点进入「已排空」状态,可以安全移除
-- 步骤2:监控排空进度
SELECT
node_name,
is_draining,
count_client_user_sessions,
oldest_session_login_timestamp
FROM draining_status
WHERE node_name = '<node_name>';
关键判断:当 count_client_user_sessions = 0 时,节点可以安全移除。
如果排空长时间不完成怎么办?
如果 oldest_session_login_timestamp 显示有超过数小时的会话未释放,可以:
- 在
sessions表中定位该会话:SELECT * FROM sessions WHERE node_name = '<node_name>'; - 通过
CLOSE_SESSION('<session_id>')关闭异常长查询 - 确认排空完成后移除节点
4.2.3 子集群的启停管理(无需添加/删除节点)¶
对于周期性工作负载(如月末报表),更轻量的方式是启停整个子集群,而不是添加/删除节点:
-- 关闭不需要的子集群(节约计算资源)
admintools -t stop_subcluster -c <subcluster_name> -d <dbname>
-- 重新启动子集群(恢复计算能力)
admintools -t start_subcluster -c <subcluster_name> -d <dbname>
双主子集群的注意事项(来自 Eon 双主子集群的注意事项):
📋 真实案例 · 来源:Eon 双主子集群的注意事项
如果数据库有两个主子集群(is_primary = true),不能直接关闭其中一个。直接关闭会导致整个数据库变为只读状态(所有节点 is_readonly = true)。正确操作顺序:
-- 错误做法:直接关闭其中一个主子集群
SELECT SHUTDOWN_SUBCLUSTER('subcluster2'); -- 整个数据库变为只读!
-- 正确做法:先降级,再关闭
SELECT DEMOTE_SUBCLUSTER_TO_SECONDARY('subcluster2');
-- 然后在 shell 中关闭
admintools -t stop_subcluster -c subcluster2 -d <dbname> -Fi
-- 重新启动后,升级回主子集群
admintools -t start_subcluster -c subcluster2 -d <dbname> -Fi
SELECT PROMOTE_SUBCLUSTER_TO_PRIMARY('subcluster2');
4.3 自动伸缩 vs 手动伸缩对比¶
| 维度 | 手动伸缩 | 自动伸缩 |
|---|---|---|
| 操作方式 | 管理员通过 admintools 或 SQL 函数手动执行 |
通过 Kubernetes Operator + HPA 或自定义脚本自动触发 |
| 适用场景 | 计划性扩容(数据增长)、一次性缩容 | 周期性负载波动、云环境按需付费 |
| 响应延迟 | 分钟级(Eon 模式)到小时级(Enterprise 模式) | 秒~分钟级(取决于监控轮询间隔) |
| 配置复杂度 | 低(几条命令) | 中~高(需要部署 K8s Operator 或编写自动化脚本) |
| 风险控制 | 高(人工审核每次操作) | 中(需要设置合理的阈值和冷却期,防止抖动) |
| Vertica 原生支持 | ✅ 全部支持 | ⚠️ Eon 模式可通过 K8s HPA 实现;Enterprise 模式需自定义脚本 |
4.3.1 Kubernetes 环境的自动伸缩(Eon 模式)¶
在 K8s 环境中部署 Eon 模式数据库时,可通过 VerticaDB Operator 配合 HorizontalPodAutoscaler (HPA) 实现自动弹性伸缩:
# 示例:基于 CPU 的 HPA 配置(来源:Vertica on Kubernetes 官方文档)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vertica-hpa
spec:
scaleTargetRef:
apiVersion: vertica.com/v1
kind: VerticaDB
name: my-eon-db
minReplicas: 3
maxReplicas: 12
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 5 分钟冷却期,防止抖动
⚠️ 外部方案,优先级低:vault 中暂无 Vertica K8s HPA 的实际集成案例。以上配置来自 Vertica 官方文档
Vertica on Kubernetes,具体阈值需根据实际负载模式调整。Vertica on Kubernetes 部署指南 有更多部署细节。
4.3.2 基于系统表的自定义自动伸缩脚本思路¶
对于非 K8s 环境,可以编写 cron 脚本监控 resource_rejections 和 query_requests 的并发量,达到阈值时自动调用 admintools 扩容。但需要特别注意的是:
- 冷却期:扩容后至少等待 30 分钟再评估是否继续扩容,避免短时波动触发反复伸缩
- 缩容安全阈值:缩容前检查
draining_status和活跃长查询,确保不会中断业务 - 审计日志:每次自动伸缩操作都记录时间、触发指标、执行结果
4.4 伸缩过程中的性能影响总结¶
| 操作 | Enterprise 模式 | Eon 模式 |
|---|---|---|
| 添加节点(rebalance 前) | 历史数据不自动分布到新节点,但新建表的数据会自动分布过去,导致新旧数据分布不均 | 轻微影响(新节点从公共存储拉数据填充 Depot,占用网络带宽) |
| Rebalance 执行中 | 严重影响:CPU/IO/网络高强度,DDL 阻塞,生产环境建议停服 | N/A |
| 节点排空 | 不支持原生排空 | 无性能影响(只是拒绝新连接,已有查询继续执行) |
| 移除节点 | 需 rebalance(严重影响,同扩容) | 无影响(排空完成后瞬间移除) |
| 子集群启停 | N/A(无子集群概念) | 零影响(停掉的子集群不处理查询,不影响其他子集群) |
5. 深入案例¶
5.1 真实案例:Enterprise 模式 20 节点扩容至 40 节点¶
集群规模:Enterprise 模式 v9.0.1,生产库 20 节点(19 计算 + 1 standby),总容量 285TB,已使用 228TB(82%)。
需求现象:核心数据表存储周期不足,无法满足更长数据保留周期要求。整体存储使用率已超 80%,急需扩容。
诊断步骤:
- 评估当前存储使用趋势,确认 40% 以上节点磁盘使用率 > 80%
- 确定扩容目标:20 节点 → 40 节点(38+2)
- 先执行数据库版本升级(9.0.1 → 9.3.1),然后再扩容
操作步骤与耗时:
| 步骤 | 关键耗时 | 操作 |
|---|---|---|
| 服务器环境准备 | — | 安装 OS、配置网络、SSH 互信、关闭 CPU 降频 |
| 数据清理 | — | 导出历史数据至 HDFS,目标每节点降至 5TB |
| 版本升级 | — | 三阶段升级(9.0.1 → 9.1.1 → 9.2.1 → 9.3.1),每阶段需停服 |
| 数据本地拆分 | 约 2 周 | 启用本地分段,将 228TB 数据按 segment 拆分(后台执行,数据库仍可用,但性能下降) |
| 添加节点 + rebalance | 停服约 10 小时 | 添加 20 节点,rebalance 期间数据库不可用 |
| 测试验证 | — | 数据完整性、查询性能、资源池配置 |
实施效果:扩容后 40 节点集群,数据存储周期大幅延长,满足业务要求。rebalance 停服窗口约 10 小时。
关键教训:
- 扩容前必须执行数据清理:原始每节点 12TB 压缩数据,清理后降到 5TB,大幅减少了 rebalance 耗时
- 数据本地拆分阶段性能显著下降:此阶段文件数暴增,数据库处理效率降低。建议预留充足的性能缓冲时间窗口
5.2 真实案例:Eon 模式双主子集群关闭导致数据库只读¶
📋 真实案例 · 来源:Eon 双主子集群的注意事项
集群规模:Eon 模式,6 节点(3+3,两个主子集群 default_subcluster 和 subcluster2)。
故障现象:管理员尝试关闭 subcluster2 子集群进行维护,执行 SELECT SHUTDOWN_SUBCLUSTER('subcluster2') 后,整个数据库变为只读状态(所有节点 is_readonly = true)。
根因分析:subcluster2 是 primary 子集群(is_primary = true)。关闭 primary 子集群会导致 primary 节点数不足半数,触发 quorum 保护机制,整个数据库进入只读模式。
修复方案:
- 先执行
SELECT DEMOTE_SUBCLUSTER_TO_SECONDARY('subcluster2')将其降级为次子集群 - 再执行
admintools -t stop_subcluster -c subcluster2 -d <dbname> -Fi关闭 - 维护完成后:
admintools -t start_subcluster启动,然后SELECT PROMOTE_SUBCLUSTER_TO_PRIMARY('subcluster2')恢复
关键教训:操作双主子集群前,必须先降级目标子集群。这不是 bug,而是 K-safety 的保护机制。
6. 快速诊断 SQL 工具箱¶
| 诊断目标 | SQL | 说明 |
|---|---|---|
| 弹性集群状态 | SELECT * FROM elastic_cluster; |
查看是否启用、伸缩因子、rebalance 是否进行中 |
| 节点与子集群分布 | SELECT node_name, node_state, subcluster_name, is_ephemeral FROM nodes ORDER BY subcluster_name, node_name; |
确认各节点的角色和归属 |
| Eon 子集群配置 | SELECT subcluster_name, is_primary, is_default, node_name FROM subclusters ORDER BY 1, 2; |
Enterprise 模式返回空集 |
| 磁盘使用率 | SELECT node_name, storage_usage, ROUND(100.0*disk_space_used_mb/NULLIF(disk_space_used_mb+disk_space_free_mb,0),1) AS pct FROM disk_storage WHERE storage_usage LIKE '%DATA%' ORDER BY pct DESC; |
扩容的首要依据 |
| 资源拒绝事件 | SELECT node_name, pool_name, resource_type, reason, rejection_count FROM resource_rejections WHERE rejection_count > 0 ORDER BY rejection_count DESC; |
内存/线程不足的直接信号 |
| Rebalance 进度(表级) | SELECT table_schema, table_name, separated_percent, transferred_percent FROM rebalance_table_status WHERE is_latest=true ORDER BY transferred_percent ASC; |
rebalance 未运行时返回空集 |
| Rebalance 操作历史 | SELECT operation_name, operation_status, is_executing, operation_start_timestamp, operation_end_timestamp FROM rebalance_operations ORDER BY operation_start_timestamp DESC; |
查看历史 rebalance 的耗时和状态 |
| 节点排空状态 | SELECT node_name, is_draining, count_client_user_sessions FROM draining_status; |
Eon 模式缩容前必须确认排空完成 |
| 查询延迟趋势 | SELECT DATE_TRUNC('HOUR', start_timestamp), COUNT(*), AVG(request_duration_ms) FROM query_requests WHERE start_timestamp >= CURRENT_TIMESTAMP - INTERVAL '24 HOURS' GROUP BY 1 ORDER BY 1; |
辅助判断是否需要增加并发能力 |
| 资源池并发 | SELECT pool_name, planned_concurrency, max_concurrency, running_query_count FROM resource_pool_status; |
running_query_count 持续接近 planned_concurrency 说明并发不足 |
| 查询排队 | SELECT pool_name, COUNT(*) AS queue_count FROM resource_queues GROUP BY pool_name ORDER BY 2 DESC; |
有排队说明资源不够 |
| 节点在线状态 | SELECT node_name, node_state, node_address, last_msg_from_node_at FROM nodes WHERE node_state != 'UP'; |
伸缩前后确认所有节点在线 |
7. 最佳实践清单¶
按投入产出比从高到低排列:
- 优先使用 Eon 模式以获得真正的弹性伸缩能力 为什么:Eon 模式的存算分离架构使得增删节点不需要 rebalance,伸缩操作从小时级降至分钟级。如果你的业务有周期性负载波动(日报、月报、促销高峰),Eon 模式是唯一能实现「按需付费」的 Vertica 模式。
- Enterprise 模式扩容前务必设置 scaling_factor ≥ 2
为什么:
scaling_factor = 1(默认值)时每次扩容都需要完全重拆 segment(占 rebalance 总耗时 80%)。预先设置 scaling_factor 等于在当前付出少量文件数代价,换取未来扩容时大幅缩短 rebalance 时间。 - 扩容前先清理数据,不要带着「垃圾」一起扩容 为什么:生产环境实测,扩容前清理数据可将每节点从 12TB 降至 5TB,rebalance 数据量减少 58%。清理项目包括:drop 未使用的 schema/表、推进 AHM 并 purge 删除记录、移走过期分区。
- 双主子集群操作前务必先降级 为什么:直接关闭一个主子集群会导致整个数据库只读(见 Eon 双主子集群的注意事项)。这不是 bug,是 K-safety 的保护机制。不需要双主配置时保持单主即可。
- 缩容前必须排空节点,不要直接拔网线
为什么:Eon 模式下
DRAIN_NODE()确保活跃查询正常完成后才移除节点,避免查询中断和数据不一致。即使节点宕机导致自动 failover,也应确保被替换节点上的查询已完成。 - 通过子集群隔离工作负载,而不是混合调度 为什么:将 ETL 和用户查询放在同一个子集群中,即使资源池隔离,CPU 和 IO 争抢仍然存在。独立子集群实现了物理层的资源隔离。
- 设置伸缩冷却期,防止弹性抖动
为什么:短时负载波动(如一次异常查询触发大量
RESOURCE_REJECTED)不应触发自动扩容。至少 15-30 分钟的冷却期甚至更长时间确保扩容决策基于真实的负载趋势。 - Eon 模式的分片数应选择有更多约数的数值(6、8、12、24) 为什么:分片数能被更多节点数整除时,数据分布均匀,避免热点问题。详见 Vertica Eon 模式中分片、节点和 Depot 选择的最佳实践。
- 定期检查
maximum_skew_percent,倾斜是扩容效率的隐形杀手 为什么:数据倾斜超过 30% 时,rebalance 需要从过载节点移动大量数据,导致数据重分布时间变长。在扩容前修复倾斜比扩容后更容易。 - 记录每次伸缩操作的时间、触发条件、执行结果 为什么:建立伸缩操作的审计日志,便于未来评估「当初的扩容是否解决了问题」「缩容是否引发了新的瓶颈」。这为容量规划提供了数据支撑,而不是依赖于直觉。
扩展阅读¶
- Vertica 集群 Rebalance 完全指南 — Enterprise 模式 rebalance 的原理与操作细节
- Vertica 节点与集群规模规划指南 — 硬件选型与初始规模确定
- Vertica 冷热数据管理与成本优化 — Eon 模式弹性缩减计算资源配合冷数据策略
- Vertica 客户端连接负载均衡配置 — 子集群级别的连接路由与负载均衡