跳转至

Vertica 客户端连接负载均衡配置

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

适用场景: 当集群中部分节点 CPU 持续偏高、SQL 准备阶段耗时异常、或发现所有客户端连接集中在少数甚至单个节点时,说明连接未做负载均衡,本文提供从检测到修复的完整方案。

关联文章

理解全文脉络

本文从原理到实战共 8 节,按三层递进组织:

  • 不管有没有问题,先读第 1 节:理解 Vertica 原生负载均衡的底层机制,为什么「连接集中」是性能杀手。
  • 怀疑有连接倾斜时,从第 2-3 节入手:用系统表快速诊断连接分布,定位倾斜来源。
  • 需要修复时,跳到第 4 节:JDBC/ODBC 参数配置 + 服务端路由规则,覆盖原生和外部的完整方案。
  • 想学习典型案例时,看第 5-6 节:4 个案例(2 真实 + 2 虚构)+ 完整演练。

1. 原理理解

1.1 什么是连接负载均衡

Vertica 是一个无共享架构(shared-nothing)的 MPP 数据库,集群中每个节点都存储数据的一部分,也都能独立接收客户端连接。当客户端发起连接时,它连接到集群中的某一个节点。此后,该客户端的所有 SQL 请求都由这个节点作为 启动节点(initiator node) 来接收、解析、生成执行计划,然后将计划分发到所有相关节点执行。

如果所有客户端都连接到同一个节点,意味着什么?

类比:一个工厂有 10 条流水线,但所有订单都从 1 号窗口递进来,由 1 号窗口的调度员统一拆解任务后再分发给 10 条线。当订单少时没问题,但当订单量上去后,调度员本身就成了瓶颈——其他 9 条线空闲,而调度员忙不过来。

在 Vertica 中,这个「调度员」就是 initiator node 上的以下阶段:

  • BeforePlan / AnalyzeRewrite:解析和重写 SQL
  • CompilePlan:访问 Catalog 获取元数据、生成执行计划
  • PreparePlan:锁表、资源预留、计划分发

这些阶段全部在 initiator node 上串行执行,不涉及数据节点。当上百个并发连接都集中在一个节点时,该节点的 CPU 和 Catalog 访问被大量争抢,单条 SQL 的准备时间从毫秒级飙升至秒级甚至几十秒。

1.2 原生负载均衡的原理

Vertica 内置了原生连接负载均衡(Native Connection Load Balancing),机制如下:

策略 server 端配置 客户端配置 工作原理
NONE(默认) SET_LOAD_BALANCE_POLICY('NONE') 不需要 关闭原生均衡,客户端始终连接 URL 中指定的主机
ROUNDROBIN SET_LOAD_BALANCE_POLICY('ROUNDROBIN') ConnectionLoadBalance=1 客户端首次连接 URL 中指定的主机后,从该节点获取集群节点列表,后续新连接按循环顺序依次分配到各个节点
RANDOM SET_LOAD_BALANCE_POLICY('RANDOM') ConnectionLoadBalance=1 与 ROUNDROBIN 类似,但使用随机选择而非轮转

关键前提:原生负载均衡需要服务端和客户端双端配置,缺一不可:

  • 服务端只配策略 → 白配,客户端不感知
  • 客户端只配参数 → 无效,因为服务端策略是 NONE
  • 两端都配 → 客户端从初始化节点获取节点列表和策略,后续连接按策略分配到不同节点

原生负载均衡的另一个重要特性是连接级生效,而非 SQL 级。也就是说:同一个连接内的所有 SQL 始终在同一个 initiator node 上执行。均衡的是连接,不是每条 SQL。

1.3 路由规则(Routing Rules)—— 进阶控制

当集群有多个子集群(subcluster)时,仅靠 ROUNDROBIN/RANDOM 策略不够精细。你可能希望:

  • 一批客户端只连到子集群 A,另一批只连到子集群 B
  • 特定 IP 段的连接路由到特定子集群

这可以通过 LOAD BALANCE GROUP + ROUTING RULE 实现。工作方式:

客户端 IP → ROUTING RULE(按 IP 匹配)→ LOAD BALANCE GROUP(包含节点/子集群)→ 策略分发

