news 2026/5/26 0:11:31

开发转兼职DBA(二):执行计划教我做事

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
开发转兼职DBA(二):执行计划教我做事

开发转兼职DBA(二):执行计划教我做事

查询慢了不知道为什么,加了索引还是慢,复合索引怎么建,执行计划怎么看——这些不是DBA的专利,是每个写SQL的开发者迟早要面对的事。


文章目录

  • 开发转兼职DBA(二):执行计划教我做事
    • 起点:查询慢了怎么办
    • 执行计划是什么
      • Oracle怎么看执行计划
      • 已经在跑的SQL怎么看
    • 我遇到的第一个问题:全表扫描
      • 解决:加索引 + 改查询
    • 索引的本质
    • 第二个问题:加了索引还是慢
    • 第三个问题:索引不是越多越好
    • 常见的执行计划操作类型
    • 统计信息:执行计划的基石
    • 我学到的几条规矩
    • 从"SQL消费者"到"SQL思考者"

起点:查询慢了怎么办

上一篇说到,社保系统查一个人要30秒。我当时的排查思路是:

  1. 重启数据库 → 没用
  2. 重启应用服务器 → 没用
  3. 清一下缓存 → 没用
  4. 问同事 → 他也不知道
  5. 百度"Oracle查询慢怎么办" → 有人说看执行计划

就这样,我第一次打开了执行计划。

执行计划是什么

执行计划就是数据库的施工图。

你写一条SQL,数据库不会直接执行。它会先"想"一下:这条SQL有几种执行方式?每种方式大概花多少成本?选成本最低的那种。

这个"想"的过程叫查询优化(Query Optimization),"想"出来的方案就是执行计划(Execution Plan)。

Oracle怎么看执行计划

EXPLAINPLANFORSELECT*FROMkc22WHEREsfzh='110101199001011234';SELECT*FROMTABLE(DBMS_XPLAN.DISPLAY);

输出大概这样:

Plan hash value: 1234567890 | Id | Operation | Name | Rows | Bytes | Cost | |----|-----------------------------|------------|------|-------|------| | 0 | SELECT STATEMENT | | 1 | 100 | 2 | | 1 | TABLE ACCESS BY INDEX ROWID| KC22 | 1 | 100 | 2 | | 2 | INDEX UNIQUE SCAN | PK_KC22_SFZH | 1 | | 1 |

关键看几个东西:

  • Operation:数据库在干什么。TABLE ACCESS FULL是全表扫描,INDEX RANGE SCAN是走索引,INDEX UNIQUE SCAN是走唯一索引
  • Cost:数据库估算的成本。越低越好
  • Rows:数据库估算会返回多少行。和实际差太远说明统计信息不准

已经在跑的SQL怎么看

生产环境的SQL已经在执行了,怎么回头看它的执行计划?

SELECT*FROMTABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id=>'abc123',format=>'ALL'));

或者查正在跑的SQL:

SELECTsql_id,sql_textFROMv$sqlWHEREsql_textLIKE'%kc22%';

拿到sql_id再去查执行计划。

我遇到的第一个问题:全表扫描

SELECT*FROMkc22WHERExmLIKE'%张%';

执行计划:

| Id | Operation | Name | Rows | Cost | |----|-------------------|------|-------|------| | 0 | SELECT STATEMENT | | 30000 | 200 | | 1 | TABLE ACCESS FULL| KC22 | 30000 | 200 |

TABLE ACCESS FULL——30万行全扫一遍。原因有两个:

  1. LIKE '%张%',通配符在前面,索引用不上
  2. xm字段上根本没有索引

解决:加索引 + 改查询

CREATEINDEXidx_kc22_xmONkc22(xm);

然后改查询,通配符放后面:

SELECT*FROMkc22WHERExmLIKE'张%';

执行计划变了:

| Id | Operation | Name | Rows | Cost | |----|-----------------------------|------------|------|------| | 0 | SELECT STATEMENT | | 500 | 5 | | 1 | TABLE ACCESS BY INDEX ROWID| KC22 | 500 | 5 | | 2 | INDEX RANGE SCAN | IDX_KC22_XM| 500 | 2 |

