SQL 优化

SQL 优化是通过分析 SQL 执行逻辑、执行计划和数据库资源使用,调整 SQL 写法、索引结构、数据库参数和表设计,以最小的系统资源消耗获得最快的执行速度的系统性工程。它不是简单的 "加索引",而是从 SQL 解析、执行到数据返回的全链路优化,是解决 80% 以上数据库性能问题的核心手段。

1、SQL 优化基础

1.1、SQL 执行的完整流程

执行一条 SQL 需要经过 5 个阶段,每个阶段都可能成为性能瓶颈:

SQL 输入 → 解析阶段 → 优化阶段 → 执行阶段 → 数据返回

  • 解析阶段 :检查 SQL 语法、语义和权限,生成解析树
    • 硬解析:第一次执行 SQL,生成新的执行计划(消耗大量 CPU 和内存)
    • 软解析:SQL 已在共享池中存在,直接复用执行计划(性能最优)
  • 优化阶段:优化器根据统计信息生成最优执行计划
  • 执行阶段:按照执行计划访问数据
  • 数据返回:将结果集返回给客户端

核心原则:SQL 优化的本质是减少数据访问量和减少数据处理量。

1.2、硬解析 vs 软解析

对比维度 硬解析 软解析
执行计划生成 重新生成 直接复用
CPU 消耗 极高 极低
共享池锁竞争 严重
适用场景 第一次执行 重复执行

优化目标:软解析率 > 99%。

1.3、执行计划:SQL 优化的核心

执行计划是 Oracle 优化器生成的 SQL 执行步骤,是 SQL 优化的唯一依据。

查看执行计划的方法:

sql 复制代码
-- 方法1:EXPLAIN PLAN(查看预估执行计划)
EXPLAIN PLAN FOR SELECT * FROM emp WHERE empno=7369;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());

-- 方法2:DBMS_XPLAN.DISPLAY_CURSOR(查看真实执行计划,最准确)
SELECT /*+ GATHER_PLAN_STATISTICS */ * FROM emp WHERE empno=7369;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT=>'ALLSTATS LAST'));

-- 方法3:SQL*Plus AUTOTRACE
SET AUTOTRACE ON;
SELECT * FROM emp WHERE empno=7369;

执行计划关键信息解读:

  • Id:执行步骤编号,从 0 开始,数字越小越先执行
  • Operation:执行操作(如全表扫描、索引扫描、连接)
  • Name:操作涉及的表或索引名称
  • Rows:优化器预估的返回行数(基数)
  • Bytes:预估的返回字节数
  • Cost:优化器计算的执行成本(值越小越好)
  • Time:预估的执行时间

2、SQL 优化方法论

2.1、第一步:定位 Top SQL

先从系统整体入手,找出消耗资源最多的 SQL:

sql 复制代码
-- 从AWR中获取历史Top SQL(按CPU消耗排序)
SELECT 
  sql_id,
  sql_text,
  cpu_time/1000000 AS cpu_sec,
  elapsed_time/1000000 AS elapsed_sec,
  executions,
  buffer_gets
FROM dba_hist_sqlstat
WHERE snap_id BETWEEN :start_snap AND :end_snap
ORDER BY cpu_time DESC;

-- 从ASH中获取当前正在执行的慢SQL
SELECT 
  sql_id,
  sql_text,
  session_id,
  wait_event,
  seconds_in_wait
FROM v$active_session_history
WHERE sample_time > SYSDATE-1/24
  AND session_state='ON CPU'
ORDER BY seconds_in_wait DESC;

2.2、第二步:分析执行计划

拿到 SQL 后,第一步就是查看真实执行计划,重点关注:

  • 全表扫描(TABLE ACCESS FULL):大表全表扫描通常是性能问题的根源
  • 索引扫描类型:优先使用 INDEX UNIQUE SCAN,其次 INDEX RANGE SCAN,避免 INDEX FULL SCAN
  • 连接方式:嵌套循环适合小表驱动大表,哈希连接适合大表连接
  • 基数预估:如果预估行数和实际行数相差 10 倍以上,说明统计信息不准确
  • 排序操作:避免不必要的排序(如 ORDER BY、GROUP BY、DISTINCT)

2.3、第三步:实施优化措施

根据执行计划分析结果,选择合适的优化手段:

  • 索引优化:创建合适的索引,删除无用索引
  • SQL 改写:优化 SQL 写法,避免低效语法
  • 统计信息更新:确保表和索引的统计信息准确
  • 参数调整:调整优化器相关参数
  • 表结构优化:分区表、表压缩、数据类型优化

2.4、第四步:验证优化效果

优化后必须验证效果,对比优化前后的性能指标:

  • 执行时间
  • CPU 消耗
  • 逻辑读(buffer gets)
  • 物理读(physical reads)
  • 执行计划是否符合预期

实例:某公司 ERP 慢查询优化

**S(Situation - 情境):**某公司核心 ERP 系统的生产订单查询功能突然变慢,原来 1 秒内返回的查询现在需要 30 秒以上,导致生产线调度系统无法正常工作。

**T(Task - 任务):**在 30 分钟内解决 SQL 性能问题,恢复系统正常运行,确保生产线不受影响。

A(Action - 行动):

1、定位 Top SQL,使用 ASH 找到消耗 CPU 最多的 SQL;

2、分析执行计划:查看执行计划发现,SQL 正在对 production_orders 表(5000 万行)进行全表扫描,成本高达 120 万。

3、查找问题根源:

order_date 和 status 列没有复合索引;

表的统计信息已经 3 个月没有更新;

优化器错误地选择了全表扫描;

4、实施优化:

sql 复制代码
-- 1. 创建复合索引(order_date在前,status在后)
CREATE INDEX idx_prod_orders_date_status ON production_orders(order_date, status) ONLINE;

-- 2. 收集表的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS('ERP', 'PRODUCTION_ORDERS', cascade=>TRUE);

5、验证效果:再次执行 SQL,执行时间从 30 秒缩短到 0.2 秒,数据库 CPU 使用率下降到 30%。

**R(Result - 结果):**SQL 执行时间从 30 秒缩短到 0.2 秒,性能大幅提升;数据库 CPU 使用率恢复正常;生产线调度系统恢复正常运行,没有影响生产进度;落地改进:建立 SQL 性能基线,定期检查和更新统计信息。

相关推荐
ClouGence1 天前
Oracle 数据同步为什么会出现数据不一致?长事务是常被忽略的原因
数据库·后端·oracle
zzzzzz3104 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
云技纵横6 天前
唯一索引 INSERT 死锁实战:5 秒复现交叉插入的 S 锁循环等待
sql·mysql
ClouGence7 天前
Oracle CDC 架构优化:从主库直连到 DataGuard 备库同步
数据库·后端·oracle
曹牧8 天前
Oracle EXPLAIN PLAN
数据库·oracle
BD_Marathon8 天前
SQL学习指南——视图
数据库·sql
贤时间8 天前
codex 助力oracle ebs 开发
数据库·oracle
秉承初心8 天前
PostgreSQL 数据性能瓶颈突破实战
数据库·postgresql·oracle
2601_962072558 天前
李梦娇常识4600问|题库|打印版
sql·华为od·华为·c#·华为云·.net·harmonyos
HackTwoHub8 天前
Sqli-Scanner SQL注入SKILL自动化挖掘SQL注入,零依赖自动化SQL注入挖掘,赏金猎人
数据库·人工智能·sql·web安全·网络安全·自动化·系统安全