路由规则在 v_catalog 中以两张表存储:

  • LOAD_BALANCE_GROUPS:定义负载均衡组,包含哪些节点或子集群,以及使用的策略
  • ROUTING_RULES:定义 IP 地址到负载均衡组的映射关系

从 Vertica 23.3 开始,还引入了 Workload Routing,通过 --workload 参数按工作负载类型(而非 IP)路由连接。相关表为 WORKLOAD_ROUTING_RULES

1.4 原生负载均衡 vs 外部负载均衡

对比维度 原生负载均衡 外部负载均衡(F5 / HAProxy / Nginx / K8s Service)
实现方式 Vertica 内置,客户端驱动感知节点列表 在 Vertica 前方放置 TCP/HTTP 代理
配置复杂度 低:服务端一条 SQL + 客户端一个参数 中-高:需配置代理软件,维护节点列表
故障转移 客户端内置 BackupServerNode 参数 代理层健康检查 + 自动摘除
均衡粒度 连接级(新连接均衡) 连接级,可做更细粒度的负载算法
节点感知 客户端内建节点列表,感知拓扑 代理层不感知 Vertica 内部拓扑
推荐的场景 大多数 Vertica 场景 K8s 部署、需要高级路由/健康检查、TCP idle timeout 防护
注意事项 与外部 LB 同时启用会导致双层均衡,ROUNDROBIN 可能表现为随机 需确保代理的 TCP idle timeout > 客户端最长事务时间

重要:不要同时启用原生负载均衡和外部负载均衡。详见 Vertica Docker CE 与 Kubernetes 常见问题 中的讨论——K8s Service 已做一层均衡,再开原生均衡会导致行为不可预期。

1.5 所有可能来源/原因总结

来源 表现 发生条件
客户端未配负载均衡参数 所有连接落在同一节点 JDBC URL / ODBC DSN 缺少 ConnectionLoadBalance
服务端策略为 NONE 原生负载均衡不生效 默认值,未执行 SET_LOAD_BALANCE_POLICY
调度工具固定 IP 连接 某类用户/任务集中在单一节点 ETL 工具硬编码了某个节点的 IP
路由规则缺失或错误 某些 IP 段的路由不符合预期 子集群隔离场景未配 ROUTING RULE
外部 LB 仅指向一个节点 看起来有 LB 但实际只转发到一个后端 LB 配置中只有一个 upstream server
连接池未释放连接 长期复用的连接永远不会被重新均衡 应用使用连接池(如 HikariCP)且生命周期很长
故障转移后回切失败 某节点故障恢复后,客户端未重新均匀连接 BackupServerNode 生效后未清理

2. 系统级监控(从宏观入手)

2.1 检查负载均衡策略状态

首先确认服务端当前配置:

SELECT LOAD_BALANCE_POLICY FROM V_CATALOG.DATABASES;

如何解读结果:

  • none → 原生负载均衡未启用,所有客户端必然连到 URL 中指定的主机
  • roundrobinrandom → 服务端已启用,但客户端仍需配置 ConnectionLoadBalance=1 才能生效

2.2 检查负载均衡组与路由规则

SELECT name, policy, filter, type, object_name FROM v_catalog.load_balance_groups;

SELECT name, source_address, destination_name FROM v_catalog.routing_rules;

SELECT workload, subcluster_name, priority FROM v_catalog.workload_routing_rules;

如何解读结果:

  • load_balance_groups 为空 → 没有定义任何负载均衡组,所有客户端按全局策略(DATABASES.LOAD_BALANCE_POLICY)分配
  • routing_rules 为空 → 没有 IP 级别的路由规则,所有客户端统一处理
  • 每行的 destination_name 应匹配一个 load_balance_groups.name,如果匹配不上则该规则无效
  • workload_routing_rules 为空 → 未使用工作负载路由(v23.3+ 特性)

2.3 检查各节点当前连接分布

实时会话分布——查看每个节点上当前有多少个活跃客户端会话:

SELECT
    nvl(n.subcluster_name, 'Enterprise Mode') AS subcluster_name,
    s.node_name,
    COUNT(*) AS session_count
FROM v_monitor.sessions s
LEFT JOIN v_catalog.nodes n ON s.node_name = n.node_name
WHERE s.user_name <> 'dbadmin'
GROUP BY 1, 2
ORDER BY 1, 2;