Cost从200降到5。查询从30秒降到毫秒级。

这一刻我理解了一件事:索引不是加不加的问题,是加在哪、怎么查的问题。

索引的本质

索引就是书的目录。没有目录,找"张三"要从第一页翻到最后一页。有了目录,先查目录知道"张三"在第37页,直接翻过去。

数据库的索引通常是B+树。B+树的特点:

  1. 叶子节点有序——支持范围查询(BETWEENLIKE '张%'
  2. 叶子节点有指针——范围扫描时不需要回溯父节点
  3. 非叶子节点只存键值——树的高度很低,通常3~4层就能索引几千万行

查找过程:

根节点 → 比较键值 → 走对应的子节点 → ... → 叶子节点 → 找到行号 → 回表取数据

"回表"是关键概念——索引里存的是键值+行号(ROWID),拿到行号后还要回原表取其他字段。如果SELECT只要索引里的字段,就不需要回表,这叫索引覆盖

第二个问题:加了索引还是慢

社保系统有个查询:按身份证号和姓名同时查。

SELECT*FROMkc22WHEREsfzh='110101199001011234'ANDxm='张三';

我加了两个索引:

CREATEINDEXidx_kc22_sfzhONkc22(sfzh);CREATEINDEXidx_kc22_xmONkc22(xm);

以为够了吧?执行计划一看:

| Id | Operation | Name | Rows | Cost | |----|-----------------------------|---------------|------|------| | 0 | SELECT STATEMENT | | 1 | 4 | | 1 | TABLE ACCESS BY INDEX ROWID| KC22 | 1 | 4 | | 2 | INDEX UNIQUE SCAN | PK_KC22_SFZH | 1 | 2 |

只走了sfzh上的索引。xm上的索引根本没用。

为什么?Oracle的优化器判断:身份证号已经能唯一定位一条记录了,再查姓名是多余的。所以只用了sfzh的索引。

那这个查询其实没问题——身份证号能唯一定位,效率已经很高了。

但换一个场景:按姓名和性别查。

SELECT*FROMkc22WHERExm='张三'ANDxb='1';

这时候只有一个索引能用。如果"张三"有500个,数据库要先从索引里找到500个行号,再回表500次去检查性别。

更好的做法:复合索引

CREATEINDEXidx_kc22_xm_xbONkc22(xm,xb);

复合索引的排列顺序很重要。原则是选择性高的放前面

  • 姓名(选择性高,"张三"在30万人里可能有几百个)放前面
  • 性别(选择性低,只有"男""女"两个值)放后面

为什么?因为复合索引是按定义顺序组织的。先按姓名排序,相同姓名再按性别排序。查询时先用姓名缩小范围,再用性别进一步过滤。如果性别放前面,索引的第一层只有两个分支,几乎没有过滤效果。

第三个问题:索引不是越多越好

有段时间我给每个查询字段都加了索引。结果:

  1. INSERT变慢了——每插一条数据,要同时更新所有索引
  2. UPDATE变慢了——改了一个索引列的值,索引要重新组织
  3. 存储空间涨了——索引也要占磁盘

一个表5个索引,意味着每次INSERT要写6个地方(1个表+5个索引)。

经验值:一个表的索引不要超过5~6个,高频查询的字段才加。可以通过监控V$SQL找出真正慢的查询,有针对性地加。

常见的执行计划操作类型

操作含义什么时候出现
TABLE ACCESS FULL全表扫描没有索引,或索引不适用
INDEX UNIQUE SCAN唯一索引扫描等值查询唯一索引列
INDEX RANGE SCAN索引范围扫描范围查询、前缀LIKE
TABLE ACCESS BY INDEX ROWID通过索引回表索引里没有所需的所有列
SORT ORDER BY排序ORDER BY且没有索引支持
HASH JOIN哈希连接大表关联,等值连接
NESTED LOOPS嵌套循环小表驱动大表
MERGE JOIN合并连接两个表都按连接列排序

看到TABLE ACCESS FULL不要慌——小表全表扫描比走索引还快。大表(超过几万行)全表扫描才是问题。

统计信息:执行计划的基石

有一次,我明明加了索引,执行计划还是走全表扫描。

折腾半天才发现:Oracle的统计信息过期了。

执行计划是优化器根据统计信息做决策的。统计信息包括:表有多少行、每个列有多少不同的值、数据分布情况等。如果统计信息不准,优化器就会做出错误判断。

-- 手动收集统计信息BEGINDBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCOTT',tabname=>'KC22',cascade=>TRUE);END;/

收集完,执行计划立刻变了。走了索引。

生产环境要定期收集统计信息。Oracle有自动任务,但有时候不够——大批量数据导入后、大量删除后,统计信息可能严重失真。

我学到的几条规矩

  1. 写SQL之前先想执行计划——这条SQL会扫多少行?能走索引吗?
  2. 索引加在查询条件上——SELECT的列不影响索引选择,WHERE的列才影响
  3. 复合索引注意顺序——选择性高的放前面
  4. 不要盲目加索引——索引有代价,写入变慢
  5. 慢查询先看执行计划——别猜,让数据库告诉你它在干什么
  6. 定期收集统计信息——执行计划的准确性依赖统计信息

从"SQL消费者"到"SQL思考者"

这个阶段最大的变化:以前写SQL只考虑"对不对",现在开始考虑"快不快"。

执行计划就是那个转折点。它让数据库告诉你它在干什么,而不是让你猜。

下一篇要进入更深的水域——当查询优化解决不了问题,数据库本身挂了的时候。


标签:#DBA #Oracle #执行计划 #索引 #全表扫描 #复合索引 #统计信息 #SQL优化

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 0:09:01

靶场启动失败的五大根因与渗透测试新手生存指南

1. 别再盲目刷靶场:为什么90%的新手练了半年还是卡在“连不上靶机”这一步你是不是也这样:下载了十几个CTF平台、靶场镜像,兴致勃勃地配好VirtualBox,导入ISO,启动——然后卡在黑屏、蓝屏、网络不通、SSH连不上、靶机I…

作者头像 李华
网站建设 2026/5/26 0:07:55

5分钟实现音乐自由:Mac端QQ音乐加密格式转换终极指南

5分钟实现音乐自由:Mac端QQ音乐加密格式转换终极指南 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录,默认转…

作者头像 李华
网站建设 2026/5/26 0:05:17

基于Arduino的智能蓝调节拍器:DIY音乐练习伴侣

1. 项目概述:一个能“演奏”蓝调的低成本节拍器玩乐器的人,对节拍器这东西又爱又恨。它像一位严厉的监工,用单调的“嘀嗒”声强迫你跟上节奏。但你想过没有,这个监工其实可以很有趣?几年前,我在练习蓝调吉他…

作者头像 李华
网站建设 2026/5/26 0:05:14

Harness 中的自适应超时:基于百分位延迟

Harness 中的自适应超时:基于百分位延迟的DevOps效能革命 1. 引入与连接:从CI/CD的"超时噩梦"说起 周一早上9点,你刚到公司,提交了一行修复线上bug的代码,期待CI/CD pipeline快速跑完就能上线。结果等了25分钟,收到了pipeline超时失败的通知——你想起上周为了…

作者头像 李华
网站建设 2026/5/26 0:01:38

强化学习策略参数调节方法及值迭代算法实现 CS188 Proj3 学习笔记

强烈推荐的更好的阅读体验 Q1.Value Iteration 第一个问题是最基础的值迭代实现,这个问题没有什么难度,主要就是一边看着公式一遍敲代码复现。可以先回顾一下Note8中的Value Iteration框架.唯一唯一需要注意的就是需要使用的是batch版本,而…

作者头像 李华
网站建设 2026/5/25 23:56:02

AI 充电式电动工具智能功率 MOSFET 完整选型方案

随着 AI 技术在充电式电动工具中的深度渗透(如智能调速、电池健康管理、自适应负载控制),对功率 MOSFET 提出更高要求:高效率、低热耗、高可靠性。微碧半导体(VBsemi)基于 SGT 及 Trench 工艺,为…

作者头像 李华