Oracle--SQL性能优化与提升策略

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除

一、导致性能问题的内在原因

系统性能问题的底层原因主要有三个方面:

  • CPU占用率过高导致资源争用和等待
  • 内存使用率过高导致内存不足并需要使用磁盘虚拟内存
  • I/O占用率过高导致磁盘访问需要等待。

性能影响的优先级从高到低依次是CPU-->内存-->I/O。在PL/SQL性能优化中,重点是减少I/O瓶颈,即尽量减少对磁盘I/O的访问

根据上述分析,PL/SQL优化的核心思想可以总结为以下几点:

  • 避免使用过多复杂的SQL脚本,以减少系统的解析过程
  • 避免进行无用的计算,例如避免出现死循环等低效代码
  • 避免浪费内存空间,例如避免执行不必要的SQL脚本,以免导致内存不足
  • 充分利用内存中的计算和访问速度快的优势
  • 尽可能减少磁盘的访问数据量
  • 尽可能减少磁盘的访问次数,这是PL/SQL优化中的重要原则

二、如何进行SQL优化

1、选择最有效率的表名顺序

Oracle的解析器从右到左处理FROM子句中的表名,因此最后写的表(基础表 driving table)最先处理。在多表查询时,建议**++将记录最少的表作为基础表++,以减少连接操作的数据量。Oracle会通过排序和合并**方式连接表:先扫描并排序基础表,再扫描其他表并与基础表匹配。这种处理顺序对查询性能至关重要,建议按照此规则编写SQL语句。目前主要使用基于成本的优化器(CBO),它会自动评估最佳执行计划,但遵循上述规则有助于提升SQL效率。

sql 复制代码
 --例如:员工表emp有16384条记录,而部门表dept有1条记录,选择dept作为基础表  
 select count(*) from emp,dept;      --选择dept作为基础表耗时0.96s
 select count(*) from dept,emp;      --选择emp作为基础表耗时26.09s

2、WHERE子句中的连接顺序

ORACLE 采用自下而上 的顺序解析 WHERE 子句,根据这个原理,表之间的连接必须写在其他 WHERE 条件之前

sql 复制代码
 --低效:
 select dept.deptno,emp.job
 from emp.dept
 where emp.job='MANAGER' AND emp.deptno=dept.deptno;
 ​
 --优化后:
 select dept.deptno,emp.job
 from emp.dept
 where emp.deptno=dept.deptno AND emp.job='MANAGER';

3、SELECT子句中避免使用'*'

在SELECT子句中使用动态SQL列引用"*"虽然方便,但效率低下。Oracle解析时会通过查询数据字典将""转换为所有列名,这增加了额外的开销和时间成本。因此,建议在SELECT子句中尽量避免使用"",而是明确列出所需的列名,以提高查询效率。

4、用EXITS 替代 IN

使用EXISTS替换IN效果有时不明显,但在基础表查询需联接另一表时,EXISTS通常提高效率,因EXISTS找到匹配即停止搜索

sql 复制代码
 --低效
 select *
 from table_name1
 where column1 in
 (select column1 from table_name2
     where column2=str_column2
     and column3='xxxx');
 ​
 --优化后:
 select *
 from table_name1
 where exists
 (select 1 from table_name2
     where column1=table_name2.column1
     and column2=str_column2
     and column3='xxxx');

5、用 NOT EXISTS 替代 NOT IN

Oracle 10g前 NOT IN 效率低,10g 虽改进但仍存在问题。建议用 NOT EXISTS 替代 NOT IN ,因 NOT IN 对子查询表全表遍历且需内部排序合并 ,效率低。改写为 NOT EXISTS 可提升效率

sql 复制代码
 --低效:
 select * 
 from table_name1
 where column1 NOT IN
 (select column1 from table_name2
     where column3='xxxx');
 ​
 --优化后:
 select *
 from table_name1
 where not exists
 (select 1 from table_name2
     where column1=table_name2.column1
     and column3='xxxx');

6、用表连接替换 EXISTS

子查询的表和主表查询是多对一的情况 ,一般采用表连接的方式比 EXISTS 更有效率

sql 复制代码
 --低效:
 select table name1.*
 from table name1
 where exists (
 select 1 from table_name2
     where column1 =table name1.column1
     and column2='xxxx'
     and column3='xxxxxx');
     
 --优化后:
 SELECT table_name1.*
 FROM table namel,table name2 Where
 table_name1.column1=table_name2.column1
 and table_name2.column2='xxxx'
 and column3='xxxx';

7、减少对表的查询

该问题是我们编程中出现过的问题,请大家一定注意,并且该类问题优化可以带来较大性能的提升