如何解读结果:

  • 均衡期望:每个节点的 session_count 应大致相等(差异不超过 2 倍为健康)
  • 倾斜告警:如果某节点 session 数是其他节点的 5 倍以上,连接倾斜已严重
  • 空节点告警:如果有节点 session_count = 0 而其他节点 > 0,说明该节点完全未被使用

近期查询发起分布——查看过去 1 天内每个节点发起过多少查询:

SELECT
    node_name,
    COUNT(*) AS query_count
FROM v_internal.dc_requests_issued
WHERE "time" > sysdate() - 1
GROUP BY 1
ORDER BY 1;

如何解读结果:

  • 期望每个节点的 query_count 大致相等
  • 如果 80% 以上的查询集中在少于 30% 的节点上 → 确认连接未均衡
  • 此查询是 Vertica 官方推荐的负载均衡快速检查方法

历史连接启动分布——查看各节点、各客户端 IP 的连接历史分布:

SELECT
    nvl(n.subcluster_name, 'Enterprise Mode') AS subcluster_name,
    dss.node_name,
    regexp_substr(client_hostname, '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') AS client_ip,
    COUNT(*) AS connection_cnt
FROM v_internal.dc_session_starts dss
LEFT JOIN v_catalog.nodes n ON dss.node_name = n.node_name
WHERE user_name <> 'dbadmin'
  AND regexp_not_like(client_hostname, '\w]:\d+')
  AND client_hostname <> ''
GROUP BY 1, 2, 3
ORDER BY 3, 2, 1;

如何解读结果:

  • client_ip 排序后,检查每个客户端 IP 的连接数是否均匀分布到多个节点
  • 如果某个 client_ip 只出现在一个 node_name 下 → 该客户端未开启负载均衡
  • 多个客户端 IP 集中在同一节点 → 可能是外部 LB 或调度工具配置问题

3. 逐步定位根因(从宏观到微观)

步骤 1:确认是服务端还是客户端的问题

做什么:先用 2.1 节的 SQL 检查 LOAD_BALANCE_POLICY

SELECT LOAD_BALANCE_POLICY FROM V_CATALOG.DATABASES;

如何解读:

  • none → 服务端没有启用负载均衡。跳到第 4 节先配服务端,再配客户端。
  • roundrobinrandom → 服务端已配好。问题在客户端,继续步骤 2。

如果不是 → 进入步骤 2。

步骤 2:检查具体哪些客户端连接未均衡

做什么:用 2.3 节的历史连接分布 SQL,按 client_ip 分析。

如何解读:

按以下模式判断:

模式 解读 下一步
所有 client_ip 都集中在同一个 node 服务端策略可能未生效或外部 LB 只指向一个节点 检查服务端策略 + 外部 LB 配置
有的 client_ip 均衡、有的不均衡 部分客户端配置了 ConnectionLoadBalance,部分没配 排查未均衡的客户端连接串
同一个 IP 段所有客户端都在一个 node 可能存在 ROUTING RULE 但配置错误 检查 routing_rules 表
所有连接都均衡但仍有性能问题 连接均衡不一定够——考虑连接池复用导致长期连接永不重分配 跳至第 4 节连接池部分

如果不是 → 进入步骤 3。

步骤 3:排查外部负载均衡器配置

如果使用了 F5 / HAProxy / Nginx / K8s Service 等外部负载均衡器:

  1. 确认 upstream 节点列表完整:所有 Vertica 节点的 IP:5433 都应该在 LB 后端列表中
  2. 确认健康检查正常:LB 是否能正确检测节点 UP/DOWN
  3. 确认均衡算法:round-robin 或 least-connections,不要用 IP-hash(会导致每个客户端始终连同一个节点)

检查外部 LB 的效果——从 Vertica 端看各节点的总连接数:

SELECT
    node_name,
    COUNT(*) AS total_sessions
FROM v_monitor.sessions
GROUP BY 1
ORDER BY 1;

如果外部 LB 做了均衡、但 Vertica 端连接仍不均衡,可能原因:

  • 长连接连接池:连接建立后长期不释放,LB 的均衡只在新建连接时生效
  • K8s Service 的 sessionAffinity: ClientIP 导致同一客户端始终连到同一 Pod

