跳转至

Vertica 弹性伸缩功能介绍与配置

作者:JiangChong | 发布时间:2026-05-15

适用场景框:当你需要根据业务负载变化动态调整集群计算资源——包括增加节点应对高峰、缩减节点节约成本、或在不同子集群间隔离工作负载——本文提供从原理到实操的完整指南。

关联文章:

理解全文脉络: 第 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 工作机制

  1. ENABLE_ELASTIC_CLUSTER() 激活弹性集群模式,允许数据库动态增减节点
  2. SET_SCALING_FACTOR(N) 设定伸缩因子,决定每个 projection 拆分为多少个存储容器
  3. 添加节点后,执行 REBALANCE_CLUSTER() 将数据按 segment → node 的映射重新分布
  4. 可选: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 模式建议设置 ≥ 2
  • maximum_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 = truescaling_factor = 4maximum_skew_percent = 15is_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 模式的弹性节点),移除时不需要 rebalance
  • node_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 扩容:小数据量(简便流程)

适用于数据量较小的场景,流程简单直接:

前置条件检查:

-- 确认弹性集群状态
SELECT is_enabled, scaling_factor, is_rebalance_running FROM elastic_cluster;

如果 is_enabled = false

SELECT ENABLE_ELASTIC_CLUSTER();

设置伸缩因子后添加节点:

SELECT SET_SCALING_FACTOR(4);
-- admintools -t db_add_node -d <dbname> --hosts=<new_host1>,<new_host2>

本地分段默认禁用——此时 rebalance 需要同时完成「拆分 segment」+「传输数据」。数据量小时总耗时可控,无需启用。

-- 执行 rebalance(异步操作)
SELECT REBALANCE_CLUSTER();

4.1.2 扩容:大数据量(精细化流程)

当数据量达几十 TB 以上,直接 rebalance 将极其缓慢(生产环境实测:228TB 数据 rebalance 约 10 小时)。正确流程是先启用本地分段提前拆分数据,再添加节点 rebalance

ENABLE_LOCAL_SEGMENTS() → 等待数据拆分完成 → 添加节点 → 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

评估存储空间 → 本地分段(重新分布数据) → rebalance → 确认节点无数据 → 移除节点
-- 步骤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 模式缩容的正确流程是排空 → 移除,而不是直接关闭节点。

-- 步骤1:将要移除的节点设为排空状态
SELECT DRAIN_NODE('<node_name>');

DRAIN_NODE() 的作用:

  1. 该节点拒绝新连接
  2. 等待现有活跃会话自然完成
  3. 完成后节点进入「已排空」状态,可以安全移除
-- 步骤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 时,节点可以安全移除。

-- 步骤3:移除节点
admintools -t db_remove_node -d <dbname> --hosts=<node_to_remove>

如果排空长时间不完成怎么办?

如果 oldest_session_login_timestamp 显示有超过数小时的会话未释放,可以:

  1. sessions 表中定位该会话:SELECT * FROM sessions WHERE node_name = '<node_name>';
  2. 通过 CLOSE_SESSION('<session_id>') 关闭异常长查询
  3. 确认排空完成后移除节点

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_rejectionsquery_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%,急需扩容。

诊断步骤

  1. 评估当前存储使用趋势,确认 40% 以上节点磁盘使用率 > 80%
  2. 确定扩容目标:20 节点 → 40 节点(38+2)
  3. 先执行数据库版本升级(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 保护机制,整个数据库进入只读模式。

修复方案

  1. 先执行 SELECT DEMOTE_SUBCLUSTER_TO_SECONDARY('subcluster2') 将其降级为次子集群
  2. 再执行 admintools -t stop_subcluster -c subcluster2 -d <dbname> -Fi 关闭
  3. 维护完成后: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. 最佳实践清单

按投入产出比从高到低排列:

  1. 优先使用 Eon 模式以获得真正的弹性伸缩能力 为什么:Eon 模式的存算分离架构使得增删节点不需要 rebalance,伸缩操作从小时级降至分钟级。如果你的业务有周期性负载波动(日报、月报、促销高峰),Eon 模式是唯一能实现「按需付费」的 Vertica 模式。
  2. Enterprise 模式扩容前务必设置 scaling_factor ≥ 2 为什么scaling_factor = 1(默认值)时每次扩容都需要完全重拆 segment(占 rebalance 总耗时 80%)。预先设置 scaling_factor 等于在当前付出少量文件数代价,换取未来扩容时大幅缩短 rebalance 时间。
  3. 扩容前先清理数据,不要带着「垃圾」一起扩容 为什么:生产环境实测,扩容前清理数据可将每节点从 12TB 降至 5TB,rebalance 数据量减少 58%。清理项目包括:drop 未使用的 schema/表、推进 AHM 并 purge 删除记录、移走过期分区。
  4. 双主子集群操作前务必先降级 为什么:直接关闭一个主子集群会导致整个数据库只读(见 Eon 双主子集群的注意事项)。这不是 bug,是 K-safety 的保护机制。不需要双主配置时保持单主即可。
  5. 缩容前必须排空节点,不要直接拔网线 为什么:Eon 模式下 DRAIN_NODE() 确保活跃查询正常完成后才移除节点,避免查询中断和数据不一致。即使节点宕机导致自动 failover,也应确保被替换节点上的查询已完成。
  6. 通过子集群隔离工作负载,而不是混合调度 为什么:将 ETL 和用户查询放在同一个子集群中,即使资源池隔离,CPU 和 IO 争抢仍然存在。独立子集群实现了物理层的资源隔离。
  7. 设置伸缩冷却期,防止弹性抖动 为什么:短时负载波动(如一次异常查询触发大量 RESOURCE_REJECTED)不应触发自动扩容。至少 15-30 分钟的冷却期甚至更长时间确保扩容决策基于真实的负载趋势。
  8. Eon 模式的分片数应选择有更多约数的数值(6、8、12、24) 为什么:分片数能被更多节点数整除时,数据分布均匀,避免热点问题。详见 Vertica Eon 模式中分片、节点和 Depot 选择的最佳实践
  9. 定期检查 maximum_skew_percent,倾斜是扩容效率的隐形杀手 为什么:数据倾斜超过 30% 时,rebalance 需要从过载节点移动大量数据,导致数据重分布时间变长。在扩容前修复倾斜比扩容后更容易。
  10. 记录每次伸缩操作的时间、触发条件、执行结果 为什么:建立伸缩操作的审计日志,便于未来评估「当初的扩容是否解决了问题」「缩容是否引发了新的瓶颈」。这为容量规划提供了数据支撑,而不是依赖于直觉。

扩展阅读