sql 复制代码
 --低效:
 cursor cur_table_lj1 is
 select column1
 from table1
 where column1 = str_column1 and column2='1111';
 cursor cur_table1_lj2 is
 select column1
 from table1
 where column1 =str_column1 and column2='2222';
 ​
 for rec_lj1 in cur_table1 loop
     业务逻辑1处理
 end loop;
 for rec_lj2 in cur_table2 loop
     业务逻辑2处理
 end loop;
 ​
 --优化后:
 cursor cur_tablel_lj1 is
 select column1,column2
 from table1
 where column1 =str_columnl and column2 in ('11111','22222');
 ​
 for rec_ljl in cur_tablel lj1 loop
 if rec_lj1.column2='11111' then
     业务逻辑1处理......
 end if;
 if rec lj1.column2='22222' then
     业务逻辑2处理....
 end if,
 end loop;

高效的做法使用同样的条件(或者说是索引)只访问一次磁盘,低效的做法访问了2次磁盘,这样速度差 别将近2倍。

8、避免循环(游标)里面嵌查询

游标中不能有游标或update、delete等语句,只能有select语句,但在实际编程中难以完全避免,需尽量减少 。优化方法是将游标循环中的查询语句提前到游标查询中一次性查询出来,减少磁盘访问次数,提升效率。如果无法避免游标中使用查询语句,要确保查询语句使用索引,提高查询速度。

9、尽量用 union all 替换 all

Union 会去掉重复的记录,会有排序的动作,会浪费时间。因此在没有重复记录的情况下或可以允许有重,复记录的话,要尽量采用 uoion all 来关联

10、group by 优化

Group by需要查询后分组,速度慢影响性能,如果查询数据量大,并且分组复杂,这样的査询语句在性能上是有问题的。采用 group by的也一定要进行优化

sql 复制代码
 --低效:
 select table1.column1,table2.column2
 table2.column3,sum(column5),table1.column4
 from table1,table2
 where table1.column1=table2.column1
 and table1.column4='xxxxxx'
 group py table1.column1,table2.column2
 table2.column3,table2.column4
 ​
 --优化后:
 select table1.column1,table2.column2,
 table2.column3,gzze,table1.column4
 from
 (select column1,sum(column5) gzze
 from table1 group by column1) table1,table2
 where table1.column1=table2.column1
 and column4='xxxx';

11、尽量避免用 order by

使用 ORDER BY 会因查询后排序而拖慢速度,尤其数据量大时。尽管有时无法避免使用 ORDER BY ,但需注意排序列表应符合索引,这样能显著提升速度。

12、用where 子句替换Having 子句

避免使用HAVING子句,因为它会++在检索完所有记录后才对结果集进行过滤++ ,这个过程需++要排序、总计等++操作。如果能通过WHERE子句限制记录数量,就能减少这方面的开销。

sql 复制代码
 --低效:
  select column1,count(1) from table1
  group by column1
  having column1 in ('1','2');
  
  --优化后:
  select column1,count(1) from table1
  where column1 in ('1','2')
  group by column1;

HAVING 中的条件一般用于对一些集合函数的比较,如 COUNT() 等等。除此而外,一般的条件应该写在 WHERE 子句中

13、使用表的别名(alias)

在SQL语句中连接多个表时,使用表的别名并将其前缀于每个列名,可减少解析时间及因列名歧义引发的语法错误。

14、COMMIT 使用

  • 提交频率过高会浪费时间,尽管单次提交时间短。
  • 提交可释放资源,在大量数据更新时需及时提交。
sql 复制代码
 --cur_table1 有5000万数据
 n_count :=0
 For arec in cur_table1 loop
     Insert into table ...
     n_count := n_count + 1;
     If n_count = = 100000 then      --10万一提交
         commit;
         n_count := 0
     End if;
 End loop;
 Commit;

15、减少多表关联

  • 表关联越多,查询速度越慢,建议表关联不超过3个(子查询也算表关联)。
  • 大数据量表关联会影响索引效率,可采用建立临时表的方法来提高速度。

三、索引使用优化

1、避免在索引列上使用函数或运算

在实际编程中要注意:在索引列上使用函数或运算,查询条件不会使用索引。

sql 复制代码
 --不使用索引
 select * from table1
 where column1='xxx'
 and to_char(column2,'yyyymm')='200801';
 或者
 select * from table1
 where column1='xxx'
 and column2+1=sysdate;
 ​
 --使用索引
 select * from table1
 where column1='xxx'
 and column2=to_date('200801','yyyymm');
 或者
 select * from table1
 where column1='xxx'
 and column2=sysdate -1;

2、避免改变索引列的类型

索引列的条件如果类型不匹配,则不能使用索引。

3、避免在索引列上使用NOT

避免在索引列上使用 NOT , NOT 不会使查询条件使用索引。对于 != 这样的判断也不能使用索引,因为索引只能告诉你什么存在于表中,而不能告诉你什么不存在于表中。

sql 复制代码
 --低效:
 select * from table1 where not column1='10';
 ​
 --优化后:
 select * from table1 where column1 in ('20','30');

4、用>=替代>