步骤 4:排查连接池导致的「伪均衡」

即使连接建立时是均衡的,如果应用侧使用连接池(如 HikariCP、C3P0、Druid)且连接生命周期很长,那么:

  • 新连接已经均衡分配了
  • 但已有的旧连接一直在复用,连接分布仍然是老的不均衡状态

检查方式——查看同一客户端 IP 的 session 创建时间分布:

SELECT
    node_name,
    client_hostname,
    MIN(login_timestamp) AS earliest_session,
    MAX(login_timestamp) AS latest_session,
    COUNT(*) AS session_count
FROM v_monitor.sessions
WHERE user_name <> 'dbadmin'
GROUP BY 1, 2
ORDER BY client_hostname, node_name;

如何解读:

  • 如果某个 client_hostname 在一个 node 上有大量 session,且 earliest_session 是很久之前(数天或数周),说明连接池中的老连接一直在复用,长期占据了该节点
  • 解决方案:配置连接池的最大空闲时间和最大生命周期(见第 4 节)

4. 解决方案(从快速见效到根本治理)

4.1 立即措施:启用原生负载均衡(当天可执行)

4.1.1 服务端配置

SELECT SET_LOAD_BALANCE_POLICY('ROUNDROBIN');

为什么推荐 ROUNDROBIN 而不是 RANDOM?

ROUNDROBIN 保证所有节点被均匀轮转使用,不会出现随机导致的短期倾斜。RANDOM 在连接数不够大时可能出现显著不均。

执行后验证策略已生效:

SELECT LOAD_BALANCE_POLICY FROM V_CATALOG.DATABASES;
-- 应返回 'roundrobin'

此操作不需要重启数据库,立即生效。

4.1.2 JDBC 客户端配置

在 JDBC 连接 URL 中添加 ConnectionLoadBalance=1

jdbc:vertica://node01:5433/dbname?ConnectionLoadBalance=1

完整推荐的 JDBC URL 模板:

jdbc:vertica://node01:5433/dbname?ConnectionLoadBalance=1&BackupServerNode=node02:5433,node03:5433&loginTimeout=30&socketTimeout=0

各参数说明:

参数 推荐值 含义 为什么这个值
ConnectionLoadBalance 1true 启用客户端负载均衡 告诉驱动从初始节点获取节点列表并按策略分配新连接
BackupServerNode node02:5433,node03:5433 备用节点列表 如果初始节点(URL 中的 host)恰好宕机,客户端自动连接到备用节点。至少配 2 个备用节点以覆盖初始节点 + 1 个备用节点同时宕机的情况
loginTimeout 30(秒) 连接超时 避免客户端在节点宕机时无限等待
socketTimeout 0(禁用) 查询超时 设为 0 表示无超时限制。不要设太短的值——分析查询可能运行数分钟甚至数小时。如果网络不稳定可设较长值如 3600

关键注意ConnectionLoadBalance 对新连接生效,但不改变已有连接分配到的节点。已经在使用的连接池中的连接不会自动迁移。

4.1.3 ODBC 客户端配置

在 ODBC 连接串中添加 ConnectionLoadBalance=1

Driver=/opt/vertica/lib64/libverticaodbc.so
Database=dbname
Servername=node01
Port=5433
ConnectionLoadBalance=1
BackupServerNode=node02:5433,node03:5433

或通过 DSN 文件(/etc/odbc.ini~/.odbc.ini):

[VerticaDSN]
Driver=/opt/vertica/lib64/libverticaodbc.so
Database=dbname
Servername=node01
Port=5433
ConnectionLoadBalance=1
BackupServerNode=node02:5433,node03:5433

4.1.4 VSQL 命令行客户端

# -C 启用 ConnectionLoadBalance
# -B 指定 BackupServerNode 列表
vsql -C -h node01 -B node02:5433,node03:5433 -U dbadmin -c "SELECT 1;"

4.2 短期优化:配置路由规则实现子集群隔离(当周执行)

如果集群有多个子集群,可以用路由规则实现精细化的连接分配。

4.2.1 创建负载均衡组

