You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Oracle SQL查询致数据库100%CPU占用问题排查与优化求助

排查与优化Oracle SQL查询CPU占用过高问题

咱们先拆解下你的查询逻辑:你要找到对应invoice_id=11954081的、未取消(canceled=0)且无前驱记录(previous=0)的发票类确认单(confirm_type='INVOICE'),再关联对应的人员表和发票确认关联表。CPU跑满通常是因为查询做了大量全表扫描、低效排序或不合理关联,咱们一步步分析解决:

一、先查执行计划(关键第一步)

你可以先执行以下语句生成执行计划,重点看几个核心点:

EXPLAIN PLAN FOR
SELECT * FROM confirm_v c JOIN person p ON c.created_by=p.id INNER JOIN invoice_confirm ic ON ic.confirm_id=c.id WHERE c.id = (SELECT id FROM (SELECT c2.id FROM confirm c2 JOIN invoice_confirm ic2 ON ic2.confirm_id=c2.id WHERE ic2.invoice_id=11954081 AND c2.previous=0 AND c2.canceled=0 AND c2.confirm_type='INVOICE' ORDER BY c2.id) WHERE rownum=1);

查看计划时关注:

  • 是否存在FULL TABLE SCAN(全表扫描),尤其是confirminvoice_confirm这两张核心表
  • SORT ORDER BY的行数,如果行数过大,排序会极度消耗CPU
  • 关联操作的类型,是否出现低效的嵌套循环或意外的笛卡尔积

二、SQL语句优化:简化嵌套,让优化器更易选高效计划

原查询的三层子查询可以大幅简化,既提升可读性,也能减少不必要的计算开销:

优化版本1(Oracle 12c+支持)

FETCH FIRST 1 ROW ONLY替代嵌套的rownum逻辑,更简洁高效:

SELECT * 
FROM confirm_v c 
JOIN person p ON c.created_by = p.id 
INNER JOIN invoice_confirm ic ON ic.confirm_id = c.id 
WHERE c.id = (
    SELECT c2.id 
    FROM confirm c2 
    JOIN invoice_confirm ic2 ON ic2.confirm_id = c2.id 
    WHERE ic2.invoice_id = 11954081 
      AND c2.previous = 0 
      AND c2.canceled = 0 
      AND c2.confirm_type = 'INVOICE' 
    ORDER BY c2.id 
    FETCH FIRST 1 ROW ONLY
);

优化版本2(兼容Oracle 11g及以下)

如果版本较低,简化子查询层级,提前过滤数据减少关联量:

SELECT * 
FROM (
    SELECT c2.id 
    FROM confirm c2 
    JOIN invoice_confirm ic2 ON ic2.confirm_id = c2.id 
    WHERE ic2.invoice_id = 11954081 
      AND c2.previous = 0 
      AND c2.canceled = 0 
      AND c2.confirm_type = 'INVOICE' 
    ORDER BY c2.id 
    WHERE rownum = 1
) target_confirm
JOIN confirm_v c ON c.id = target_confirm.id
JOIN person p ON c.created_by = p.id
JOIN invoice_confirm ic ON ic.confirm_id = c.id;

三、索引优化:针对性创建组合索引

从查询条件来看,这两个组合索引能直接解决大部分性能问题:

  • invoice_confirm表索引:快速定位目标发票对应的确认单ID,避免全表扫描
    CREATE INDEX idx_invoice_confirm_invid_confid ON invoice_confirm(invoice_id, confirm_id);
    
  • confirm表索引:覆盖过滤条件和排序字段,让Oracle直接从索引取数,无需回表
    CREATE INDEX idx_confirm_type_pre_cancel_id ON confirm(confirm_type, previous, canceled, id);
    

如果confirm_v是视图,还要确保其底层表的created_by字段有索引(关联person表用)。

四、额外注意点

  • 确认confirm_type的过滤值大小写是否和数据一致?虽然Oracle默认不区分大小写,但如果索引是区分大小写的,可能导致索引失效
  • confirm表数据量极大,ORDER BY c2.id原本会触发大排序,上述组合索引因为包含id字段,索引本身有序,Oracle可以直接取第一条,无需额外排序

内容的提问来源于stack exchange,提问作者Tom

火山引擎 最新活动