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 性能基线,定期检查和更新统计信息。

相关推荐
霸道流氓气质2 小时前
Spring Boot + Jasypt 实战指南:配置文件敏感信息加密完全手册
数据库·spring boot·oracle
郝学胜-神的一滴2 小时前
系统设计 014:缓存深度实战:如何用 Cache 优雅优化数据库读写?
java·数据库·python·缓存·oracle·php·软件构建
JdSnE27zv2 小时前
数据库表字段命名规范
数据库·oracle
数据库小学妹2 小时前
MySQL 误删数据恢复全流程:Binlog 回放+全量备份+延迟从库三种方案实战
数据库·经验分享·mysql·dba
一只fish3 小时前
Oracle官方文档翻译《Database Concepts 26ai》第21章-Oracle AI 数据库中的AI
数据库·人工智能·oracle
暴躁小师兄数据学院12 小时前
【AI大数据工程师特训笔记】第12讲:表分区与索引
大数据·笔记·sql·postgresql
Litluecat12 小时前
信创迁移:Oracle切换海量数据库,慢sql扫描
数据库·sql·oracle·信创·海量
2601_9611940213 小时前
27考研刘晓艳单词pdf
linux·sql·ubuntu·华为·pdf·.net
消失在人海中13 小时前
Oracle的CURRENT REDO丢失,数据丢失风险分析
数据库·oracle