-- 为每个子集群创建独立的负载均衡组
CREATE LOAD BALANCE GROUP LBG_analytics WITH SUBCLUSTER analytics_cluster FILTER '0.0.0.0/0';
CREATE LOAD BALANCE GROUP LBG_etl WITH SUBCLUSTER etl_cluster FILTER '0.0.0.0/0';

POLICY 参数可选,默认继承全局策略(DATABASES.LOAD_BALANCE_POLICY)。也可以显式指定:

CREATE LOAD BALANCE GROUP LBG_analytics
  WITH SUBCLUSTER analytics_cluster
  FILTER '0.0.0.0/0'
  POLICY 'ROUNDROBIN';

FILTER 的含义'0.0.0.0/0' 表示不限制 IP。如果只想特定 IP 段的连接进入该组,可以写如 '192.168.1.0/24'

4.2.2 创建路由规则

-- 将来自 192.168.1.x 的连接路由到分析子集群
CREATE ROUTING RULE rr_analytics ROUTE '192.168.1.0/24' TO LBG_analytics;

-- 将来自 10.0.0.x 的连接路由到 ETL 子集群
CREATE ROUTING RULE rr_etl ROUTE '10.0.0.0/24' TO LBG_etl;

路由规则匹配逻辑:客户端 IP 与 source_address 的 CIDR 做匹配,匹配到的第一个规则生效。如果没有任何规则匹配,使用全局策略。

4.2.3 修改已有负载均衡组

-- 添加子集群到已有组
ALTER LOAD BALANCE GROUP LBG_analytics ADD SUBCLUSTER new_cluster;

-- 修改策略
ALTER LOAD BALANCE GROUP LBG_analytics SET POLICY TO 'RANDOM';

4.2.4 修改路由规则

-- 修改路由目标 IP 段
ALTER ROUTING RULE rr_analytics SET ROUTE TO '192.168.2.0/24';

-- 修改路由目标组
ALTER ROUTING RULE rr_analytics SET GROUP TO 'LBG_etl';

4.3 连接池层面的配合措施

即使连接建立时做了负载均衡,如果连接池长期持有连接不释放,均衡效果也会被稀释。以下配置可配合使用:

HikariCP 示例

# HikariCP 连接池配置(Java/Spring Boot)
hikari:
  maximumPoolSize: 30
  minimumIdle: 5
  maxLifetime: 1800000        # 连接最大生命周期 30 分钟,到期强制回收
  idleTimeout: 600000          # 空闲连接 10 分钟回收
  connectionTestQuery: "SELECT 1"

为什么配置 maxLifetime?

连接在池中驻留太久 → 新建连接(被均衡的)占比越来越低 → 老连接(可能不均衡的)占比越来越高。设置 30 分钟的最大生命周期确保连接定期被回收和重建,每次新建都会触发负载均衡。

C3P0 示例

<!-- c3p0 连接池配置 -->
<property name="maxIdleTime">600</property>        <!-- 空闲 10 分钟回收 -->
<property name="maxConnectionAge">1800</property>   <!-- 最大寿命 30 分钟 -->

4.4 外部负载均衡器配置方案

外部方案,优先级低。本库暂无外部 LB 集成案例,以下配置为通用参考。

HAProxy 示例

# /etc/haproxy/haproxy.cfg
frontend vertica_frontend
    bind *:5433
    default_backend vertica_backend
    timeout client 24h       # 关键:必须足够长以容纳长查询

backend vertica_backend
    balance roundrobin
    option tcp-check
    timeout server 24h       # 关键:与 client timeout 一致
    server node01 10.0.1.1:5433 check
    server node02 10.0.1.2:5433 check
    server node03 10.0.1.3:5433 check

关键点:TCP idle timeout 必须设置得足够长。如果 timeout 太短而查询运行了 10 分钟,HAProxy 会断开连接导致客户端收到 java.io.EOFException。相关问题排查可参考 Vertica 错误日志解读与常见错误处理

K8s Service 示例

apiVersion: v1
kind: Service
metadata:
  name: vertica-service
spec:
  type: ClusterIP
  sessionAffinity: None    # 关键:不要用 ClientIP,否则同一客户端始终连同一 Pod
  ports:
  - port: 5433
    targetPort: 5433
  selector:
    app: vertica

