根本原因是ORDER BY暴露了执行路径低效,真正应优先让Oracle避免全量排序;需按ORDER BY字段严格顺序建复合索引、调优PGA内存、确保WHERE命中索引前缀,并用FIRST_ROWS提示优化首行响应。
ORDER BY 在 Oracle 12c 中导致 CPU 时间过长,根本原因不是“排序本身慢”,而是它暴露了底层执行路径的低效——**CPU 高往往意味着大量数据在内存中被反复比较、移动、重排,而这些操作本可避免**。直接调大 PGA_AGGREGATE_TARGET 或换更快 CPU 是治标;真正该优先优化的,是让 Oracle 根本不必做全量排序。
为什么 ORDER BY 会吃掉大量 CPU?
oracle 的排序(sort order by)全程在 pga 内存中进行:读取行 → 提取排序键 → 比较 → 堆排序或快速排序 → 输出。一旦数据量超过 sort_area_size(或自动管理下的 pga 限额),就会 spill 到临时表空间,触发磁盘 i/o + 更多次 cpu 比较 —— 但即使没 spill,纯内存排序对百万级以上结果集仍是 cpu 密集型操作。
常见错误现象包括:
-
EXPLAIN PLAN中出现SORT ORDER BY且ROWS预估远大于实际业务需要的条数 -
V$SQL中该 SQL 的CPU_TIME占ELAPSED_TIME比例 > 70% -
AWR报告里sql ordered by CPU排名靠前,且EXECUTIONS高
ORDER BY 无法走索引的典型场景
很多人以为“字段有索引就能加速 ORDER BY”,但实际常因以下原因失效:
- 排序字段含函数或表达式,如
ORDER BY UPPER(name)→ 索引不匹配 - 混合升序/降序,如
ORDER BY create_time DESC, status ASC,而索引是(create_time, status)(默认全升序)→ 无法复用 - WHERE 条件未命中索引最左前缀,导致优化器放弃索引访问路径,改走全表扫描 + 排序
- 统计信息陈旧,优化器误判索引选择性,认为全表扫描 + 排序代价更低
验证方式:运行 EXPLAIN PLAN FOR ... 后查 PLAN_TABLE,确认 OPERATION 是否为 INDEX RANGE SCAN 或 INDEX FULL SCAN,而非 TABLE ACCESS FULL 后跟 SORT ORDER BY。
分页查询中 OFFSET 越大越慢的本质
写 ORDER BY x OFFSET 1000000 ROWS FETCH NEXT 20 ROWS ONLY,Oracle 必须先完整排序全部满足 WHERE 条件的数据,再跳过前 100 万行 —— CPU 和内存消耗与总结果集大小正相关,与返回行数无关。
- 真实业务中,用户 rarely 翻到第 5 万页;应改用基于游标的分页(如用上一页最后的
create_time和id作为下一页 WHERE 条件) - 若必须用 OFFSET,确保排序字段有高选择性索引,且 WHERE 已大幅收敛数据量(比如加时间范围过滤)
- 避免在视图或复杂子查询外层套
OFFSET,这会阻止优化器下推过滤条件
OPTIMIZER_MODE 设置影响 ORDER BY 执行策略
Oracle 12c 默认 OPTIMIZER_MODE = ALL_ROWS,目标是整体吞吐最优,可能选全排序;而交互式查询更需要快速返回前几行 —— 这时应显式指定 /*+ FIRST_ROWS(10) */ 提示,或设会话级参数:
ALTER SESSION SET OPTIMIZER_MODE = FIRST_ROWS_10;
该设置会引导优化器优先考虑索引扫描 + 有序合并,哪怕总代价略高,也能显著降低首行响应时间及 CPU 累积消耗。但注意:FIRST_ROWS_n 对 OFFSET 类分页无效,只对 FETCH FIRST 生效。
最容易被忽略的一点:即使加了索引、改了优化器模式,如果查询里有 SELECT * 且表含 LOB 字段,Oracle 可能被迫回表读取大字段,导致排序键虽走索引,但实际排序仍发生在宽行上 —— CPU 和内存开销翻倍。务必只查必需列。











