阅读前置知识: 了解 OB 多租户架构、Zone/Unit/Tenant 资源模型、LS(Log Stream) 与 Tablet 概念、Compaction 机制(major/minor/mini)、SQL 引擎执行链路(SQE → 优化器 → 执行引擎 → 存储引擎)
在 OB 应急语境中, CPU拉爆(也叫 CPU 打满、CPU 顶满)指observer 进程的 CPU 使用率持续超过 80%,甚至跑到 100%,通常伴随以下级联故障:
OB-4012: Timeout 或
-4016: Internal ErrorOB 黑话科普:OB 内部我们一般说"CPU 拉爆"而不是"CPU 打爆","拉"字体现的是 CPU 被 SQL 或后台任务逐步拉上去的过程,更贴合实际故障现象。
CPU 拉爆
→ 工作线程(worker thread)排队等不到 CPU 时间片
→ SQL RT 飙升,elapsed_time 中 queue_time 占比增大
→ OBProxy 侧连接池占满,新请求被 reject
→ 业务侧报 Timeout(-4012) / Connection Refused
→ 应用重试风暴 → 进一步加剧 CPU 压力(雪崩效应)
→ 业务全面中断,触发 GOC 应急
V4 相比 V3 架构做了重大重构,CPU 问题的排查思路也有本质区别:
| 维度 | V3.x(多进程) | V4.x(单进程多线程) |
|---|---|---|
| 进程模型 | 每个 Zone 一个 observer 进程,多进程隔离 | 一个物理机只有一个 observer 进程,所有租户共享 |
| 租户隔离 | 进程级天然隔离 | 线程级隔离,依赖内部调度(
_enable_cgroup) |
| CPU 互影响 | Zone 间互不影响 | 一个租户的 CPU 问题可能拖垮整个 observer,影响同机所有租户 |
| 诊断方式 |
top 看各 observer 进程 |
top -H -p 看线程级 CPU |
| 资源管理 | 靠操作系统分配 | Resource Pool + Unit 配置 + 内部线程池调度 |
| 关键风险 | 单 Zone 挂掉 | 单进程 hang → 整个节点不可用 → Paxos 触发选主(election) |
V4 核心概念提醒:V4 中每个 observer 内部由多个 Log Stream (LS) 承载数据,每个 LS 下有多个 Tablet。SQL 执行链路经过 SQE(SQL Query Engine)解析优化后,由 PX/DFO 分发到各 tablet 所在节点执行。理解这条链路对定位 CPU 热点至关重要。
根据 GOC 处理过的大量 P0/P1/P2 应急案例复盘,CPU 拉爆的根因可分为 六大类:
CPU 拉爆根因分类(按 GOC 实战频率排序)
├── A. SQL 层面(最常见,~50%)— 慢SQL / 计划走飞 / 并发打满
├── B. Compaction 等后台任务(~15%)— major freeze / minor compaction / dump
├── C. 内核热点 / 已知缺陷(~15%)— kvcache CAS / latch 竞争 / 内存分配热点
├── D. 资源不足 / 参数配置不当(~10%)— unit 配小了 / 线程池参数不对
├── E. 业务突增 / 流量异常(~5%)— 重试风暴 / 连接风暴 / 缓存穿透
└── F. 系统层面问题(~5%)— NUMA / THP / swap / 内核参数
典型表现:
elapsed_time 极高,在
gv$sql_audit 中一目了然logical_reads(逻辑读)或
get_count(KV 层 get 次数)极大PHY_TABLE_SCAN)常见场景:
| 场景 |
sql_audit 特征 |
执行计划特征 | 应急手段 |
|---|---|---|---|
| 全表扫描 |
table_scan=TRUE,
logical_reads 百万级 |
PHY_TABLE_SCAN 无索引 |
加索引 / 绑 plan baseline |
| Nested Loop Join 爆炸 | 大表 JOIN,
return_rows 巨大 |
NESTED-LOOP JOIN 无 Hash/Merge |
加 Hint
/*+ USE_HASH */ / 加索引 |
| 索引走飞 | 明明有索引但走了全表扫描 |
plan_hash_value 变了 |
检查统计信息 /
ANALYZE 重新收集 |
| COUNT(*) 大表 | 单条 SQL 执行几十秒 |
PHY_TABLE_SCAN +
PHY_COUNT |
业务侧改造 / 缓存计数 |
| 相关子查询未展开 |
execute_count 巨大(逐行执行) |
SUBPLAN SCAN 嵌套 |
改写为 JOIN |
| LIKE '%xxx' 前模糊 | 无法使用 B-tree 索引 |
PHY_TABLE_SCAN |
全文索引 / ES 外置 |
| 大事务 / 批量 DML |
affected_rows 巨大,
elapsed_time 长 |
— | 拆分事务 / 分批执行 |
排查命令(应急三板斧):
-- 【第一斧】查当前在跑的慢 SQL(is_exit=0 表示还在执行)
SELECT tenant_id, svr_ip,
sid, trace_id,
LEFT(query_sql,
200)
AS sql_text,
elapsed_time /
1000000
AS elapsed_sec,
-- 微秒转秒
queue_time /
1000000
AS queue_sec,
-- 排队耗时
logical_reads, physical_reads, get_count,
-- 逻辑读/物理读/KV get
plan_type,
-- 0=本地计划 1=远程 2=分布式
is_inner_sql
FROM oceanbase.gv$sql_audit
WHERE is_exit =
0
AND elapsed_time > 1000000
-- 超过 1 秒
ORDER BY elapsed_time DESC
LIMIT 20;
-- 【第二斧】查执行计划(确认是否走飞)
EXPLAIN
EXTENDED ;
-- 或用 EXPLAIN FORMAT=JSON 看详细代价
EXPLAIN
FORMAT=
JSON ;
-- 【第三斧】查 Top SQL 排行(按 CPU 总消耗排序,抓真正的 CPU 大户)
SELECT sql_id,
LEFT(
MIN(query_sql),
120)
AS sql_text,
COUNT(*)
AS exec_count,
ROUND(
AVG(elapsed_time)/
1000000,
2)
AS avg_rt_sec,
ROUND(
SUM(cpu_time)/
1000000,
2)
AS total_cpu_sec,
ROUND(
AVG(logical_reads),
0)
AS avg_logical_reads,
ROUND(
AVG(get_count),
0)
AS avg_get_count
FROM oceanbase.gv$sql_audit
WHERE request_time > UNIX_TIMESTAMP(NOW() - INTERVAL 10 MINUTE) * 1000000
GROUP BY sql_id
ORDER BY total_cpu_sec DESC
LIMIT 20;
OB 老兵经验:
plan_type字段很关键 —— 0 是本地计划(Local Plan),2 是分布式计划(Dist Plan)。如果是分布式计划拉爆 CPU,说明涉及多机 shuffle,需要关注 PX worker 的并行度和数据倾斜。
典型表现:
plan_hash_value 发生变化,走了不同的物理执行计划sql_audit 中
plan_type 可能从 0(本地计划)变成 2(分布式计划),代价陡增常见原因:
broadcast_version 推进,plan cache 中旧计划失效,重新生成可能选错optimizer_* 系列参数或
_nlj_batching_enabled 等隐藏参数被改过应急手段:
-- 1. 检查统计信息是否过期(关注 last_analyzed 和 stale_stats)
SELECT owner, table_name, last_analyzed, num_rows, stale_stats
FROM oceanbase.dba_tab_statistics
WHERE table_name =
UPPER(
'');
-- 2. 手动收集统计信息(触发优化器重新感知数据分布)
ANALYZE
TABLE .
COMPUTE
STATISTICS;
-- 或者增量收集(大表推荐,更快)
ANALYZE
TABLE .
COMPUTE
STATISTICS
FOR
ALL
COLUMNS
SIZE
AUTO;
-- 3. 绑定执行计划(SPM - SQL Plan Management)
-- 先从 sql_audit 找到之前跑得好的 plan_hash_value
SELECT sql_id, plan_hash_value, elapsed_time, plan_type
FROM oceanbase.gv$sql_audit
WHERE sql_id =
''
ORDER
BY elapsed_time
ASC
LIMIT
5;
-- 用 plan baseline 固定好的计划
SELECT *
FROM oceanbase.dba_sql_plan_baselines
WHERE sql_handle
LIKE
'%%';
-- 4. 紧急场景:直接清 plan cache 让所有计划重新生成
-- 注意:这是双刃剑,清完会短暂 CPU 尖刺(重新编译计划)
ALTER
SYSTEM
SET plan_cache_evict_interval =
'5s';
-- 等恢复后记得改回来
典型表现:
gv$sql_audit 中同一
sql_id 出现几十上百条
is_exit=0queue_time 显著增大(排队时间可能超过实际执行时间)cpu_time 并不算很高(CPU 时间片被争抢)gv$ob_sql_workarea_memory_info 中
queue_size 持续走高排查与应急:
-- 1. 查看当前各 SQL 的并发数(哪些 SQL 在排队)
SELECT sql_id,
COUNT(*)
AS concurrent_cnt,
ROUND(
AVG(queue_time)/
1000000,
2)
AS avg_queue_sec,
ROUND(
AVG(elapsed_time)/
1000000,
2)
AS avg_elapsed_sec
FROM oceanbase.gv$sql_audit
WHERE is_exit =
0
GROUP
BY sql_id
ORDER
BY concurrent_cnt
DESC
LIMIT
10;
-- 2. 查看线程队列堆积情况
-- V4 中每个租户有独立的 request queue
SELECT tenant_id, svr_ip,
in_queue_count, queue_size
FROM oceanbase.gv$tenant_runtime_info;
-- 3. 设置 query timeout 兜底(防止 SQL 长期霸占 CPU)
ALTER
SYSTEM
SET ob_query_timeout =
10000000 TENANT =
'';
-- 10秒
-- 或 session 级别
SET ob_query_timeout =
10000000;
-- 当前会话生效
-- 4. 紧急 Kill 会话(先确认不是关键 DML 事务!)
SHOW
FULL
PROCESSLIST;
KILL ;
-- 5. 通过 OCP 设置租户级并发限制(Resource Manager)
-- V4 支持通过 DBMS_RESOURCE_MANAGER 控制租户内资源组的并发
CALL DBMS_RESOURCE_MANAGER.CREATE_PLAN(
'emergency_plan');
CALL DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
plan => 'emergency_plan',
group_or_subplan => 'default',
max_concurrency => 100
-- 限制最大并发
);
OB 老兵提醒:Kill 会话之前务必确认
trans_id,如果是大事务(affected_rows 很大),Kill 后回滚(rollback)也要消耗大量 CPU,可能短暂加剧问题。可以先让业务侧停发请求,等 CPU 降下来再处理。
OB 背景知识:OB 存储引擎是 LSM-Tree 架构,数据先写 MemTable,达到阈值后 freeze → dump 成 mini SSTable,然后通过 mini compaction / minor compaction / major compaction 逐层合并。每一层 compaction 都会消耗 CPU + IO。
典型表现:
__all_virtual_zone_merge_info 中
major_compact_status = COMPACTING__all_virtual_tablet_compaction_progress 能看到大量 tablet 在合并排查与应对:
-- 1. 检查各 Zone 合并状态
SELECT zone, major_compact_status, broadcast_version,
gmt_create
AS last_start_time, last_finish_time
FROM oceanbase.__all_virtual_zone_merge_info;
-- 2. 检查 tablet 级合并进度(看还有多少没合完)
SELECT tenant_id, zone,
status,
COUNT(*)
AS tablet_cnt
FROM oceanbase.__all_virtual_tablet_compaction_progress
GROUP
BY tenant_id, zone,
status;
-- 3. 检查是否有合并报错(卡住的合并可能拉 CPU)
SELECT *
FROM oceanbase.__all_virtual_zone_merge_info
WHERE major_compact_status =
'ERROR';
-- 4. 【紧急】暂停合并(业务高峰期拉 CPU 时的第一止损手段)
ALTER
SYSTEM
SUSPEND
MERGE;
-- 5. 【低峰期】恢复合并
ALTER
SYSTEM
RESUME
MERGE;
-- 6. 调整合并时间窗口(避免高峰期触发 major freeze)
ALTER
SYSTEM
SET merge_schedule_policy =
'schedule_time=02:00-06:00';
-- 或者通过 OCP 设置“合并时间窗口”
OB 老兵经验:major compaction 的 CPU 消耗主要来自 SSTable 数据的压缩/解压和重写。如果开了 zstd 压缩,合并时 CPU 开销会更大。可以临时把
compaction_memory_bytes_limit调小来降低合并并发。
典型表现:
__all_virtual_tablet_compaction_progress 中大量 mini/minor compaction 任务堆积应对:
-- 调整写入限速阈值(避免写入过快导致 MemTable 爆仓)
ALTER
SYSTEM
SET writing_limit_trigger_percentage =
80;
-- 调整 minor compaction 触发阈值
ALTER
SYSTEM
SET minor_compact_trigger =
4;
-- mini sstable 数达到 4 时触发 minor
-- 检查 MemTable 内存使用(是否快爆了)
SELECT tenant_id, svr_ip, freeze_trigger,
memstore_used, memstore_limit
FROM oceanbase.gv$tenant_runtime_info;
| 后台任务 | OB 术语 | CPU 影响 | 控制手段 |
|---|---|---|---|
| 日志归档 | log_archive_dest | clog/ilog 归档消耗 CPU + 网络 IO |
ALTER SYSTEM SET log_archive_concurrency = 2 |
| 数据备份 | backup_data | 全量/增量备份消耗 CPU + 磁盘 IO | 调整备份窗口,避开业务高峰 |
| 日志回放 | replay / apply | 备集群回放 clog 消耗 CPU | 检查回放延迟(
gv$ob_log_stat) |
| 统计信息收集 | auto_analyze | 后台全表扫描消耗 CPU | 调整自动收集窗口 |
| PDML | Parallel DML | 并行 DML 占用 PX worker 线程 | 调低
parallel_servers_target |
| location cache 刷新 | location_cache | 内部元数据操作 | 一般不需干预,异常时检查 RS 状态 |
| 选举 (election) | paxos election | leader 切换期间短暂 CPU 尖峰 | 一般不需干预,频繁选举需检查网络 |
问题描述:
ref_cnt) 和序列号 (
seq_num) 共用一个
int64dec_ref_cnt_and_inc_seq_num 函数占比显著(超过 5%)触发条件:
影响版本:V4.2.x BP 系列及更早版本, V4.3.5 已修复(重构了引用计数实现)
排查方法:
# 1. 抓取火焰图(perf,这是定位内核热点的核武器)
perf record -g -p $(pgrep observer) -- sleep 30
perf report --stdio > perf_report.txt
# 2. 检查热点函数(看占比)
perf report --sort=symbol | head -30
# 3. 关注以下调用栈(看到这个栈基本就是 KVCache 热点)
# ObSSTableMultiPrefetcher::~ObSSTableMultiPrefetcher
# → ObKVCacheHandle::~ObKVCacheHandle
# → ObAtomicReference::dec_ref_cnt_and_inc_seq_num ← 热点函数
# 4. 也可以用 obdiag 一键采集
obdiag gather all --since
"30m"
# 采集结果发给研发分析
应急手段(按优先级):
| 优先级 | 止损措施 | 原理 |
|---|---|---|
| P0 紧急 | 创建覆盖索引
CREATE INDEX idx ON t(col) STORING (所有查询列) |
避免回表,彻底绕过 KVCache 热点路径 |
| P1 短期 | 将大表改为分区表 | 打散流量到多个节点,降低单机并发 |
| P2 短期 | 切回原环境(如 ARM 切回 x86) | x86 单核性能更强,触发概率更低 |
| P3 长期 | 升级 OBServer 至 V4.3.5+ | 内核层面修复了引用计数实现 |
常见场景:
ob_malloc /
ob_free 或 jemalloc 相关)排查:
# perf 抓取锁热点
perf record -e cycles -g -p $(pgrep observer) -- sleep 10
perf report --sort=symbol,dso
# 关注以下函数
# - ob_latch_rdlock / ob_latch_wrlock ← OB 内部 latch
# - ob_malloc / ob_free / je_malloc ← 内存分配热点
# - __pthread_mutex_lock ← 系统级锁竞争
-- 查看 latch 统计(哪个 latch 竞争最严重)
SELECT latch_id,
name, gets, misses,
ROUND(misses/
NULLIF(gets,
0)*
100,
4)
AS miss_pct
FROM oceanbase.gv$latch
WHERE misses > 0
ORDER BY misses DESC
LIMIT 20;
问题:plan cache 过大导致淘汰时 CPU 升高;或 plan cache 命中率低,大量 SQL 需要重新编译计划
-- 检查 plan cache 状态(关注命中率和内存使用)
SELECT tenant_id, svr_ip,
plan_cache_size, mem_used, plan_num,
ROUND(hit_count/
NULLIF(access_count,
0)*
100,
2)
AS hit_pct
FROM oceanbase.gv$plan_cache_status;
-- plan cache 命中率低于 90% 需要关注
-- 必要时清理 plan cache(谨慎操作,会短暂 CPU 尖峰)
ALTER
SYSTEM
SET plan_cache_evict_interval =
'30s';
-- 恢复后改回默认
V4 中租户资源通过
Resource Pool → Resource Unit 模型管理。Unit 的
min_cpu / max_cpu 决定了租户能拿到的 CPU 上下限。
-- 1. 检查租户资源单元配置
SELECT t.tenant_name, t.tenant_id,
u.unit_config_id, u.unit_name,
u.min_cpu, u.max_cpu,
u.memory_size/
1024/
1024/
1024
AS memory_gb
FROM oceanbase.dba_ob_tenants t
JOIN oceanbase.dba_ob_resource_pools p
ON t.tenant_id = p.tenant_id
JOIN oceanbase.dba_ob_unit_configs u
ON p.unit_config_id = u.unit_config_id;
-- 2. 检查各节点 CPU 实际分配情况
SELECT svr_ip, tenant_id,
SUM(cpu_assigned)
AS cpu_assigned,
SUM(cpu_used_max)
AS cpu_used_max
FROM oceanbase.gv$ob_processlist
GROUP
BY svr_ip, tenant_id;
-- 3. 【紧急扩容】调大 Unit 的 CPU(OCP 操作更方便)
ALTER
RESOURCE UNIT MIN_CPU =
16, MAX_CPU =
32;
-- 4. 如果集群有富余节点,也可以通过加 Unit 横向扩容
-- 通过 OCP “增加 Unit” 操作
OB 老兵提醒:V4 中
min_cpu是硬保障(预留),max_cpu是上限。如果min_cpu设小了,即使机器有空闲 CPU,租户也拿不到。OLTP 业务建议min_cpu = max_cpu,不超卖。
表现:CPU 高 + iowait 高 +
vmstat 的
si/so 不为 0(在用 swap)
-- 检查各租户内存使用
SELECT svr_ip, tenant_id,
ROUND(mem_used/
1024/
1024/
1024,
2)
AS mem_used_gb,
ROUND(mem_limit/
1024/
1024/
1024,
2)
AS mem_limit_gb,
ROUND(mem_used/mem_limit*
100,
2)
AS mem_pct
FROM oceanbase.gv$tenant_runtime_info;
-- 检查模块级内存使用(看哪个模块吃内存最多)
SELECT tenant_id, svr_ip,
module,
ROUND(hold/
1024/
1024,
0)
AS hold_mb,
ROUND(used/
1024/
1024,
0)
AS used_mb
FROM oceanbase.gv$ob_memory
WHERE hold > 100*1024*1024
-- 超过 100MB 的模块
ORDER BY hold DESC;
# 系统层面检查 swap
free -h
vmstat 1 5
# si/so 不为 0 → 说明在用 swap → observer 性能会严重下降
| 参数 | 默认值 | OLTP 建议 | 影响 | OB 内部说明 |
|---|---|---|---|---|
workers_per_cpu_quota |
10 | 8-10 | 每个 CPU quota 对应的工作线程数 | 调太大会导致上下文切换加剧 |
cpu_reserved_cores |
0 | 2-4 | 预留给系统线程(RS、timer 等) | 调太小会导致系统线程抢不到 CPU |
sys_cpu_limit_trigger |
80 | 保持默认 | 系统线程 CPU 限制触发阈值 | 触发后系统线程会被限速 |
large_query_cpu_time_threshold |
100ms | 按业务调整 | 大查询识别阈值 | 超过此值的查询会被调度到大查询队列 |
enable_sql_operator_dump |
True | 保持开启 | SQL 算子落盘 | 防止 hash join/sort 内存爆掉 |
px_workers_per_cpu_quota |
10 | OLTP 建议 1-2 | PX worker 线程比例 | OLTP 场景调低,避免 PX 抢占 worker 线程 |
_enable_cgroup |
True | 保持开启 | 租户间 CPU 隔离 | 关闭会导致租户间 CPU 互相抢 |
_hash_group_by_no_sort |
True | 保持默认 | hash group by 优化 | 影响聚合类 SQL 的执行效率 |
典型场景:
应急手段:
-- 1. 确认连接数 / QPS 变化
SELECT svr_ip, tenant_id,
COUNT(*)
AS session_count
FROM oceanbase.gv$ob_processlist
GROUP
BY svr_ip, tenant_id;
-- 2. 设置连接数上限(防止雪崩)
ALTER
SYSTEM
SET max_connections =
500;
-- 3. 租户级限流(V4 Resource Manager 支持)
CALL DBMS_RESOURCE_MANAGER.CREATE_PLAN_DIRECTIVE(
plan => 'peak_protection',
group_or_subplan => 'app_group',
max_concurrency => 200,
-- 限制最大并发
max_iops => 10000,
mgmt_p1 => 50
);
-- 4. 业务侧配合:让应用侧临时停发请求 / 加指数退避
表现:短时间内大量新连接涌入,OBProxy 的连接打满,observer CPU 瞬间拉爆
OB 特有现象:连接建立时需要做认证、分配 session 内存,大量并发连接会触发 observer 内部内存分配热点。
-- 快速查看当前连接状态
SHOW
FULL
PROCESSLIST;
-- 查看各租户连接分布
SELECT tenant,
COUNT(*)
AS cnt, state
FROM information_schema.processlist
GROUP
BY tenant, state
ORDER
BY cnt
DESC;
-- 批量 Kill 空闲或异常连接(先确认不是关键业务!)
SELECT
CONCAT(
'KILL ',
id,
';')
FROM information_schema.processlist
WHERE command =
'Sleep'
AND
time > 60;
OB 规范要求:observer 必须用
numactl --interleave=all启动,否则跨 NUMA 节点内存访问会导致 CPU 开销显著增加。
# 检查 NUMA 状态
numactl --hardware
numactl --show
# 检查 observer 进程的 NUMA 策略
cat /proc/$(pgrep observer)/numa_maps | head -5
# 正确启动方式(必须在 observer 启动脚本中配置)
numactl --interleave=all /home/admin/oceanbase/bin/observer
OB 规范要求:THP 必须关闭。THP 会导致内存分配时出现大页延迟,影响 observer 的 RT。
# 检查 THP 状态(应显示 [never])
cat /sys/kernel/mm/transparent_hugepage/enabled
# 关闭 THP
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# OB 标准内核参数检查(与 obd 部署工具检查项对齐)
sysctl vm.swappiness
# 必须 = 0,禁止 swap
sysctl vm.overcommit_memory
# 建议 = 0
sysctl net.core.somaxconn
# 建议 >= 65535
sysctl net.ipv4.tcp_tw_reuse
# 建议 = 1
sysctl fs.file-max
# 建议 >= 655350
# 检查 observer 文件描述符限制
ulimit -n
# 建议 >= 655350
[0-1min] 确认问题 → 登录 OCP,看 CPU 监控趋势;确认是单节点还是全集群
↓
[1-2min] 定位线程 → SSH 到节点,top -H -p 看哪些线程 CPU 高
↓
[2-3min] 关联 SQL → 查 gv
$sql_audit(is_exit=0),关联 trace_id 找到高 CPU SQL
↓
[3-4min] 判断类别 → 区分 SQL 问题 / Compaction 拉 CPU / 内核热点 / 资源不足
↓
[4-5min] 执行止损 → Kill SQL / SUSPEND MERGE / 扩 Unit / 限流
# 1.1 查看整体 CPU 使用(关注 us/sy/wa/id)
top -bn1 | head -20
# 1.2 查看 observer 线程级 CPU(V4 单进程,必须看线程)
top -H -p $(pgrep observer) -bn1 | head -40
# 1.3 查看 CPU 各核使用情况(是否有单核打满)
mpstat -P ALL 1 3
# 1.4 查看是否有 iowait(可能是 IO 问题伪装的 CPU 问题)
iostat -x 1 3
# 关注 await、util 指标,如果 await > 10ms 或 util > 90% → IO 瓶颈
# 1.5 查看 swap(observer 绝对不能用 swap)
free -h
# 1.6 查看系统日志是否有 OOM / observer crash
dmesg | grep -i
"oom\|killed\|out of memory" | tail -20
# 1.7 检查 observer 日志是否有异常(WARN/ERROR)
tail -1000 /home/admin/oceanbase/
log/observer.log | grep -E
'WARN|ERROR' | tail -20
-- 2.1 查看当前活跃 SQL(is_exit=0 表示还在跑)
SELECT tenant_id, svr_ip,
sid, trace_id,
LEFT(query_sql,
150)
AS sql_text,
elapsed_time /
1000000
AS elapsed_sec,
queue_time /
1000000
AS queue_sec,
logical_reads, physical_reads, get_count,
plan_type
-- 0=Local 1=Remote 2=Distributed
FROM oceanbase.gv$sql_audit
WHERE is_exit =
0
ORDER
BY elapsed_time
DESC
LIMIT
20;
-- 2.2 查看租户级别的 CPU 消耗(哪个租户在拉 CPU)
SELECT tenant_id, svr_ip,
ROUND(
SUM(cpu_time)/
1000000,
2)
AS total_cpu_sec,
COUNT(*)
AS active_sessions
FROM oceanbase.gv$sql_audit
WHERE request_time > UNIX_TIMESTAMP(NOW() - INTERVAL 5 MINUTE) * 1000000
GROUP BY tenant_id, svr_ip
ORDER BY total_cpu_sec DESC;
-- 2.3 查看等待事件(V4 性能诊断利器)
SELECT
event, wait_class,
COUNT(*)
AS wait_cnt,
ROUND(
AVG(wait_time_micro),
0)
AS avg_wait_us,
ROUND(
SUM(timeouts),
0)
AS total_timeouts
FROM oceanbase.gv$session_wait
GROUP
BY
event, wait_class
ORDER
BY wait_cnt
DESC
LIMIT
20;
-- 重点关注:
-- "row lock wait" → 行锁竞争
-- "SQL QUEUEING" → 工作线程不足,SQL 在排队
-- "PX QUEUEING" → PX worker 不够
-- "db file data read" → 物理 IO 多
-- 2.4 查看 Compaction 状态(是否在跑 major/minor)
SELECT zone, major_compact_status, broadcast_version
FROM oceanbase.__all_virtual_zone_merge_info;
SELECT tenant_id, zone,
status,
COUNT(*)
AS tablet_cnt
FROM oceanbase.__all_virtual_tablet_compaction_progress
GROUP
BY tenant_id, zone,
status;
-- 2.5 查看线程队列堆积
SELECT svr_ip, tenant_id,
queue_size, in_queue_count
FROM oceanbase.gv$ob_sql_workarea_memory_info;
-- 2.6 查看 MemTable 内存状态(是否快爆了导致频繁 freeze)
SELECT tenant_id, svr_ip,
ROUND(memstore_used/
1024/
1024/
1024,
2)
AS memstore_used_gb,
ROUND(memstore_limit/
1024/
1024/
1024,
2)
AS memstore_limit_gb,
freeze_trigger, active_freeze_trigger
FROM oceanbase.gv$tenant_runtime_info;
# 3.1 获取高 CPU 线程的 LWP(轻量级进程号)
top -H -p $(pgrep observer) -bn1 | awk
'$9 > 10 {print $1, $9}' | head -20
# 3.2 将 LWP 转为十六进制(用于关联 observer 日志中的线程号)
printf
'%x\n'
# 3.3 通过 pstack 抓取线程栈(生产慎用,可能导致 observer 短暂 hang)
pstack $(pgrep observer) > pstack_$(date +%Y%m%d%H%M).txt
# 3.4 通过 perf 抓取火焰图(推荐!内核热点定位的终极武器)
perf record -g -F 99 -p $(pgrep observer) -- sleep 30
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
# 3.5 通过 obdiag 一键采集(最推荐,自动化程度高)
obdiag gather all --since
"30m"
# 会自动采集 perf、日志、sql_audit、系统信息等,打包发给研发
-- 3.6 V4 SQL Trace 诊断(推荐)
-- 开启 SQL Trace 捕获详细执行信息
ALTER
SYSTEM
SET trace_log_slow_query_watermark =
'1s';
-- 慢查询日志在 /home/admin/oceanbase/log/observer.log.slow_query
-- 3.7 关联 trace_id 查看完整执行链路
SELECT *
FROM oceanbase.gv$sql_audit
WHERE trace_id =
'';
| 级别 | 场景 | 止损措施 | 预期恢复时间 |
|---|---|---|---|
| P0-紧急 | 业务完全不可用,报 -4012/-4016 | Kill 问题 SQL + 扩 Unit + 切流 | 1-5 分钟 |
| P1-严重 | 业务严重降级,RT 恶化 10 倍+ | SUSPEND MERGE + 限流 + 加索引 | 5-15 分钟 |
| P2-一般 | 部分功能受影响 | 分析执行计划 + 加索引 + 绑 SPM | 15-60 分钟 |
| P3-观察 | CPU 偏高但未影响业务 | 收集 perf + sql_audit 数据 + 制定优化方案 | 计划性处理 |
# ===== 第一步:快速 Kill 慢 SQL =====
# 通过 obclient 经 OBProxy 连接(也可以直接连 observer 的 2881 端口)
obclient -h -P -u@
# -p -D
# 找到问题 SQL 的 session
SELECT id, user, host, db,
command, time, state, LEFT(info, 100) AS sql_text
FROM information_schema.processlist
WHERE time > 5 AND
command !=
'Sleep'
ORDER BY time DESC;
# Kill 对应 session(注意:先确认不是关键 DML 事务!)
KILL ;
# ===== 第二步:紧急扩 Unit CPU =====
# 通过 OCP 操作更方便:租户管理 → 资源单元 → 修改
# 或通过 SQL
ALTER RESOURCE UNIT unit_xxx MIN_CPU = 32, MAX_CPU = 64;
# ===== 第三步:暂停 Compaction =====
ALTER SYSTEM SUSPEND MERGE; -- 暂停 major compaction
# ===== 第四步:设置 SQL 超时兆底 =====
ALTER SYSTEM SET ob_query_timeout = 30000000 TENANT =
''; -- 30秒
ALTER SYSTEM SET ob_trx_timeout = 30000000 TENANT =
''; -- 事务超时 30秒
# ===== 第五步:如仍无法恢复,考虑重启 observer =====
# 注意:这是最后手段!
# 重启会触发 Paxos 选主,其他节点接管 leader,业务会短暂中断
# 必须通过 OCP 操作,不能手动 kill -9
# 重启前确认其他节点健康,避免多个节点同时不可用
-- 批量生成 Kill 语句(超过 60 秒的非 Sleep 会话)
SELECT
CONCAT(
'KILL ',
id,
';')
FROM information_schema.processlist
WHERE
time > 60 AND command != 'Sleep';
-- 最近 10 分钟 Top SQL(按 CPU 总消耗排序)
SELECT sql_id,
LEFT(
MIN(query_sql),
120)
AS sql_text,
COUNT(*)
AS exec_cnt,
ROUND(
AVG(elapsed_time)/
1000000,
2)
AS avg_rt_sec,
ROUND(
AVG(queue_time)/
1000000,
2)
AS avg_queue_sec,
ROUND(
AVG(logical_reads),
0)
AS avg_lreads,
ROUND(
AVG(get_count),
0)
AS avg_gets,
MAX(plan_type)
AS plan_type
FROM oceanbase.gv$sql_audit
WHERE request_time > UNIX_TIMESTAMP(NOW() - INTERVAL 10 MINUTE) * 1000000
GROUP BY sql_id
ORDER BY SUM(cpu_time) DESC
LIMIT 20;
-- 查看当前 Compaction 状态(是否需要 SUSPEND MERGE)
SELECT zone, major_compact_status, broadcast_version,
gmt_create
AS start_time, last_finish_time
FROM oceanbase.__all_virtual_zone_merge_info;
-- 快速查看租户资源使用全景
SELECT t.tenant_name, t.tenant_id,
u.min_cpu, u.max_cpu,
r.mem_used_percent,
r.cpu_used_percent
FROM oceanbase.dba_ob_tenants t
JOIN oceanbase.dba_ob_resource_pools p
ON t.tenant_id = p.tenant_id
JOIN oceanbase.dba_ob_unit_configs u
ON p.unit_config_id = u.unit_config_id;
-- 检查 Paxos 状态(是否有节点不可用 / leader 分布不均)
SELECT tenant_id, ls_id, svr_ip,
role, member_list
FROM oceanbase.gv$ob_log_stat
WHERE
role =
'LEADER'
ORDER
BY tenant_id, ls_id;
| 指标 | OCP 告警项 | 阈值建议 | 告警级别 |
|---|---|---|---|
| observer CPU 使用率 |
ob_cpu_percent |
> 70% 持续 5min / > 85% 持续 3min | Warning / Critical |
| SQL 平均 RT |
ob_sql_rt |
> 基线 2 倍 且 > 100ms | Warning |
| 活跃会话数 |
ob_active_session |
> 连接池 80% | Warning |
| 队列等待 |
SQL QUEUEING 等待事件 |
出现 > 1秒 | Critical |
| Compaction 状态 |
merge_timeout |
超过预期窗口未完成 | Info |
| MemTable 内存 |
memstore_used_percent |
> 80% | Warning |
| Paxos 日志回放延迟 |
replay_lag |
> 10s | Warning |
| OBServer 存活 |
ob_server_exists |
节点不可用 | Critical |
stale_stats(是否有表统计信息过期)major_compact_status)row lock wait /
SQL QUEUEING)freeze_trigger)log_archive_dest 是否正常)
OB Unit CPU 配置公式:
目标 Unit min_cpu = 预估 QPS × 平均 CPU 耗时(ms) / 1000 / 目标CPU利用率(0.6)
示例:
QPS = 10000, 平均 CPU 耗时 = 2ms
所需 CPU = 10000 × 2 / 1000 / 0.6 ≈ 33 核
安全余量建议:实际分配 = 计算值 × 1.5(预留高峰 + Compaction 等后台任务)
注意事项:
- OLTP 业务建议 min_cpu = max_cpu,不超卖
- HTAP 场景建议 OLTP 和 OLAP 用不同租户隔离
- 考虑 major compaction 的 CPU 开销(压缩算法选型影响很大)
| 工具 | OB 术语 | 用途 | 使用场景 |
|---|---|---|---|
| OCP | OCP 运维平台 | 全功能运维平台 | 日常运维、监控、告警、合并管理、租户管理 |
| obdiag | OB 诊断工具 | 一键诊断、日志分析、巡检 | 现场诊断、研发协同、采集诊断包 |
| obclient | OB 命令行客户端 | 连接 observer/OBProxy 执行 SQL | 应急 SQL 执行、Kill 会话 |
| perf + FlameGraph | 火焰图 | 内核热点定位 | CPU 拉爆且 SQL 层无法解释时使用 |
| top / mpstat | 系统工具 | 线程级 CPU 监控 | 第一步快速诊断 |
| gv$sql_audit | SQL 审计视图 | SQL 级问题定位 | 最常用,每个 SQL 的执行详情 |
| gv$session_wait | 等待事件 | V4 性能瓶颈分析 | 区分 CPU / IO / Lock / Queue |
| gv$latch | Latch 统计 | 内部轻量锁竞争 | 定位内部锁热点 |
| pstack | 线程栈快照 | 锁竞争分析 | 生产慎用,可能导致 observer 短暂 hang |
# 一键性能诊断(分析 observer 日志中的 WARN/ERROR)
obdiag analyze
log --since
"30m"
# 分析指定 SQL 的执行详情
obdiag analyze sql --sql_id
""
# 巡检报告(生成全面的健康检查报告)
obdiag check
# 采集诊断信息(打包发给研发分析,应急时必做)
obdiag gather all --since
"1h"
# 会自动采集:perf、observer日志、sql_audit、系统信息、火焰图等
gv$sql_audit +
top -H 是应急双绝,精准定位高 CPU 线程和问题 SQL。| 踩坑 | 说明 | 正确做法 |
|---|---|---|
不看
plan_type 就下结论 |
本地计划和分布式计划的代价完全不同 | 先确认
plan_type 是 Local 还是 Distributed |
| Kill 会话后不管回滚 | 大事务 Kill 后 rollback 也要耗 CPU | 先看
affected_rows 和
trans_id,评估回滚代价 |
| SUSPEND MERGE 后忘记 RESUME | 长期不合会导致 SSTable 层数爆炸,读放大严重 | 低峰期记得 RESUME,并跟踪合并完成 |
| 不采集 perf 就让研发介入 | 内核热点没有 perf 数据无法定位 | 应急时同步
obdiag gather all,保存现场数据 |
| 只看 SQL 不看等待事件 | CPU 高不一定都是 SQL 问题 |
gv$session_wait 看等待事件分布,可能是 lock/queue |
| V4 下忽略租户互影响 | 一个租户拉爆 CPU 可能影响同机所有租户 | 确认是哪个租户在拉 CPU,必要时通过 cgroup 隔离 |
本文档基于 OB GOC 团队实战经验整理,适用于 OceanBase V4 系列版本。如有新的典型案例或解决方案,欢迎及时补充至知识库。
如果在操作过程中遇到问题,欢迎在评论区留言交流,后续我们会持续分享更多实战运维干货,记得 关注不迷路 ,下次见~
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70047467/viewspace-3179723/ ,如需转载,请注明出处,否则将追究法律责任。