重要:K8s 场景下建议关闭 Vertica 原生负载均衡(SET_LOAD_BALANCE_POLICY('NONE')),让 K8s Service 的 iptables/ipvs 做 TCP 层均衡。双层均衡会导致行为不可预期。


5. 深入案例

案例 1:调度程序连接集中导致 SQL 准备 20 秒

真实案例

场景描述:某运营商 48+4 节点 Vertica 9.0.1 集群,2023 年 11 月 14 日凌晨开始,数仓整体性能明显下降,部分程序耗时翻倍。

诊断过程

  1. 数据库节点全部 UP,资源池无排队,无高开销 SQL,硬件指标正常
  2. 分析典型慢 SQL(插入 1 条日志记录):
阶段 耗时 执行节点
BeforePlan 4.8 秒 node0037
PreparePlan 10.8 秒 node0037
CompilePlan 4.8 秒 node0037
ExecutePlan 0.6 秒 所有节点
  1. 检查各节点 session 分布:批处理用户 schedule 的所有连接全在 node0037

根因分析:调度程序的数据库连接串未配置负载均衡参数,所有连接固定指向 node0037。48+4 节点集群中,所有 SQL 的 BeforePlan / CompilePlan / PreparePlan 全部在该节点串行执行,Catalog 访问争抢严重。

修复方案:在调度服务器的 JDBC 连接串中添加 ConnectionLoadBalance=1

效果对比

  • 修复前:单条 SQL 准备阶段 20 秒(Execute 仅 0.6 秒)
  • 修复后:单条 SQL 准备阶段 < 1 秒
  • 集群整体平均响应时间在 15 点调整完成后恢复至正常水平

案例 2:外部 LB 未覆盖所有节点

虚构案例

场景描述:某金融机构 12 节点 Vertica 集群,前端使用 HAProxy 做 TCP 负载均衡。运维反馈集群性能不均衡:前 4 个节点 CPU 持续 > 70%,后 8 个节点 CPU 在 20-30%。

诊断过程

-- 检查各节点查询发起分布
SELECT node_name, COUNT(*)
FROM v_internal.dc_requests_issued
WHERE "time" > sysdate() - 1
GROUP BY 1 ORDER BY 1;

输出显示 node01~node04 各有约 50,000 条查询,node05~node12 各只有几百条。进一步检查 HAProxy 配置:

backend vertica_backend
    server node01 10.0.1.1:5433 check
    server node02 10.0.1.2:5433 check
    server node03 10.0.1.3:5433 check
    server node04 10.0.1.4:5433 check
    # node05~node12 未加入 upstream 列表

根因分析:HAProxy 配置时只添加了前 4 个节点作为后端,遗漏了后 8 个节点。

修复方案:将全部 12 个节点加入 HAProxy backend 配置。

效果对比

  • 修复前:前 4 节点 CPU 70%,后 8 节点 20%
  • 修复后:所有节点 CPU 均衡在 35-45%

案例 3:连接池老连接长期未回收

虚构案例

场景描述:某电商平台 8 节点 Vertica 集群,使用 HikariCP 连接池,maximumPoolSize=50。负载均衡已配置(ConnectionLoadBalance=1 + SET_LOAD_BALANCE_POLICY('ROUNDROBIN')),但运行 2 周后发现 node02 的 CPU 仍然偏高。

诊断过程

-- 检查 session 的创建时间分布
SELECT
    node_name,
    MIN(login_timestamp) AS earliest,
    MAX(login_timestamp) AS latest,
    COUNT(*) AS cnt
FROM v_monitor.sessions
WHERE user_name = 'app_user'
GROUP BY 1;

输出显示 node02 有 48 个 session,最早的可追溯到 14 天前(应用部署日),而其他节点的 session 都在最近 1 天内。

根因分析:HikariCP 默认 maxLifetime=1800000(30 分钟),但该应用的部署配置中显式设置了 maxLifetime=0禁用了连接生命周期管理,导致连接一旦建立就永不回收。首次部署时连接池在 node02 上大量建立连接(当时调度工具启动了高并发批量任务),这些连接存活了 14 天一直没有被回收重建。虽然新连接均衡分配了,但老连接占据了 node02。

修复方案:在 HikariCP 配置中显式设置 maxLifetime=600000(10 分钟)并滚动重启应用实例。