虽然效果不是特别明显,但建议采用这种方式

sql 复制代码
 --低效:
 select * from table1 where column1 > '10';
 ​
 --优化后:
 select * from table1 where column >= '20';

特别说明:两者的区别在于,前者 DBMS 首先定位到 column1=10的记录并且向前扫描到第一个column1 大于 10 的记录,而后者 DBMS 将直接跳到第一个 column1 等于 20 的记录

5、避免在索引列上使用 IS NULL和IS NOT NULL

在Oracle中,索引列使用 IS NULL 或 IS NOT NULL 时不会利用索引,因为空值不存储于索引列。这会导致Oracle停用索引

sql 复制代码
 --低效:
 select * from table1 where column1 is not null;
 ​
 --优化后:
 select * from table1 where column1 in ('10','20','30');

6、带通配符(%)的like语句

%在常量前面索引就不会使用。

sql 复制代码
 --不使用索引:
 select * from table1 where column1 like '%210104';
 select * from table1 where column1 like '%210104%';
 ​
 --使用索引:
 select * from table1 where column1 like '210104%';

7、总是使用索引的第一个列

如果索引是建立在多个列上,只有在它的第一个列被 where 子句引用时,优化器才会选择使用该索引。

sql 复制代码
 --如:table1的复合索引(column1,column2,column3)
 --低效(不会使用索引):
 select * from table1 where column2='110' and column3='200801';
 ​
 --优化后(会使用索引):
 select * from table1 where column1 = '10001000';

如果不使用索引第一列基本上不会使用索引,使用索引要按照索引的顺序使用,另外使用复合索引的列越多,查询的速度就越快

8、 关于索引建立

索引可大幅提升查询速度,但也占用空间。过多索引会影响 INSERT 、 DELETE 和 UPDATE 操作的速度,因这些操作会改变索引顺序,需Oracle调整,导致性能下降。因此,要合理创建有效索引,编程时符合索引规则,而非让索引适应编程。

示例:在某项目数据转换中,采用游标循环插入2000万条数据耗时4小时,因目标表索引过多。解决方法是先删除索引再执行转换脚本,不到1小时完成,重建所有索引仅需半小时。

四、对于千万级的大表应该怎么优化

1、制定优化方案

针对Oracle数据库千万级大表的读、写、计算优化,可采取以下措施:

优化读:
  • 建立合适索引,使用索引覆盖查询避免全表扫描。
  • 使用分区表,将大表拆分,查询时只需扫描部分数据。
  • 增加内存,扩大数据库缓存区,减少磁盘I/O操作。
  • 优化SQL语句,避免子查询、减少连接操作。
优化写:
  • 使用并行写入,将数据写入多个表或节点。
  • 采用批量写入,减少写入操作次数。
  • 减少索引数量,避免过多索引影响写入性能。
  • 避免使用触发器,减少额外的I/O操作。
优化计算:
  • 使用分布式计算,分散计算任务到多个节点。
  • 采用并行计算,将任务划分成多个子任务并行执行。
  • 使用合适的数据结构,减少计算时间。
  • 优化SQL语句,减少计算操作的数据量。

2、优化方案总结

总结优化方案的几种方法:

  • 建立合适的索引:索引可提升查询速度,但过多或不合适的索引会影响数据库性能,需根据实际情况合理建立。

  • 分区表:将大表分成多个小表,提高查询速度和维护效率。

  • 优化SQL语句:减少数据库的I/O操作,提高查询效率。可采用优化查询语句、查询条件,避免使用子查询等方式。

  • 增加内存:扩大数据库缓存区和内存,减少磁盘I/O操作,提高查询效率。

  • 优化磁盘I/O :使用RAID技术、SSD硬盘等方式提升磁盘I/O速度,增强数据库性能。


    学习永无止境,让我们共同进步!!

相关推荐
恒悦sunsite2 小时前
Ubuntu之apt安装ClickHouse数据库
数据库·clickhouse·ubuntu·列式存储·8123
奥尔特星云大使2 小时前
MySQL 慢查询日志slow query log
android·数据库·mysql·adb·慢日志·slow query log
来自宇宙的曹先生2 小时前
MySQL 存储引擎 API
数据库·mysql
间彧2 小时前
MySQL Performance Schema详解与实战应用
数据库
间彧3 小时前
MySQL Exporter采集的关键指标有哪些,如何解读这些指标?
数据库
weixin_446260853 小时前
Django - 让开发变得简单高效的Web框架
前端·数据库·django
mpHH3 小时前
babelfish for postgresql 分析--todo
数据库·postgresql
zizisuo3 小时前
解决在使用Lombok时maven install 找不到符号的问题
java·数据库·maven
老苏畅谈运维4 小时前
Oracle的connect by level在MySQL中的华丽变身
mysql·oracle
程序边界5 小时前
国产之光!金仓数据库KingbaseES Oracle兼容性深度体验大赏
数据库·oracle