OceanBase 4.2 SQL性能优化指南 - CPU高耗时SQL诊断与优化
·
目录
OceanBase 4.2 SQL性能优化指南 - CPU高耗时SQL诊断与优化
一、CPU高耗时SQL识别
1.1 通过GV$OB_SQL_AUDIT视图查找高CPU消耗SQL
-- 查找CPU消耗TOP 20的SQL
SELECT /*+ QUERY_TIMEOUT(30000000) */
SQL_ID,
PLAN_ID,
QUERY_SQL,
ROUND(AVG(ELAPSED_TIME)/1000, 2) AS avg_elapsed_ms,
ROUND(AVG(CPU_TIME)/1000, 2) AS avg_cpu_ms,
ROUND(AVG(CPU_TIME)/AVG(ELAPSED_TIME)*100, 2) AS cpu_ratio,
SUM(EXECUTIONS) AS total_exec,
ROUND(SUM(CPU_TIME)/1000000, 2) AS total_cpu_sec,
ROUND(AVG(ROW_PROCESSED)) AS avg_rows,
ROUND(AVG(LOGICAL_READS)) AS avg_logical_reads,
ROUND(AVG(PHYSICAL_READS)) AS avg_physical_reads,
MAX(REQUEST_TIME) AS last_exec_time
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
REQUEST_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
AND IS_INNER_SQL = 0
AND EXECUTIONS > 0
GROUP BY
SQL_ID, PLAN_ID, QUERY_SQL
HAVING
AVG(CPU_TIME) > 10000 -- CPU时间超过10ms
ORDER BY
total_cpu_sec DESC
LIMIT 20;
1.2 实时监控CPU密集型会话
-- 查看当前正在执行的高CPU消耗SQL
SELECT
sid,
sql_id,
plan_id,
trace_id,
query_sql,
ROUND((SYSDATE - query_start) * 24 * 60 * 60) AS exec_seconds,
ROUND(cpu_time/1000, 2) AS cpu_ms,
state,
event,
tenant_id
FROM
oceanbase.GV$OB_PROCESSLIST
WHERE
state = 'ACTIVE'
AND cpu_time > 100000 -- CPU时间超过100ms
ORDER BY
cpu_time DESC;
1.3 按Plan Hash聚合分析
-- 按执行计划聚合统计
SELECT
PLAN_ID,
COUNT(DISTINCT SQL_ID) AS sql_count,
SUM(EXECUTIONS) AS total_exec,
ROUND(AVG(CPU_TIME)/1000, 2) AS avg_cpu_ms,
ROUND(SUM(CPU_TIME)/1000000, 2) AS total_cpu_sec,
ROUND(AVG(ELAPSED_TIME)/1000, 2) AS avg_elapsed_ms,
ROUND(AVG(LOGICAL_READS)) AS avg_logical_reads,
MIN(QUERY_SQL) AS sample_sql
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
REQUEST_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
AND IS_INNER_SQL = 0
GROUP BY
PLAN_ID
HAVING
SUM(CPU_TIME) > 1000000000 -- 总CPU时间超过1000秒
ORDER BY
total_cpu_sec DESC
LIMIT 20;
二、基于Plan Hash的执行计划详情分析
2.1 获取执行计划详情
-- 方法1:通过PLAN_ID获取执行计划
SELECT * FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
WHERE PLAN_ID = 'your_plan_id';
-- 方法2:通过SQL_ID和PLAN_ID获取详细执行计划
SELECT
OPERATOR,
NAME,
ROWS,
COST,
PROPERTY
FROM
oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN
WHERE
TENANT_ID = 1001 -- 替换为实际租户ID
AND PLAN_ID = 'your_plan_id'
ORDER BY
OPERATOR_ID;
-- 方法3:使用EXPLAIN EXTENDED查看执行计划
EXPLAIN EXTENDED
SELECT /*+ USE_PLAN_CACHE(NONE) */
*
FROM your_table
WHERE your_conditions;
2.2 执行计划关键指标解读
-- 分析执行计划的关键性能指标
SELECT
p.PLAN_ID,
p.SQL_ID,
p.TYPE,
p.IS_BIND_SENSITIVE,
p.IS_BIND_AWARE,
s.AVG_EXE_TIME,
s.SLOWEST_EXE_TIME,
s.SLOW_COUNT,
s.HIT_COUNT,
s.PLAN_SIZE,
s.EXECUTIONS,
s.DISK_READS,
s.BUFFER_GETS,
s.CPU_TIME,
s.ELAPSED_TIME,
s.TABLE_SCAN,
s.INSERT_COUNT,
s.UPDATE_COUNT,
s.DELETE_COUNT,
s.SELECT_COUNT
FROM
oceanbase.GV$OB_PLAN_CACHE_PLAN p
JOIN oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT s
ON p.PLAN_ID = s.PLAN_ID
WHERE
p.PLAN_ID = 'your_plan_id';
2.3 执行计划算子分析
-- 详细分析每个算子的执行情况
WITH plan_detail AS (
SELECT
PLAN_ID,
OPERATOR_ID,
OPERATOR,
NAME,
ROWS,
COST,
CARDINALITY,
PROPERTY,
EXECUTION_TIME,
CPU_TIME,
IO_TIME
FROM
oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN
WHERE
PLAN_ID = 'your_plan_id'
)
SELECT
OPERATOR_ID,
OPERATOR,
NAME,
ROWS AS estimated_rows,
COST AS estimated_cost,
EXECUTION_TIME AS actual_time,
CPU_TIME,
IO_TIME,
CASE
WHEN ROWS > 0 THEN ROUND(CARDINALITY/ROWS, 2)
ELSE NULL
END AS cardinality_accuracy,
PROPERTY
FROM
plan_detail
ORDER BY
OPERATOR_ID;
三、CPU密集型SQL常见问题模式
3.1 问题模式识别
模式1:全表扫描(TABLE SCAN)
-- 识别全表扫描
SELECT
SQL_ID,
PLAN_ID,
QUERY_SQL,
TABLE_SCAN,
LOGICAL_READS,
CPU_TIME/1000 AS cpu_ms
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
TABLE_SCAN = 1
AND LOGICAL_READS > 10000
AND REQUEST_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
ORDER BY
CPU_TIME DESC
LIMIT 10;
模式2:嵌套循环连接(NESTED LOOP JOIN)大数据集
-- 识别低效的嵌套循环
EXPLAIN FORMAT=JSON
SELECT /*+ USE_NL(t1 t2) */ *
FROM large_table t1
JOIN another_large_table t2 ON t1.id = t2.ref_id;
-- 查看实际执行统计
SELECT
OPERATOR,
ROWS,
COST,
OUTPUT_ROWS,
EXECUTION_TIME
FROM
oceanbase.GV$OB_PLAN_CACHE_PLAN_EXPLAIN
WHERE
PLAN_ID = 'your_plan_id'
AND OPERATOR LIKE '%NESTED%LOOP%';
模式3:排序操作(SORT)消耗大量CPU
-- 识别大量排序操作
SELECT
SQL_ID,
PLAN_ID,
QUERY_SQL,
SORT_MERGE_PASSES,
SORTS,
CPU_TIME/1000 AS cpu_ms
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
SORTS > 0
AND CPU_TIME > 100000
AND REQUEST_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
ORDER BY
CPU_TIME DESC;
3.2 典型问题案例分析
案例1:缺失索引导致的全表扫描
-- 原始SQL(问题)
SELECT * FROM orders
WHERE order_date = '2024-01-01'
AND status = 'COMPLETED';
-- 执行计划分析
EXPLAIN EXTENDED SELECT * FROM orders
WHERE order_date = '2024-01-01'
AND status = 'COMPLETED';
-- 输出示例:
-- | TABLE SCAN | orders | 1000000 rows | cost=50000 |
-- 优化方案:创建复合索引
CREATE INDEX idx_orders_date_status
ON orders(order_date, status);
-- 优化后执行计划
-- | INDEX RANGE SCAN | idx_orders_date_status | 100 rows | cost=50 |
案例2:不当的JOIN顺序
-- 问题SQL:小表驱动大表
SELECT /*+ LEADING(large_table) USE_NL(small_table) */
l.*, s.*
FROM
large_table l
JOIN small_table s ON l.id = s.ref_id
WHERE
s.type = 'A';
-- 优化:调整JOIN顺序,小表驱动大表
SELECT /*+ LEADING(small_table) USE_NL(large_table) */
l.*, s.*
FROM
small_table s
JOIN large_table l ON s.ref_id = l.id
WHERE
s.type = 'A';
-- 或使用HASH JOIN
SELECT /*+ USE_HASH(l s) */
l.*, s.*
FROM
large_table l
JOIN small_table s ON l.id = s.ref_id
WHERE
s.type = 'A';
四、优化策略与方法
4.1 索引优化
-- 1. 分析表的索引使用情况
SELECT
table_name,
index_name,
column_name,
column_position,
index_type
FROM
oceanbase.DBA_IND_COLUMNS
WHERE
table_name = 'YOUR_TABLE'
ORDER BY
index_name, column_position;
-- 2. 查看索引使用统计
SELECT
INDEX_NAME,
TABLE_NAME,
BLEVEL,
LEAF_BLOCKS,
DISTINCT_KEYS,
AVG_LEAF_BLOCKS_PER_KEY,
AVG_DATA_BLOCKS_PER_KEY,
CLUSTERING_FACTOR
FROM
oceanbase.DBA_INDEXES
WHERE
TABLE_NAME = 'YOUR_TABLE';
-- 3. 创建覆盖索引减少回表
CREATE INDEX idx_covering
ON orders(customer_id, order_date, status, amount)
GLOBAL PARTITION BY HASH(customer_id) PARTITIONS 16;
4.2 SQL改写优化
4.2.1 避免函数操作导致索引失效
-- 问题SQL
SELECT * FROM orders
WHERE DATE_FORMAT(order_date, '%Y-%m') = '2024-01';
-- 优化后
SELECT * FROM orders
WHERE order_date >= '2024-01-01'
AND order_date < '2024-02-01';
4.2.2 使用EXISTS替代IN
-- 问题SQL
SELECT * FROM orders o
WHERE o.customer_id IN (
SELECT c.id FROM customers c
WHERE c.vip_level > 3
);
-- 优化后
SELECT * FROM orders o
WHERE EXISTS (
SELECT 1 FROM customers c
WHERE c.id = o.customer_id
AND c.vip_level > 3
);
4.2.3 分页查询优化
-- 问题SQL:深分页
SELECT * FROM orders
ORDER BY id
LIMIT 1000000, 20;
-- 优化方案1:使用延迟关联
SELECT o.* FROM orders o
INNER JOIN (
SELECT id FROM orders
ORDER BY id
LIMIT 1000000, 20
) t ON o.id = t.id;
-- 优化方案2:使用游标方式
SELECT * FROM orders
WHERE id > last_seen_id
ORDER BY id
LIMIT 20;
4.3 使用Hint优化
-- 常用Hint示例
SELECT /*+
USE_PLAN_CACHE(NONE) -- 不使用计划缓存
QUERY_TIMEOUT(10000000) -- 设置查询超时
READ_CONSISTENCY(WEAK) -- 弱一致性读
USE_HASH(t1 t2) -- 使用Hash Join
USE_NL(t1 t2) -- 使用Nested Loop Join
USE_MERGE(t1 t2) -- 使用Merge Join
LEADING(t1 t2 t3) -- 指定JOIN顺序
INDEX(t1 idx_name) -- 指定使用的索引
FULL(t1) -- 强制全表扫描
PARALLEL(4) -- 并行度
*/
t1.*, t2.*
FROM
table1 t1
JOIN table2 t2 ON t1.id = t2.ref_id;
4.4 并行执行优化
-- 1. 设置会话级并行度
SET SESSION parallel_servers_target = 64;
SET SESSION parallel_degree_policy = AUTO;
-- 2. 查询级别指定并行度
SELECT /*+ PARALLEL(8) */
COUNT(*),
SUM(amount)
FROM large_orders
WHERE order_date >= '2024-01-01';
-- 3. 表级别设置并行度
ALTER TABLE large_orders PARALLEL 8;
-- 4. 监控并行执行
SELECT
SQL_ID,
PLAN_ID,
PX_SERVERS_REQUESTED,
PX_SERVERS_ALLOCATED,
ELAPSED_TIME,
CPU_TIME
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
PX_SERVERS_ALLOCATED > 0
ORDER BY
REQUEST_TIME DESC;
五、执行计划缓存管理
5.1 计划缓存分析
-- 查看计划缓存使用情况
SELECT
TENANT_ID,
SVR_IP,
SQL_NUM,
MEM_USED,
MEM_HOLD,
ACCESS_COUNT,
HIT_COUNT,
HIT_RATE,
PLAN_NUM,
PLAN_MEM_USED
FROM
oceanbase.GV$OB_PLAN_CACHE_STAT;
-- 查看特定SQL的计划缓存
SELECT
SQL_ID,
PLAN_ID,
SCHEMA_VERSION,
MERGED_VERSION,
PLAN_TYPE,
PLAN_SIZE,
FIRST_LOAD_TIME,
LAST_ACTIVE_TIME,
AVG_EXE_TIME,
SLOWEST_EXE_TIME,
HIT_COUNT
FROM
oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
WHERE
SQL_ID = 'your_sql_id';
5.2 计划缓存清理
-- 清理特定SQL的执行计划
ALTER SYSTEM FLUSH PLAN CACHE GLOBAL SQL_ID = 'your_sql_id';
-- 清理特定租户的所有计划缓存
ALTER SYSTEM FLUSH PLAN CACHE TENANT = tenant_name;
-- 全局清理计划缓存
ALTER SYSTEM FLUSH PLAN CACHE GLOBAL;
六、自动化诊断脚本
6.1 CPU热点SQL自动诊断
DELIMITER //
CREATE PROCEDURE diagnose_cpu_intensive_sql()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_sql_id VARCHAR(64);
DECLARE v_plan_id BIGINT;
DECLARE v_cpu_time BIGINT;
DECLARE cur CURSOR FOR
SELECT SQL_ID, PLAN_ID, AVG(CPU_TIME) as avg_cpu
FROM oceanbase.GV$OB_SQL_AUDIT
WHERE REQUEST_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY SQL_ID, PLAN_ID
HAVING AVG(CPU_TIME) > 100000
ORDER BY avg_cpu DESC
LIMIT 10;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- 创建临时结果表
CREATE TEMPORARY TABLE IF NOT EXISTS cpu_diagnosis_result (
sql_id VARCHAR(64),
plan_id BIGINT,
issue_type VARCHAR(100),
recommendation TEXT,
priority INT
);
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_sql_id, v_plan_id, v_cpu_time;
IF done THEN
LEAVE read_loop;
END IF;
-- 检查是否存在全表扫描
IF EXISTS (
SELECT 1 FROM oceanbase.GV$OB_SQL_AUDIT
WHERE SQL_ID = v_sql_id AND TABLE_SCAN = 1
) THEN
INSERT INTO cpu_diagnosis_result VALUES
(v_sql_id, v_plan_id, 'FULL_TABLE_SCAN',
'Consider adding appropriate index', 1);
END IF;
-- 检查是否存在大量排序
IF EXISTS (
SELECT 1 FROM oceanbase.GV$OB_SQL_AUDIT
WHERE SQL_ID = v_sql_id AND SORTS > 1000
) THEN
INSERT INTO cpu_diagnosis_result VALUES
(v_sql_id, v_plan_id, 'EXCESSIVE_SORTING',
'Consider adding index on ORDER BY columns', 2);
END IF;
END LOOP;
CLOSE cur;
-- 输出诊断结果
SELECT * FROM cpu_diagnosis_result ORDER BY priority;
DROP TEMPORARY TABLE cpu_diagnosis_result;
END//
DELIMITER ;
6.2 执行计划对比分析
-- 对比同一SQL不同执行计划的性能
WITH plan_comparison AS (
SELECT
SQL_ID,
PLAN_ID,
COUNT(*) AS exec_count,
AVG(ELAPSED_TIME) AS avg_elapsed,
AVG(CPU_TIME) AS avg_cpu,
AVG(LOGICAL_READS) AS avg_reads,
AVG(ROW_PROCESSED) AS avg_rows
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
SQL_ID = 'your_sql_id'
AND REQUEST_TIME > DATE_SUB(NOW(), INTERVAL 24 HOUR)
GROUP BY
SQL_ID, PLAN_ID
)
SELECT
p1.PLAN_ID AS plan_id,
p1.exec_count,
ROUND(p1.avg_elapsed/1000, 2) AS avg_elapsed_ms,
ROUND(p1.avg_cpu/1000, 2) AS avg_cpu_ms,
p1.avg_reads,
p1.avg_rows,
CASE
WHEN p1.avg_cpu < (SELECT MIN(avg_cpu) FROM plan_comparison) * 1.1
THEN 'OPTIMAL'
WHEN p1.avg_cpu < (SELECT AVG(avg_cpu) FROM plan_comparison)
THEN 'GOOD'
ELSE 'POOR'
END AS performance_rating
FROM
plan_comparison p1
ORDER BY
p1.avg_cpu;
七、性能调优最佳实践
7.1 建立性能基线
-- 创建性能基线表
CREATE TABLE sql_performance_baseline (
sql_id VARCHAR(64),
plan_id BIGINT,
baseline_date DATE,
avg_cpu_ms DECIMAL(10,2),
avg_elapsed_ms DECIMAL(10,2),
avg_logical_reads BIGINT,
p95_elapsed_ms DECIMAL(10,2),
p99_elapsed_ms DECIMAL(10,2),
daily_executions BIGINT,
PRIMARY KEY (sql_id, plan_id, baseline_date)
);
-- 定期收集基线数据
INSERT INTO sql_performance_baseline
SELECT
SQL_ID,
PLAN_ID,
CURDATE(),
AVG(CPU_TIME)/1000,
AVG(ELAPSED_TIME)/1000,
AVG(LOGICAL_READS),
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY ELAPSED_TIME)/1000,
PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY ELAPSED_TIME)/1000,
COUNT(*)
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
REQUEST_TIME >= CURDATE()
AND REQUEST_TIME < CURDATE() + INTERVAL 1 DAY
GROUP BY
SQL_ID, PLAN_ID;
7.2 性能监控告警
-- 检测性能退化
SELECT
current.sql_id,
current.plan_id,
baseline.avg_cpu_ms AS baseline_cpu,
current.avg_cpu_ms AS current_cpu,
ROUND((current.avg_cpu_ms - baseline.avg_cpu_ms) / baseline.avg_cpu_ms * 100, 2) AS degradation_pct
FROM (
SELECT
SQL_ID,
PLAN_ID,
AVG(CPU_TIME)/1000 AS avg_cpu_ms
FROM
oceanbase.GV$OB_SQL_AUDIT
WHERE
REQUEST_TIME > DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY
SQL_ID, PLAN_ID
) current
JOIN sql_performance_baseline baseline
ON current.sql_id = baseline.sql_id
AND current.plan_id = baseline.plan_id
AND baseline.baseline_date = CURDATE() - INTERVAL 1 DAY
WHERE
current.avg_cpu_ms > baseline.avg_cpu_ms * 1.5 -- 性能退化超过50%
ORDER BY
degradation_pct DESC;
八、参考资源
九、总结
- 监控先行:建立完善的SQL性能监控体系
- 基线管理:定期收集性能基线,及时发现退化
- 计划分析:深入理解执行计划,找出性能瓶颈
- 索引优化:合理创建和维护索引
- SQL改写:掌握常见的SQL优化模式
- 并行执行:充分利用OceanBase的并行处理能力
- 持续优化:建立持续的性能优化机制
更多推荐



所有评论(0)