效果对比

  • 修复前:node02 有 48 个 14 天前的长连接
  • 修复后:连接在每个生命周期(10 分钟)结束后回收重建,分布回归均衡

案例 4:未启用负载均衡导致连接倾斜

真实案例

场景描述:某运营商集群出现性能问题,排查发现大量连接集中在少数节点,导致节点间负载严重不均。

诊断过程

  1. 检查 LOAD_BALANCE_POLICY:仍为 NONE(默认值)
  2. 检查各节点 dc_requests_issued 分布:查询集中在少数节点
  3. 检查客户端连接配置:未使用负载均衡参数

根因分析:客户端连接串中未添加 -C(连接负载均衡)和 -B(备用节点)参数,所有连接固定指向同一节点。

修复方案:在连接串中加入 -C-B 参数。同时检查客户端驱动版本——旧版驱动对负载均衡的支持可能不完整,建议升级至与服务端匹配的版本。

提示:本案例和案例 1 的共同教训是——连接负载均衡是「默认关闭」的功能,需要在服务端和客户端两端显式启用,切勿假定它已经生效。


6. 完整诊断流程实战

虚构场景 · 完整演练

场景:某企业 6 节点 Vertica v24.x 集群,DBA 收到告警:「node03 CPU 持续 > 85%,其他节点 30-40%」。集群 KSAFE=1,所有节点 UP,最近未做任何变更。

时间线(模拟排查全过程)


09:00 — 收到告警,登录数据库。

09:05 — Step 1:检查服务端负载均衡策略。

SELECT LOAD_BALANCE_POLICY FROM V_CATALOG.DATABASES;

→ 结果:roundrobin。服务端已启用,不是服务端问题。

09:08 — Step 2:检查查询发起分布。

SELECT node_name, COUNT(*) AS query_count
FROM v_internal.dc_requests_issued
WHERE "time" > sysdate() - 1
GROUP BY 1 ORDER BY 1;

输出:

node_name | query_count
node01    | 3,245
node02    | 2,891
node03    | 158,432   👈 异常!
node04    | 2,567
node05    | 3,012
node06    | 2,789

09:10 — 确认:node03 处理了全集群 90% 的查询。

09:12 — Step 3:检查哪些客户端连接集中在 node03。

SELECT
    regexp_substr(client_hostname, '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') AS client_ip,
    COUNT(*) AS conn_count
FROM v_monitor.sessions
WHERE node_name = 'node03' AND user_name <> 'dbadmin'
GROUP BY 1
ORDER BY 2 DESC;

输出:

client_ip      | conn_count
10.2.3.100     | 142        👈 来自同一台调度服务器
10.2.3.101     | 3
10.2.3.102     | 2

09:15 — Step 4:锁定问题来源。IP 10.2.3.100 是调度服务器。

SSH 登录该服务器检查 ETL 工具的 JDBC 连接配置:

grep -r "jdbc:vertica" /opt/etl/config/

发现:

jdbc:vertica://node03:5433/prod_db?user=etl_user&password=***

没有 ConnectionLoadBalance=1,且 Host 硬编码为 node03 的 IP。

09:20 — 修复:修改连接串。

jdbc:vertica://node01:5433/prod_db?ConnectionLoadBalance=1&BackupServerNode=node02:5433,node03:5433,node04:5433,node05:5433,node06:5433&user=etl_user&password=***

注意:URL 中的 host 改为 node01(任意节点均可),BackupServerNode 列出了其余所有节点作为备选。

09:30 — 滚动重启调度应用的 worker 进程。

10:00 — 验证效果:

SELECT node_name, COUNT(*) AS session_count
FROM v_monitor.sessions WHERE user_name = 'etl_user'
GROUP BY 1 ORDER BY 1;

输出:

node01: 24
node02: 23
node03: 25
node04: 22
node05: 24
node06: 23

10:15 — 检查 node03 CPU:

SELECT
    node_name,
    AVG(average_cpu_usage_percent) AS avg_cpu
FROM v_monitor.system_resource_usage
WHERE end_time > sysdate() - 1/24
GROUP BY 1;

→ node03 CPU 从 85% 降至 38%,与其他节点持平。

