Vertica CPU 持续高负载诊断与优化¶
作者:JiangChong | 发布时间:2026-04-07
适用场景:集群 CPU 持续高负载(idle < 20%),查询性能下降,资源竞争严重。
关联:Vertica 资源池最佳实践 | Vertica 监控最佳实践 | Vertica 客户端连接负载均衡配置 | Vertica 性能调优 - 2 使用系统表排除 Vertica 查询性能故障 | Vertica 性能调优 - 1 如何阅读执行计划
理解全文脉络¶
本文按照「发现问题 → 定位根因 → 解决问题」的路径组织。如果你是第一次遇到 CPU 高负载问题,建议从第 1 节读起逐步推进;如果你已经有明确的怀疑方向(比如知道是某条 SQL 导致的),可以直接跳到第 3 节。
1. 理解 Vertica 的 CPU 消耗模型¶
1.1 为什么 CPU 对 Vertica 如此重要¶
Vertica 是一个 MPP(大规模并行处理)列存分析型数据库。与传统行存数据库(如 MySQL、PostgreSQL)不同,Vertica 的设计哲学是:
- 列存压缩:数据按列存储在磁盘上,读取时需要在内存中解压缩。压缩算法(LZO、Delta Val、RLE 等)虽然节省了磁盘空间,但解压缩本身需要消耗 CPU。
- 无索引设计:Vertica 不使用 B-Tree 索引,而是依赖投影(Projection)的排序键和分区裁剪来加速查询。这意味着每次查询都是对原始数据的扫描和过滤,CPU 参与度远高于传统数据库。
- 大规模并行:一个查询会被拆分成数十甚至数百个并行算子,分布在所有节点上同时执行。每个算子都是独立的线程,都需要占用 CPU 时间片。
- 内存换时间:Vertica 倾向于将数据尽量保留在内存中处理(如 Hash Join、Hash GroupBy),这能避免磁盘 I/O,但代价是大量 CPU 用于哈希计算。
理解这四点之后,就容易明白:CPU 高负载在 Vertica 中不一定是坏事——它说明集群正在全力工作。但持续超过 80% 的 CPU 利用率则说明计算资源已经饱和,新的查询将被迫排队等待。
1.2 CPU 消耗的四大来源¶
当我们在操作系统层面看到 CPU 负载很高时,这些 CPU 周期究竟花在了哪里?在 Vertica 内部,主要可以归为四类:
| 来源 | 说明 | 典型表现 |
|---|---|---|
| 查询执行(ExecutePlan) | SQL 查询的算子执行,如 Scan、Join、GroupBy、Sort。这是最核心的 CPU 消耗来源,通常占总 CPU 的 70%-95%。 | 大量查询同时运行或单条查询并行度过高 |
| Tuple Mover(TM) | 后台数据整理任务,包括 mergeout(将多个小 ROS 文件合并为一个大文件)和 moveout(将 WOS 内存数据刷写到磁盘 ROS)。 | ROS container 数量过大(>500),或加载频率极高 |
| Recovery(节点恢复) | 当某个节点宕机后重新加入集群时,需要从 buddy 节点同步缺失的数据。这个过程涉及大量数据校验和网络传输,CPU 开销很大。 | 节点频繁 UP/DOWN,或集群刚重启 |
| 其他系统开销 | 编码解码、网络数据收发、全局 catalog 锁争抢、JVM GC 等。虽然单项占比不大,但叠加后也会推高 CPU。 | 编码类型复杂、跨节点数据传输量大 |
1.3 诊断路由图¶
在实际排查中,建议按以下优先级依次排查,每排除一项再进入下一项:
第 1 步:系统级 CPU 使用率 → 确认是否真的高、哪些节点高
↳ 若各节点 CPU 差异 > 3 倍 → 优先排查连接倾斜(详见第 2.1 节)
第 2 步:排除 Recovery → 是否有节点在恢复中
第 3 步:排除 Tuple Mover → 是否有大量 mergeout/moveout
第 4 步:定位高消耗查询 → 找出具体的 SQL 语句
第 5 步:算子级分析 → 找到 SQL 内部的 CPU 热点操作符
第 6 步:针对性优化 → 调整资源池、改写 SQL、优化投影
下面我们将按照这个路线逐一展开。
2. 系统级 CPU 监控:先看清全局¶
2.1 使用 system_resource_usage 查看历史 CPU¶
Vertica 内置的 v_monitor.system_resource_usage 表会以固定间隔(默认每 10 秒)采集每个节点的 CPU、内存、网络、磁盘 I/O 指标。这是排查问题时首先要看的数据。
SELECT node_name,
end_time,
average_cpu_usage_percent,
average_memory_usage_percent,
net_rx_kbytes_per_second,
net_tx_kbytes_per_second,
io_read_kbytes_per_second,
io_written_kbytes_per_second
FROM v_monitor.system_resource_usage
WHERE end_time > sysdate - INTERVAL '1 hour'
ORDER BY end_time DESC;
如何解读结果:
average_cpu_usage_percent > 80%且持续超过 5 分钟:说明 CPU 确实成为瓶颈。此时操作系统调度器会频繁切换线程上下文,反而降低整体吞吐量。- 各节点 CPU 使用率差异很大:比如 node1 和 node2 跑满 95%,而 node3 只有 30%——这说明可能存在数据倾斜(某些节点承担了更多数据),或者连接倾斜(所有客户端连接集中在少数节点,这些节点的 initiator 承担了全部 SQL 的编译和计划生成工作)。此时先用以下 SQL 快速确认是否为连接倾斜:
-- 检查过去 1 天各节点发起的查询数,差异大则说明连接未均衡
SELECT node_name, COUNT(*) AS query_count
FROM v_internal.dc_requests_issued
WHERE "time" > sysdate() - 1
GROUP BY 1 ORDER BY 1;
如果 80% 以上的查询集中在 30% 以下的节点上,则是连接倾斜问题。先修连接均衡再继续排查——连接倾斜修复后 CPU 通常会自动恢复,无需继续后面的步骤。详见 Vertica 客户端连接负载均衡配置。 - CPU 高但内存、网络、磁盘 I/O 都正常:这是典型的纯计算密集型负载,优化重点放在 SQL 和并行度上。 - CPU、网络收发、磁盘 I/O 同时高:可能是大表全表扫描 + 跨节点重分段(RESEGMENT)。查询不仅要读大量数据,还要通过网络传输到其他节点重新分布。
2.2 使用 Linux 命令辅助定位¶
有时问题不一定出在 Vertica 内部,也可能是操作系统层面的资源争抢。常用的 Linux 排查命令:
# 1. 查看 Vertica 进程的总体 CPU 占用
top -b -n1 -p $(pgrep -d',' vertica)
# 2. 按线程级别查看——Vertica 是多线程执行,单个线程可能跑满一个核心
top -H -b -n1 -p $(pgrep -d',' vertica) | head -20
# 3. 查看系统整体负载(1 分钟、5 分钟、15 分钟均值)
uptime
如何解读:
- load average 远超 CPU 核心数:比如 48 核机器,load average 持续在 60+,说明有大量进程/线程在排队等待 CPU,系统严重过载。
- top 中
%us(user space)高:说明 CPU 主要花在 Vertica 的用户态计算上,属于正常消耗。 - top 中
%sy(system/kernel)高:说明 CPU 大量花在内核态,可能是系统调用过多、上下文切换过频、或网络/磁盘中断密集。需要检查操作系统层面配置(如 IRQ affinity、NUMA 绑定)。
3. 定位 CPU 消耗来源:从宏观到微观¶
3.1 第一步:确认是否有 Recovery 在进行¶
Recovery 是 CPU 消耗大户,而且它是「被动」的——不是由你的查询触发,而是节点状态变化触发。所以要首先排除。
SELECT node_name,
recover_epoch,
recovery_phase,
current_completed,
current_total,
is_running
FROM v_monitor.recovery_status
WHERE is_running = 't'
ORDER BY node_name;
如果 is_running = 't':
recovery_phase告诉你当前处于恢复的哪个阶段(拷贝数据、重放 WOS、校验一致性等)current_completed / current_total显示恢复进度- 此时 CPU 高是正常的,不建议做其他优化——等 Recovery 完成后再观察
- 如果 Recovery 频繁发生,重点排查该节点的硬件健康度(内存、磁盘)和网络稳定性
如果 Recovery 早就结束了但 CPU 还是高:继续下一步排查。
3.2 第二步:确认是否有 Tuple Mover 大量工作¶
Tuple Mover(TM)是 Vertica 的后台数据整理组件。它有两个核心操作:
- Moveout:将 WOS(Write Optimized Store,写入优化存储)中的数据批量转移到 ROS(Read Optimized Store,读取优化存储)。WOS 是内存结构,ROS 是磁盘文件。
- Mergeout:将多个小 ROS 文件合并为较大的 ROS 文件。ROS 文件数量过多会影响查询性能(因为每次查询需要打开大量文件句柄),所以 TM 会定期将小文件合并。
这两个操作在处理大量数据时 CPU 消耗很高——因为涉及排序、压缩、重新编码等计算密集型任务。
SELECT node_name,
operation_name,
table_schema,
table_name,
is_executing,
operation_start_timestamp
FROM v_monitor.tuple_mover_operations
WHERE is_executing = 't'
ORDER BY operation_start_timestamp;
TM 高 CPU 的常见诱因及解决思路:
| 诱因 | 现象 | 解决方向 |
|---|---|---|
| 大量小批次加载(每次几百行) | ROS container 数量快速增长到 500+,mergeout 频繁触发 | 合并加载批次,每次至少加载几十万行 |
| 分区键设计不当,每次加载跨多个分区 | 单次加载产生 N 个分区 × K 个节点个 ROS 文件 | 重新评估分区策略:分区键是否粒度太细? |
| MoveOutInterval 设置过短 | WOS 还没满就被频繁刷写到磁盘 | 适当增大 MoveOutInterval,让 WOS 积累更多数据再批量写入 |
| 表没有分区,数据无限增长 | 单个 ROS 文件无限增大,mergeout 耗时指数增长 | 为大表添加分区,利用 DROP_PARTITION 定期清理旧数据 |
3.3 第三步:通过 query_requests 找到高消耗的 SQL¶
排除 Recovery 和 TM 之后,剩下最大的 CPU 消耗者就是用户查询。此时我们需要从 Vertica 系统表中找出「罪魁祸首」。
v_monitor.query_requests 是 Vertica 最重要的查询监控表之一。它记录了所有已完成和正在执行的 SQL 请求的详细信息——包括谁提交的、什么时候开始、执行了多久、用了多少内存。
-- 先看当前正在运行的查询,按执行时间从长到短排列
SELECT node_name,
user_name,
session_id,
transaction_id,
statement_id,
request_duration_ms,
memory_acquired_mb,
LEFT(request, 200) AS query_text
FROM v_monitor.query_requests
WHERE is_executing = 't'
ORDER BY request_duration_ms DESC
LIMIT 20;
-- 如果当前没有异常,再看最近 1 小时的历史慢查询
SELECT user_name,
start_timestamp,
request_duration_ms,
memory_acquired_mb,
request
FROM v_monitor.query_requests
WHERE start_timestamp > sysdate - INTERVAL '1 hour'
AND request_type = 'QUERY'
AND request_duration_ms > 30000 -- 只关注运行超过 30 秒的查询
ORDER BY request_duration_ms DESC
LIMIT 20;
如何解读:
request_duration_ms很大(几十万毫秒甚至更多):该查询运行了很长时间。如果它还在运行中(is_executing = 't'),它很可能就是当前 CPU 高负载的元凶。- 多条查询同时具有很大的
request_duration_ms:说明不是某一条 SQL 的问题,而是并发度过高,大量查询同时争抢 CPU。这时需要从资源池层面做并发控制,而不是优化单条 SQL。 memory_acquired_mb异常大(几十 GB):该查询申请了极多内存。大内存通常伴随大 CPU 消耗——因为大量数据在内存中参与哈希计算和排序。
确定要分析的查询后,将 transaction_id 和 statement_id 记下并赋给 vsql 变量,后续所有深入分析的 SQL 都会复用它:
3.4 第四步:用 PROFILE 分析查询的执行引擎细节¶
这是 CPU 诊断最关键的一步。PROFILE 关键字会让 Vertica 在执行查询的同时,将每个算子(operator)的详细性能指标写入 execution_engine_profiles 系统表。
操作流程:
-- 1. 打开计时器,关闭结果输出(因为我们要的是性能数据,不是查询结果本身)
\timing
\o /dev/null
-- 2. 用 PROFILE 关键字重新执行你要分析的查询
PROFILE SELECT ...;
-- 3. 恢复输出
\o
PROFILE 之后 Vertica 会给出几个关键提示,理解这些提示是诊断的起点:
NOTICE 4788: Statement is being profiled
HINT: Select * from v_monitor.execution_engine_profiles where transaction_id=... and statement_id=...;
NOTICE 3557: Initiator memory for query: [on pool general: 1548488 KB, minimum: 1209481 KB]
NOTICE 5077: Total memory required by query: [1548488 KB]
Time: First fetch (1000 rows): 718.507 ms. All rows formatted: 832.033 ms
Initiator memory for query:查询在发起节点申请的内存总量。如果这个值接近或超过资源池的query_budget,说明内存预算刚好够用甚至偏紧。Total memory required by query:查询在所有节点上总共需要的内存。如果这个值远大于query_budget,说明查询需要多次向 Resource Manager 申请额外内存(AcquireAdditional请求),每次申请都有开销。Time: First fetch:返回第一批结果的时间。对于大查询,如果这个值远大于总执行时间的一半,说明瓶颈在查询的中间步骤(如 JOIN 或 GROUP BY),而不是最后的排序输出。
3.5 第五步:在 execution_engine_profiles 中定位 CPU 热点算子¶
execution_engine_profiles 表可能包含数千行数据——每一个算子的每一个计数器都是一行。我们需要聚合分析,按算子类型和路径汇总 CPU 时间。
-- 找出最耗 CPU 的算子和执行路径
SELECT operator_name,
path_id,
SUM(counter_value) AS total_time_us
FROM execution_engine_profiles
WHERE transaction_id = :t_id
AND statement_id = :s_id
AND counter_name ILIKE 'execution%'
GROUP BY operator_name, path_id
ORDER BY 3 DESC
LIMIT 20;
先理解两个关键概念:
path_id:执行路径的编号。一个查询的执行计划由多条路径组成,每条路径代表一个数据流分支。路径之间有的是串行依赖关系(路径 A 的结果必须传给路径 B),有的是并行独立关系(路径 A 和路径 B 可以同时执行)。operator_name:算子类型。Vertica 的执行引擎将 SQL 拆解成一系列算子,数据从底部的 Scan 算子流入,经过 Join、Filter、GroupBy 等算子逐层处理,最终从 Root 算子输出结果。
常见高 CPU 算子的深入解读:
| 算子 | 它做什么 | 为什么会 CPU 高 | 优化方向 |
|---|---|---|---|
GroupByHash |
在内存中构建哈希表来聚合数据。所有 GROUP BY 列的值被哈希后放入哈希桶中。 | 聚合的数据量太大,哈希表装不下内存,数据被迫溢出到磁盘(Spill)。溢出的数据需要排序后再聚合,排序是 CPU 密集操作。 | ① 增大资源池的 query_budget,让哈希表有更多内存;② 创建按 GROUP BY 列排序的投影,让优化器用 GroupByPipe(流式聚合,不需要哈希表)替代 GroupByHash;③ 减少 GROUP BY 的列数。 |
JoinHash |
将 JOIN 的内表(较小的那张表)完全加载到内存中构建哈希表,然后扫描外表进行匹配。 | 内表太大导致哈希表构建时间长;或者哈希表太大导致内存频繁换页。 | ① 确保统计信息准确,这样优化器能正确选择内外表顺序;② 对齐两张表的 SEGMENTED BY 键,使 JOIN 在同一节点内完成,避免跨节点数据传输;③ 如果内表确实太大,可考虑预关联投影(pre-join projection)。 |
Scan |
从磁盘读取列数据并解压缩。每一列的解压都需要 CPU 参与运算。 | 扫描的数据量太大(全表扫描);列编码类型复杂导致解压 CPU 高;没有分区裁剪导致读取了不需要的 ROS 文件。 | ① 添加 WHERE 谓词匹配分区键,启用分区裁剪;② CPU 瓶颈时考虑将编码改为 NONE 或 RLE(牺牲压缩率换 CPU);③ 确保查询只 SELECT 需要的列,不用的列不要写 SELECT *。 |
NetworkSend / NetworkRecv |
将数据通过网络发送到其他节点(send),或接收其他节点发来的数据(recv)。 | 表的 SEGMENTED BY 键与 JOIN 键不一致,导致数据需要跨节点重新分布(RESEGMENT)。每一条数据都要经过序列化、网络传输、反序列化三个 CPU 密集步骤。 |
① 对齐 JOIN 两端表的 SEGMENT BY 键;② 如果 RESEGMENT 不可避免,确保网络带宽充足(万兆网卡、UDP 缓冲区足够)。 |
Sort |
对数据流进行排序。如果内存足够,用快排;不够则用外部归并排序。 | 排序的数据量超过内存,退化为外部排序,涉及大量磁盘 I/O 和多次归并比较。 | ① 确保投影的 ORDER BY 列与查询的 ORDER BY 一致,这样数据本身已经排好序,Vertica 可以跳过 Sort 算子;② 增大 query_budget 提供更多排序内存。 |
ExprEval |
计算表达式,如 C1 + C2、UPPER(name)、CASE WHEN 等。 |
表达式复杂(嵌套多层函数),或者表达式应用于数亿行数据。 | ① 简化表达式,将复杂计算在 ETL 阶段预先算好存入表;② 避免在 WHERE 子句中对列使用函数(如 WHERE UPPER(name) = 'ABC'),这会阻止分区裁剪和索引优化。 |
3.6 第六步:按节点拆分,识别数据倾斜¶
同样的查询,在不同节点上的 CPU 消耗应该大致均衡。如果某个节点的 CPU 时间明显比其他节点高,说明数据分布不均匀。
SELECT node_name,
operator_name,
path_id,
SUM(counter_value) AS sum_time,
COUNT(DISTINCT operator_id) AS num_operators
FROM dc_execution_engine_profiles
WHERE transaction_id = :t_id
AND statement_id = :s_id
AND counter_name ILIKE 'execution%'
GROUP BY node_name, operator_name, path_id
ORDER BY 4 DESC
LIMIT 20;
如何解读:
- 某节点的
sum_time是其他节点的 2 倍以上:该节点存储的数据量远超其他节点(数据倾斜)。需要检查投影的分段键(SEGMENTED BY)是否选择了基数不均匀的列。比如按gender分段(只有男/女两个值),数据只会分布到两个节点上。 num_operators远小于EXECUTIONPARALLELISM:该查询的并行度没有达到资源池配置的期望值。可能的原因包括:ROS container 数量太少不足以拆分为更多并行算子;或者EXECUTIONPARALLELISM设置过高但实际数据量无法支撑那么多并行线程。
如何验证数据倾斜:
SELECT node_name,
projection_schema,
projection_name,
SUM(row_count) AS row_count
FROM v_monitor.projection_storage
WHERE projection_name = 'your_projection_name'
GROUP BY 1, 2, 3
ORDER BY 4 DESC;
如果某节点的 row_count 占总量 50% 以上(在 3 节点集群中),说明数据严重倾斜。根本原因几乎都是分段键选择不当。
3.7 第七步:区分 CPU 时间与墙上时钟时间¶
这是容易被忽略但非常有用的分析维度。execution_engine_profiles 有两个关键计数器:
execution time (us):线程实际占用 CPU 的计算时间(CPU time)。如果你有 4 个并行线程各跑了 10 秒,execution time = 40 秒。clock time (us):算子从开始到结束的墙上时钟时间(wall-clock time),包括等待 I/O、等待网络、等待上游算子的时间。同样 4 线程各跑 10 秒,clock time = 10 秒。
SELECT path_id, operator_name,
SUM(DECODE(counter_name, 'execution time (us)', counter_value, NULL)) AS cpu_time_us,
SUM(DECODE(counter_name, 'clock time (us)', counter_value, NULL)) AS clock_time_us,
SUM(DECODE(counter_name, 'execution time (us)', counter_value, NULL)) /
NULLIF(SUM(DECODE(counter_name, 'clock time (us)', counter_value, NULL)), 0)::FLOAT AS cpu_ratio
FROM execution_engine_profiles
WHERE transaction_id = :t_id
AND statement_id = :s_id
GROUP BY path_id, operator_name
ORDER BY cpu_time_us DESC;
如何解读 cpu_ratio:
cpu_ratio≈ 1.0 或更高(高可能因为多线程并行):纯 CPU 密集型。算子在拼命计算,几乎没有等待。优化方向是提高单线程效率(改进算法、简化表达式)或降低并行度释放 CPU 给其他查询。cpu_ratio< 0.5:算子大部分时间在等待——要么等磁盘 I/O(Scan 算子常见),要么等网络数据(NetworkRecv 算子常见),要么等上游算子产出数据(对应input queue wait (us)计数器)。优化方向是减少等待源(优化 I/O、减少数据传输)。
3.8 第八步:检查查询阶段耗时——CPU 花在了哪一步¶
一个查询从提交到完成,要经历多个阶段。dc_query_executions 表记录了每个阶段在初始化节点和执行节点的耗时。
-- 初始化节点(initiator node)的各阶段耗时
SELECT node_name, execution_step,
(completion_time - "time") AS duration
FROM v_internal.dc_query_executions
WHERE transaction_id = :t_id
AND statement_id = :s_id
AND node_name = (SELECT local_node_name())
ORDER BY "time";
各阶段的含义和异常判断:
| 阶段 | 做什么 | 异常信号 | 可能原因 |
|---|---|---|---|
Plan |
优化器分析 SQL、评估统计信息、生成最优执行计划 | 耗时 > 1 秒 | 涉及的投影太多(>1000 个),或统计信息缺失导致优化器评估不准,或需排队等 catalog 锁 |
SerializePlan |
将执行计划序列化并通过 spread 发送给所有执行节点 | 耗时 > 500ms | UDP 网络问题(丢包、超时),集群 spread 通信异常 |
CompilePlan:ReserveResources |
向 Resource Manager 申请内存和线程 | 耗时异常长 | 资源池资源紧张,需排队等待其他查询释放资源 |
CompilePlan:EEpreexecute |
初始化算子:分配内存、启动线程、打开网络连接 | 耗时异常长 | 需要打开的文件句柄太多(ROS container 过多),或线程启动开销大 |
ExecutePlan |
实际执行查询,数据在各算子间流动处理 | 占总时间 90%+ 是正常的 | 如果占比太低(比如只占 50%),说明时间花在了规划和资源申请上,问题不在查询本身 |
AbandonPlan |
清理资源、释放锁、关闭连接 | 耗时 > 1 秒 | 也是 UDP 通信问题(发送 AbandonPlan 消息到所有节点) |
4. 使用资源池控制 CPU 消耗¶
前面的步骤定位了 CPU 消耗来源。接下来我们讨论如何控制和限制 CPU 使用——这是治标(防止单条查询或某类工作负载独占 CPU)和治本(合理分配资源)结合的手段。
4.1 理解资源池与 CPU 的关系¶
Vertica 的资源池(Resource Pool)本质上是一个资源限额系统。它定义了:
- 内存:每个池最多能用多少内存(
MAXMEMORYSIZE) - 并发:每个池最多同时运行多少条查询(
MAXCONCURRENCY) - 并行度:每条查询最多能使用多少个 CPU 线程(
EXECUTIONPARALLELISM) - 查询预算:每条查询的初始内存配额(
query_budget)
这些参数共同决定了 CPU 的分配方式。核心逻辑是:
EXECUTIONPARALLELISM决定了单条查询能吃多少 CPU →MAXCONCURRENCY决定了同时有多少条查询在吃 CPU → 两者相乘大致决定了该资源池的总 CPU 占用。
默认资源池 general 的行为:
EXECUTIONPARALLELISM = AUTO(等于 CPU 核心数,如 48 核则 48 线程)MAXCONCURRENCY无硬限制PLANNEDCONCURRENCY = AUTO(也等于 CPU 核心数)
这意味着:默认配置下,每一条查询都可能尝试占用所有 CPU 核心。如果同时有 5 条查询在运行,每条申请 48 个线程,就会有 240 个线程争抢 48 个核心,上下文切换开销急剧上升,整体吞吐量反而下降。
4.2 核心参数详解¶
| 参数 | 作用 | 对 CPU 的直接影响 | 经验建议 |
|---|---|---|---|
EXECUTIONPARALLELISM |
单条查询最多使用的线程数 | 最关键参数。值越大,单查询能并行处理的算子越多,CPU 占用越高。设置为 AUTO 时等于节点 CPU 核心数。 | 高并发 OLTP 类短查询:4-8;中等分析查询:16-24;大型 ETL/报表:32-48。总原则是 核心数 × 资源池数 × 40% 左右。 |
MAXCONCURRENCY |
资源池最多同时运行的查询数 | 超出后新查询进入队列等待,不会消耗 CPU。这是防止 CPU 过载的「硬刹车」。 | 设置为 PLANNEDCONCURRENCY 的 50%-80%。过大会导致 CPU 过载,过小会导致查询排队时间过长。 |
PLANNEDCONCURRENCY |
优化器用于计算 query_budget 的预期并发数 |
不直接控制 CPU,但影响内存预算,间接影响查询是否会 spill 到磁盘(spill = 额外 CPU 消耗)。 | 设置为高峰期预期的并发查询数的 1.2-1.5 倍,留一些余量。 |
RUNTIMECAP |
单条查询的最长运行时间 | 超时后查询被自动终止,释放 CPU。防止「失控查询」长期占用资源。 | 短查询池:1-5 分钟;中等查询:15-30 分钟;长报表/ETL:设置一个合理上限(如 2 小时)。 |
QUEUETIMEOUT |
查询在队列中最长等待时间 | 超时后查询被取消。防止查询永久排队。 | 通常设置为 300-600 秒。过短会导致高并发时大量查询被取消。 |
4.3 策略一:降低 EXECUTIONPARALLELISM —— 牺牲单查询速度换吞吐¶
这是解决 CPU 过载最直接有效的方法。原理很简单:不让任何单条查询独占所有 CPU 核心。
-- 创建一个适合高并发场景的资源池
CREATE RESOURCE POOL high_concurrency_pool
MEMORYSIZE '0%' -- 不预留独占内存
MAXMEMORYSIZE '60%' -- 池中所有查询最多用 60% 节点内存
PLANNEDCONCURRENCY 60 -- 预期 60 条查询同时运行
MAXCONCURRENCY 30 -- 硬限制最多 30 条
EXECUTIONPARALLELISM 4 -- 每条查询最多 4 个线程
PRIORITY 0;
不同工作负载的推荐值:
| 工作负载类型 | EXECUTIONPARALLELISM | 原因 |
|---|---|---|
| ETL / COPY 数据加载 | 4-8 | 数据加载主要是 I/O 密集型,过多的 CPU 线程反而增加锁竞争 |
| 短 ad-hoc 查询(< 5 秒) | 8-16 | 短查询需要快速响应,适度并行能显著降低延迟 |
| BI 报表查询(10-120 秒) | 16-32 | 需要较高并行度扫描大量数据,但也不能无限放大 |
| 大型批处理 / 数据导出 | AUTO / 2 |
保留一半核心给其他工作负载 |
将用户分配到自定义资源池:
-- 将用户分配到指定资源池
ALTER USER analyst_user RESOURCE POOL high_concurrency_pool;
-- 或者在会话级别临时切换
SET SESSION RESOURCE POOL high_concurrency_pool;
4.4 策略二:级联资源池 —— 长短查询分离¶
理想情况下,短查询(几秒完成)和长查询(几分钟甚至几小时)应该分开处理。但用户往往不知道自己的查询是长是短。级联资源池(Cascade Resource Pool)的思路是:
- 所有查询先进入「快速池」,获得较小的内存预算和适中的并行度
- 如果查询在规定时间内(
RUNTIMECAP)没完成,自动级联到下一级池 - 下一级池可以设置更大的内存预算和更高的并行度,但并发限制更严格
- 逐级级联,直到最大超时被终止
-- 第一级:快速池(3 秒内完成)
-- 内存预算小(fast in, fast out),并行度适中
CREATE RESOURCE POOL fast_pool
MAXMEMORYSIZE '40%'
PLANNEDCONCURRENCY 30
EXECUTIONPARALLELISM 8
RUNTIMECAP '00:00:03'
CASCADE TO medium_pool;
-- 第二级:中速池(3 秒到 15 秒)
-- 级联到此的查询会重新规划,获得更大的内存预算和更高的并行度
CREATE RESOURCE POOL medium_pool
MAXMEMORYSIZE '20%'
PLANNEDCONCURRENCY 15
EXECUTIONPARALLELISM 16
RUNTIMECAP '00:00:15'
CASCADE TO slow_pool;
-- 第三级:慢速池(15 秒到 5 分钟)
-- 最大并行度但最少并发数,只允许少数几条大查询同时跑
CREATE RESOURCE POOL slow_pool
MAXMEMORYSIZE '10%'
PLANNEDCONCURRENCY 3
EXECUTIONPARALLELISM 24
RUNTIMECAP '00:05:00';
关键配置:启用级联重新规划
默认情况下,级联到新池的查询不会重新规划(replan),只是换个地方继续跑。但配合 CascadeResourcePoolAlwaysReplan = 1 会在级联时重新规划查询——这样可以获得新池的更大 query_budget,对大查询的性能提升非常明显。
级联池的工作流程图:
所有 SELECT 查询 → fast_pool(3s / 8线程 / 小预算)
│
├─ 3秒内完成 → 返回结果
│
└─ 超时 → 级联到 medium_pool(15s / 16线程 / 中等预算)
│
├─ 15秒内完成 → 返回结果
│
└─ 超时 → 级联到 slow_pool(5min / 24线程 / 大预算)
│
├─ 5分钟内完成 → 返回结果
│
└─ 超时 → 查询被终止
4.5 策略三:监控资源池健康度¶
创建资源池后,需要持续监控它是否按预期运行:
-- 查看各资源池的当前并发和内存状态
SELECT pool_name,
running_query_count,
planned_concurrency,
max_concurrency,
query_budget_kb,
memory_inuse_kb,
general_memory_borrowed_kb
FROM resource_pool_status
WHERE pool_name NOT IN ('sysquery', 'sysdata')
ORDER BY running_query_count DESC;
需要关注的信号:
running_query_count持续等于max_concurrency:并发已打满,新查询在排队。考虑增大MAXCONCURRENCY或优化慢查询。general_memory_borrowed_kb > 0:该池从 general 池借了内存,说明MAXMEMORYSIZE不够用。query_budget_kb只有几百 MB:内存预算太小,几乎所有查询都会频繁 spill 到磁盘,导致 CPU 消耗翻倍。
5. 调整查询计划来降低 CPU 消耗¶
资源池控制的是「量」(多少查询同时跑、每条能用多少资源),而查询计划优化控制的是「质」(每条查询本身的效率)。两者配合才能真正解决 CPU 高负载问题。
5.1 负面事件检查 —— 快速定位问题方向¶
Vertica 在执行每条查询时,会记录「查询事件」(Query Events)。事件分为正面(优化做得好)和负面(有问题需要关注)。排查时应首先关注负面事件。
SELECT event_type,
event_category,
operator_name,
path_id,
event_description,
suggested_action
FROM query_events
WHERE transaction_id = :t_id
AND statement_id = :s_id
AND event_category = 'EXECUTION'
ORDER BY operator_name;
最常见的几个负面事件及其解决方向:
| 事件类型 | 含义 | 为什么会增加 CPU | 解决方向 |
|---|---|---|---|
GROUP_BY_SPILLED |
GROUP BY 的哈希表装不进内存,数据溢出到磁盘 | 溢出到磁盘的数据需要排序后再聚合——排序是 CPU 密集操作,而且磁盘 I/O 增加了等待时间,线程空转更多 | ① 增大 query_budget;② 创建按 GROUP BY 列排序的投影,让优化器用 GroupByPipe(流式聚合)代替 GroupByHash |
JOIN_SPILLED |
JOIN 的内表装不进内存,退化为外部归并连接 | 外部归并连接需要先排序再归并,CPU 消耗远超哈希连接 | ① 增大 query_budget;② 检查统计信息是否准确(优化器可能选错了内外表);③ 考虑为 JOIN 建预连接投影 |
RESEGMENTED_MANY_ROWS |
大量数据行在查询执行期间被跨节点重新分段 | 每条数据都要序列化 → 网络发送 → 网络接收 → 反序列化,每一步都是 CPU 消耗 | ① 对齐 JOIN 两张表的 SEGMENTED BY 键;② 如果无法对齐,检查是否是真正的查询需求——能否改变查询逻辑避免跨节点 JOIN |
MEMORY_LIMIT_HIT |
优化器在规划查询时用光了分配的内存(默认 100MB) | 优化器无法生成最优计划,退而求其次生成次优计划,导致执行效率下降 | ① 增加 MaxOptMemMB 配置参数;② 删除不用的旧投影减少优化器评估对象 |
5.2 虚构案例一:GROUP BY Spill 的深入诊断与修复¶
📝 虚构案例
场景:某报表查询对 5 亿行数据做 GROUP BY region, product_category, date 聚合,执行时间超过 10 分钟,CPU 使用率持续 95%。
诊断步骤:
-- 1. 确认负面事件
SELECT event_type, suggested_action
FROM query_events
WHERE transaction_id = :t_id AND statement_id = :s_id;
-- 结果: GROUP_BY_SPILLED + "Consider a sorted projection. Increase memory available to the plan."
-- 2. 查看 GroupByHash 算子的内存和溢出情况
SELECT operator_name, counter_name, SUM(counter_value)
FROM execution_engine_profiles
WHERE transaction_id = :t_id AND statement_id = :s_id
AND operator_name = 'GroupByHash'
GROUP BY operator_name, counter_name
ORDER BY 3 DESC;
-- 关注: memory allocated, size of raw temp data (bytes)
-- 如果 temp data 很大 → 确实大量溢出到磁盘
修复方案(按推荐优先级):
-
创建按 GROUP BY 列排序的投影(首选,效果最好):
这样数据按 region → product_category → date 顺序物理排序存储在磁盘上。查询时 Vertica 可以直接用CREATE PROJECTION sales_grouped_proj AS SELECT * FROM sales_fact ORDER BY region, product_category, date SEGMENTED BY HASH(region) ALL NODES;GroupByPipe流式聚合——每读完一组的最后一行就输出聚合结果,全程不需要哈希表,也不会有 Spill。 -
增大内存预算(次选,快速见效):
-
减少 GROUP BY 粒度(如果业务允许): 如果用户不需要按
date分组,去掉它可以显著减少哈希表大小。
5.3 虚构案例二:Join RESEGMENT 的深入诊断与修复¶
📝 虚构案例
场景:两个大表 orders(按 order_id 分段)和 customers(按 customer_id 分段)做 JOIN,JOIN 条件是 orders.customer_id = customers.customer_id。执行计划中出现 RESEGMENTED_MANY_ROWS 事件。
为什么会有 RESEGMENT?
因为 orders 的分段键是 order_id,但 JOIN 键是 customer_id。Vertica 无法在同一节点内完成 JOIN(因为 order_id 分段的 orders 数据分布在所有节点,customer_id 分段的 customers 数据也分布在所有节点,但两者的分布规则不同)。数据库必须把其中一张表的数据按 customer_id 重新分布到各节点——这就是 RESEGMENT。
修复方案:
将 orders 的分段键改为 customer_id,与 JOIN 键对齐:
CREATE PROJECTION orders_segmented_by_cust AS
SELECT * FROM orders
SEGMENTED BY HASH(customer_id) ALL NODES;
这样两张表的数据按相同的键(customer_id)分布在相同的节点上,JOIN 可以在本地完成,不需要任何网络传输。
如何判断是否应该改分段键?
如果这个 JOIN 是系统中最频繁执行的查询之一,那么值得为该查询调整分段设计。如果只是偶尔跑一次,那么 RESEGMENT 的开销是可以接受的。
5.4 虚构案例三:编码与 CPU 的权衡¶
📝 虚构案例
Vertica 的列编码(Encoding)是个「用 CPU 换磁盘空间」的机制。编码类型越复杂,压缩率越高,但解压缩需要的 CPU 也越多。
常见编码类型的 CPU 消耗排序(从低到高):
AUTO 编码在大多数情况下选择了压缩率较高的类型,对于 CPU 瓶颈的场景可能不够友好。
如何判断编码是否成为 CPU 热点:
-- 查看 Scan 算子的 execution time 和 rows produced
-- 如果 execution time 很大但 rows produced 不是特别多
-- 可能是编码解压消耗了大量 CPU
SELECT operator_name, counter_name, AVG(counter_value)
FROM execution_engine_profiles
WHERE transaction_id = :t_id AND statement_id = :s_id
AND operator_name = 'Scan'
AND counter_name IN ('execution time (us)', 'rows produced',
'rle rows produced', 'bytes read from disk')
GROUP BY operator_name, counter_name;
rle rows produced接近rows produced:说明大部分数据以 RLE 格式直接处理,没有物化(解压)——这是理想状态,CPU 消耗低。bytes read from disk很小但execution time很大:数据压缩率很高,但解压很花 CPU——可以尝试降低编码复杂度。
修改编码:
-- 对 CPU 瓶颈场景,改为 NONE 或 RLE
ALTER PROJECTION big_fact_b0 ALTER COLUMN metric_col SET ENCODING NONE;
ALTER PROJECTION big_fact_b0 ALTER COLUMN dim_key_col SET ENCODING RLE;
注意:修改编码需要刷新投影(
REFRESH PROJECTION),会触发数据重写。在生产环境操作前先评估影响范围。
5.5 统计信息的重要性¶
你可能会疑惑:为什么统计信息缺失会导致 CPU 高?因为优化器依赖统计信息来估算每个执行计划步骤的成本。没有统计信息,优化器只能猜测——猜错了就生成次优计划,导致不必要的 CPU 消耗。
例如:优化器需要决定 JOIN 的内外表顺序。如果它不知道小表只有 1000 行而大表有 1 亿行,它可能错误地把大表当作内表加载到内存中构建哈希表——这会导致巨大的内存消耗和后续的 spill。
-- 已验证:Vertica v26.1.0-2
SELECT projection_name,
projection_column_name,
statistics_type,
statistics_updated_timestamp
FROM v_catalog.projection_columns
WHERE table_name IN ('your_key_table1', 'your_key_table2')
AND sort_position >= 0 -- 只查排序键列的统计信息
ORDER BY projection_name, sort_position;
-- 补充或更新统计信息
SELECT ANALYZE_STATISTICS('schema.table_name');
最佳实践:每次大批量加载数据后,对受影响的大表运行 ANALYZE_STATISTICS。可以设置定时任务自动执行。
5.6 分区裁剪 —— 让 Vertica 少读不必要的数据¶
分区裁剪(Partition Pruning)是 Vertica 减少 Scan 消耗的核心机制。它的原理很简单:如果表按日期分区,查询中带有 WHERE date = '2026-05-01',Vertica 只会读取 date=2026-05-01 这个分区的 ROS 文件,其他分区的文件直接跳过。
没有分区裁剪 → 全表扫描所有 ROS 文件 → 解压所有数据 → 在过滤器中丢弃不需要的行 → 巨大 CPU 浪费。
-- 检查表是否有分区
SELECT table_schema, table_name, partition_expression
FROM v_catalog.tables
WHERE partition_expression IS NOT NULL;
-- 为没有分区的大表添加分区
ALTER TABLE large_fact_table PARTITION BY date_column;
分区键的选择原则:
- 选择查询中最常用的过滤条件列(通常就是日期列)
- 分区基数不宜太大也不宜太小——每天一个分区很合理,每分钟一个分区就是灾难
- 分区表达式会应用到该表的所有投影,不需要在每个投影上单独定义
5.7 SIP(横向信息传递)的性能评估¶
SIP(Sideways Information Passing)是 Vertica 的一种优化技术:在 Hash Join 中,优化器会将内表的过滤条件「传递」给外表,在 Scan 阶段就提前过滤掉不匹配的行,减少后续 JOIN 的数据量。
但 SIP 本身也有 CPU 开销(过滤逻辑需要在 Scan 时执行)。如果 SIP 过滤掉的行很少(比如 < 5%),它的开销可能大于收益。
-- 评估 SIP 的过滤效果
SELECT node_name, sip_expr_id,
rows_processed, rows_pruned,
ROUND((rows_pruned::FLOAT / NULLIF(rows_processed, 0)) * 100, 2) AS prune_pct
FROM dc_sips_statistics
WHERE transaction_id = :t_id AND statement_id = :s_id
ORDER BY prune_pct DESC;
prune_pct > 20%:SIP 效果很好,过滤掉了大量不必要的数据,应保留。prune_pct < 5%:SIP 几乎没过滤掉什么数据,但开销仍存在。可以考虑在该查询中禁用 SIP:
5.8 真实案例一:CPU 降频导致集群性能持续恶化¶
📋 真实案例
背景:某运营商经分系统,138 节点 Vertica v11.1.1-20 集群,运行于国产欧拉操作系统(BigCloud Enterprise Linux For Euler 21.10)。硬件为 HP DL380 Gen9 及少量 RH2288HV5 服务器。
现象:业务高峰期(下午至晚间)查询响应时间逐步恶化,长查询从正常 2-3 分钟延长至 15-20 分钟。系统级排查发现 CPU 使用率并不高(约 60%),但 uptime 显示的 load average 异常。
诊断过程:
# 第 1 步:确认 CPU 频率状态
cpupower frequency-info
# 输出显示:当前频率仅 1.2 GHz,而硬件基础频率为 2.6 GHz
# 频率降幅超过 50%,说明 CPU 处于深度降频状态
# 第 2 步:查看 CPU 调速器
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# 输出:powersave ← 这是问题根源
# powersave 调速器在国产欧拉系统上过于激进,CPU 稍有空闲就降频
# 第 3 步:查看当前频率与最大频率的比值
grep -E 'cpu MHz|model name' /proc/cpuinfo | head -20
根因分析:国产欧拉操作系统的默认 CPU 调速策略为 powersave,该策略在 Vertica 这种「突发计算型」负载下表现很差——查询一来需要全力计算,但 CPU 从低频升到高频有延迟(P-State 切换延迟约 100-500μs),导致查询执行时间被人为拉长。此外,国产 CPU 的 P-State 控制机制不如 Intel SpeedStep 成熟。
修复措施:
# 1. 将所有 CPU 调速器切换为 performance(立即生效,无需重启)
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance > $cpu
done
# 2. 可选:通过 BIOS 彻底固化(需重启,更彻底)
# Power Profile → Maximum Performance
# Processor Monitor/Mwait Support → Disabled
效果:CPU 频率固化在 2.6 GHz 后,同样查询执行时间从 15-20 分钟恢复至 2-3 分钟。load average 从 60+ 降至 15-20(对应正常排队水平)。CPU 使用率从 60% 升至 85%——这看似变差了,实际上是「好事」:CPU 不再偷懒降频,全力工作。
关键教训:CPU 使用率低≠CPU 没有瓶颈。降频状态下 CPU「假装不忙」但实际处理能力大幅下降。排查 CPU 问题时务必先
cpupower frequency-info确认频率状态。
5.9 真实案例二:国产 CPU 架构导致跨节点网络性能骤降¶
📋 真实案例
背景:某政务系统,Vertica Eon 模式集群,混合部署了 Intel Xeon Gold 6330N 服务器和国产 Hygon C86 7390 服务器。扩容时新加入了 28 台 Hygon 节点。
现象:扩容后数据库整体性能反而比扩容前更差。业务高峰期查询平均延迟从 800ms 升至 3200ms。初期怀疑是数据分布或资源池问题。
诊断过程:
# 第 1 步:vnetperf 测试各节点网络延迟
/opt/vertica/bin/vnetperf
# Intel 节点间 RTT:180-200 μs ← 正常
# Hygon 节点间 RTT:400-600 μs ← 明显偏高
# Intel ↔ Hygon 跨架构 RTT:800-1200 μs ← 严重异常
# 第 2 步:UDP 吞吐测试
iperf3 -u -c <target> -b 10G
# Intel 节点:UDP 约 800 MB/s
# Hygon 节点:UDP 仅约 200 MB/s ← 只有 Intel 的 1/4!
根因分析:三层根因叠加:
- 网卡固件兼容性:Hygon 服务器使用 NIC-10GE-2P-530F-B2 网卡,在 Red Hat 7.6 上的固件版本(1.712.30-0)未经官方测试认证
- Bonding 方式差异:Hygon 使用同一张网卡的两个口做 Bond4,Intel 使用两张独立网卡各取一个口做 Bond4——Hygon 的单卡方案使中断全部汇聚到一个 PCIe 通道,CPU 中断风暴严重
- UDP 缓冲区不足:国产 CPU 处理网络中断的效率低于 Intel,默认 UDP 缓冲区无法吸收突发流量,导致大量 UDP 丢包和重传
修复措施:
# 1. 调整网卡 Ring Buffer 到最大(减少丢包)
ethtool -G ens5f0 rx 4096 tx 4096
# 2. 调整网卡队列到最大(利用多核并行处理网络中断)
ethtool -L ens5f0 combined 32
# 3. 增大 UDP 缓冲区(内核参数)
sysctl -w net.core.rmem_max=134217728
sysctl -w net.core.wmem_max=134217728
# 4. 将业务流量从 Hygon 集群切回 Intel 集群
# 待 Hygon 网络问题修复后再逐步切回
效果:业务切回 Intel 集群后性能立即恢复。Hygon 集群网络优化后 UDP 吞吐从 200 MB/s 提升至约 600 MB/s,仍低于 Intel,但已可支撑业务。
关键教训:MPP 数据库中 CPU 架构不仅要看计算性能,更要看网络处理能力。Vertica 的 spread 协议依赖低延迟高吞吐的 UDP 通信,国产 CPU + 未认证网卡驱动的组合是高风险配置。
6. 完整诊断流程实战示例¶
📝 虚构场景 · 完整演练
下面通过一个完整案例,展示从发现到解决的整个流程。
场景¶
- 3 节点 Vertica 集群(每节点 48 核 / 256GB 内存)
- 用户反馈:下午 2 点到 4 点期间,所有查询都非常慢
- 检查发现:CPU 持续 95%,load average > 60
诊断过程¶
Step 1:确认系统状态
SELECT node_name, average_cpu_usage_percent
FROM v_monitor.system_resource_usage
WHERE end_time > sysdate - INTERVAL '1 hour'
ORDER BY end_time DESC;
→ 三个节点的 CPU 都在 92%-98% 之间,确认 CPU 是瓶颈。
Step 2:排除 Recovery 和 TM
SELECT * FROM recovery_status WHERE is_running = 't'; -- 0 rows
SELECT * FROM tuple_mover_operations WHERE is_executing = 't'; -- 0 rows
→ 不是 Recovery,也不是 TM。问题在查询层面。
Step 3:找慢查询
SELECT user_name, request_duration_ms, memory_acquired_mb, LEFT(request, 150)
FROM v_monitor.query_requests
WHERE is_executing = 't'
ORDER BY request_duration_ms DESC
LIMIT 10;
→ 发现 analyst_user 有 3 条查询跑了超过 15 分钟,SQL 都是对 10 亿行销售表做多维度聚合。同时有 25 条短查询在排队。
Step 4:PROFILE 最慢的查询
\timing
\o /dev/null
PROFILE SELECT region, category, SUM(amount), COUNT(*)
FROM sales_fact WHERE date BETWEEN '2026-05-01' AND '2026-05-28'
GROUP BY region, category;
\o
→ 执行时间 18 分钟。看到 GROUP_BY_SPILLED 事件和 total temp data = 45GB。
Step 5:分析算子级 CPU
SELECT operator_name, path_id, SUM(counter_value)
FROM execution_engine_profiles
WHERE transaction_id = :t_id AND statement_id = :s_id
AND counter_name ILIKE 'execution%'
GROUP BY 1, 2 ORDER BY 3 DESC;
→ GroupByHash: 480 秒(占总时间 44%)
→ Scan: 320 秒(占总时间 30%)
→ JoinHash: 180 秒(占总时间 17%)
分析结论:
GroupByHash是最大瓶颈——数据量太大,哈希表装不下,频繁 spill 到磁盘Scan时间也很长——sales_fact 表没有分区,全表扫描了所有数据- 资源池是默认的
general,没有并发限制,25 条查询同时争抢资源
修复措施¶
立即措施(当天执行):
-- 1. 限制 analyst_user 的并发数
CREATE RESOURCE POOL analyst_pool
MAXMEMORYSIZE '40%'
PLANNEDCONCURRENCY 8
MAXCONCURRENCY 4 -- 最多同时跑 4 条分析查询
EXECUTIONPARALLELISM 16 -- 每条最多 16 个线程
RUNTIMECAP '00:30:00';
ALTER USER analyst_user RESOURCE POOL analyst_pool;
短期优化(当周执行):
-- 2. 为 sales_fact 表添加日期分区
ALTER TABLE sales_fact PARTITION BY date;
-- 3. 创建按分组列排序的投影
CREATE PROJECTION sales_analyst_proj AS
SELECT * FROM sales_fact
ORDER BY region, category, date
SEGMENTED BY HASH(region) ALL NODES;
-- 4. 更新统计信息
SELECT ANALYZE_STATISTICS('public.sales_fact');
效果验证:
- 同样的查询从 18 分钟降到 45 秒
- CPU 使用率从 95% 降到 60%
- 查询排队现象消失
7. 快速诊断 SQL 工具箱¶
| 诊断目标 | SQL |
|---|---|
| 系统 CPU 历史 | SELECT * FROM system_resource_usage ORDER BY end_time DESC; |
| 连接均衡检查(CPU 不均时优先) | SELECT node_name, COUNT(*) FROM dc_requests_issued WHERE "time" > sysdate() - 1 GROUP BY 1 ORDER BY 1; |
| 当前慢查询 | SELECT * FROM query_requests WHERE is_executing='t' ORDER BY request_duration_ms DESC; |
| 算子 CPU 热点 | SELECT operator_name, path_id, SUM(counter_value) FROM execution_engine_profiles WHERE counter_name ILIKE 'execution%' GROUP BY 1,2 ORDER BY 3 DESC; |
| 负面事件 | SELECT event_type, event_category, suggested_action FROM query_events WHERE transaction_id=:t_id AND statement_id=:s_id; |
| 查询阶段耗时 | SELECT execution_step, (completion_time-"time") FROM dc_query_executions WHERE transaction_id=:t_id AND statement_id=:s_id; |
| CPU vs Clock 比值 | SELECT path_id, operator_name, SUM(DECODE(counter_name,'execution time (us)',counter_value,NULL))/NULLIF(SUM(DECODE(counter_name,'clock time (us)',counter_value,NULL)),0) FROM execution_engine_profiles WHERE ... GROUP BY 1,2; |
| 节点数据均衡 | SELECT node_name, SUM(row_count) FROM projection_storage WHERE projection_name='...' GROUP BY 1 ORDER BY 2 DESC; |
| 资源池并发 | SELECT pool_name, running_query_count, max_concurrency, query_budget_kb FROM resource_pool_status; |
| 资源拒绝 | SELECT * FROM resource_rejections; |
| TM 操作 | SELECT * FROM tuple_mover_operations WHERE is_executing='t'; |
| Recovery 状态 | SELECT * FROM recovery_status WHERE is_running='t'; |
| SIP 过滤效果 | SELECT sip_expr_id, rows_processed, rows_pruned FROM dc_sips_statistics WHERE transaction_id=:t_id AND statement_id=:s_id; |
8. 最佳实践清单¶
- 为不同工作负载分离资源池:ETL、短查询、长报表各用独立池,互不干扰。这是投入产出比最高的优化。
- 启用客户端连接负载均衡:服务端
SET_LOAD_BALANCE_POLICY('ROUNDROBIN')+ 客户端ConnectionLoadBalance=1。避免所有 SQL 的编译和计划生成集中在单一 initiator node,这是节点间 CPU 不均的首要排查项。详见 Vertica 客户端连接负载均衡配置。 - 设置 EXECUTIONPARALLELISM 小于 CPU 核心数:默认 AUTO 太激进,高并发场景下 4-16 通常更合理。宁可让单条查询稍慢,也要保证整体吞吐。
- 设置合理的 MAXCONCURRENCY:资源池的硬并发限制是防止 CPU 过载的最后防线。一般设为
PLANNEDCONCURRENCY的 50%-80%。 - 定期更新统计信息:大表每次加载数据后执行
SELECT ANALYZE_STATISTICS('')。统计信息是优化器做出正确决策的基础。 - 对齐 JOIN 表的分段键:这是避免
RESEGMENTED_MANY_ROWS最有效的手段。如果两张表经常 JOIN,它们的 SEGMENTED BY 键应该一致。 - CPU 瓶颈时降低编码复杂度:改为
NONE或RLE可以减少解压缩的 CPU 开销,代价是更多磁盘占用。在磁盘充足、CPU 紧张的场景下值得考虑。 - 开启 resource_rejections 监控:资源拒绝事件是 CPU/内存不足的前兆。如果拒绝次数快速增长,说明资源池配置需要调整。
- 使用 RUNTIMECAP + CASCADE 防止失控查询:级联资源池能自动分离长短查询,避免一条大查询拖垮整个系统。
- 为大表添加分区:分区裁剪能减少 90% 以上的无效 SCAN 操作。分区键选择查询中最常用的过滤列(通常就是日期)。
- 为高频聚合查询创建排序投影:投影的 ORDER BY 与查询的 GROUP BY/ORDER BY 一致时,Vertica 可以用流式算子(Pipe)代替哈希算子(Hash),既省内存又省 CPU。
扩展阅读¶
-
Vertica Linux IO 调度器性能影响与调优 — IO 调度器选择不当可能导致 CPU iowait 飙升
-
Vertica 客户端连接负载均衡配置 — 连接倾斜是节点间 CPU 不均的首要排查项
- Vertica 错误日志解读与常见错误处理 — CPU 异常在错误日志中的体现与诊断入口
- Vertica Join 重分段倾斜诊断与修复 — RESEGMENT 倾斜是 CPU 高负载的常见根因
- Vertica 内存压力诊断与调优 — CPU 与内存问题往往相互关联
- Vertica 资源池最佳实践 — 资源池参数详解与配置策略
- Vertica 监控最佳实践 — 建立 CPU、内存、查询的监控告警体系
- Vertica 性能调优 - 2 使用系统表排查查询故障 — 系统表排查方法论