结论:根因是调度 ETL 工具的 JDBC 连接串硬编码了 node03 且未开启负载均衡。修复耗时 1 小时,性能恢复。


7. 快速诊断 SQL 工具箱

诊断目标 SQL 说明
查看当前负载均衡策略 SELECT LOAD_BALANCE_POLICY FROM V_CATALOG.DATABASES; 返回 none/roundrobin/random
启用负载均衡 SELECT SET_LOAD_BALANCE_POLICY('ROUNDROBIN'); 立即生效,无需重启
查看负载均衡组 SELECT name, policy, filter, type, object_name FROM v_catalog.load_balance_groups; 为空表示未定义任何组
查看路由规则 SELECT name, source_address, destination_name FROM v_catalog.routing_rules; 为空表示无 IP 路由规则
查看工作负载路由 SELECT workload, subcluster_name, priority FROM v_catalog.workload_routing_rules; v23.3+ 特性
查看节点状态和地址 SELECT node_name, node_state, export_address, subcluster_name FROM v_catalog.nodes ORDER BY 1; export_address 是客户端连接的目标地址
查看各节点查询发起数(1 天) SELECT node_name, COUNT(*) AS cnt FROM v_internal.dc_requests_issued WHERE "time" > sysdate() - 1 GROUP BY 1 ORDER BY 1; 差异大 = 连接不均衡
查看各节点活跃会话数 SELECT s.node_name, COUNT(*) AS cnt FROM v_monitor.sessions s WHERE s.current_statement IS NOT NULL GROUP BY 1 ORDER BY 1; 差异大 = 连接不均衡
查看各节点总会话数 SELECT node_name, COUNT(*) AS cnt FROM v_monitor.sessions WHERE user_name <> 'dbadmin' GROUP BY 1 ORDER BY 1; 全局视角
按客户端 IP 查看连接分布 SELECT node_name, regexp_substr(client_hostname, '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') AS ip, COUNT(*) FROM v_monitor.sessions WHERE user_name <> 'dbadmin' GROUP BY 1, 2 ORDER BY 2, 1; 定位哪个客户端未均衡
查看网络地址定义 SELECT name, node, address, port, is_enabled FROM v_catalog.network_addresses; 显式定义的网络地址
查看节点导出地址 SELECT node_name, export_address, node_address FROM v_catalog.nodes; export_address ≠ node_address 时说明有 NAT

8. 最佳实践清单

  1. 服务端和客户端必须同时配置 — 只配一端等于没配。服务端 SET_LOAD_BALANCE_POLICY('ROUNDROBIN') + 客户端 ConnectionLoadBalance=1,缺一不可。

  2. BackupServerNode 至少配 2 个备用节点 — 避免初始节点和唯一备用节点同时宕机的极端场景。列出所有节点最安全。

  3. JDBC URL 中 socketTimeout 不要设太短 — 设为 0(禁用)或至少 3600 秒。分析查询可能运行数分钟甚至数小时,短超时会误杀正常查询。

  4. 连接池配合 maxLifetime — 无论 HikariCP / C3P0 / Druid,设置连接的最大生命周期(建议 10-30 分钟),确保老连接定期回收重建,触发负载均衡。

  5. 不要同时启用原生负载均衡和外部负载均衡 — 双层均衡会导致 ROUNDROBIN 行为偏离预期(如 K8s Service 场景)。

  6. 外部 LB 的 TCP idle timeout 必须 > 最长事务时间 — 否则长查询会被 LB 断开,客户端收到 EOFException。

  7. 每季度巡检连接分布 — 使用第 7 节工具箱中的 SQL 定期检查各节点连接分布。建议设置 (max - min) / max > 0.3 作为告警阈值。

  8. 客户端驱动版本与服务端匹配 — 旧版驱动可能不完全支持 ConnectionLoadBalance 或存在已知 Bug。升级驱动往往比排查配置更高效。

  9. 多子集群场景务必配置路由规则 — 不要依赖客户端自行选择子集群。用 LOAD BALANCE GROUP + ROUTING RULE 明确隔离工作负载。

  10. 新建应用/工具时默认配置负载均衡 — 将包含 ConnectionLoadBalance=1 的 JDBC URL 模板写入团队 Wiki,避免因遗漏导致的重蹈覆辙。


扩展阅读