Oracle--数据库优化

开发者博客www.developsearch.com

对于ORACLE数据库的数据存取,主要有四个不同的调整级别:
• 第一级调整是操作系统级包括硬件平台
• 第二级调整是ORACLE  RDBMS级的调整
• 第三级是数据库设计级的调整
• 最后一个调整级是SQL级

1、别名的使用  
别名是大型数据库的应用技巧,就是表名、列名在查询中以一个字母为别名,查询速度要比建连接表快1.5倍。

2、管理组织索引            
索引可以大大加快数据库的查询速度,索引把表中的逻辑值映射到安全的RowID,因此索引能进行快速定位数据的物理地址。但是有些DBA发现,对一个大型表建立的索引,并不能改善数据查询速度,反而会影响整个数据库的性能。这主要是和SGA的数据管理方式有关。ORACLE在进行数据块高速缓存管理时,索引数据比普通数据具有更高的驻留权限,在进行空间竞争时,ORACLE会先移出普通数据。对一个建有索引的大型表的查询时,索引数据可能会用完所有的数据块缓存空间,ORACLE不得不频繁地进行磁盘读写来获取数据,因此在对一个大型表进行分区之后,可以根据相应的分区建立分区索引。如果对这样大型表的数据查询比较频繁,或者干脆不建索引。另外,DBA创建索引时,应尽量保证该索引最可能地被用于where子句中,如果对查询只简单地制定一个索引,并不一定会加快速度,因为索引必须指定一个适合所需的访问路径。

如果检索数据量超过30%的表中记录数.使用索引将没有显著的效率提高.
b. 在特定情况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的区别. 而通常情况下,使用索引比全表扫描要块几倍乃至几千倍!

3、高效地进行SQL语句设计  
通常情况下,可以采用下面的方法优化SQL对数据操作的表现:
    (1)减少对数据库的查询次数,即减少对系统资源的请求,使用快照和显形图等分布式数据库对象可以减少对数据库的查询次数。
    (2)尽量使用相同的或非常类似的SQL语句进行查询,这样不仅充分利用SQL共享池中的已经分析的语法树,要查询的数据在SGA中命中的可能性也会大大增加。
    (3)限制动态SQL的使用,虽然动态SQL很好用,但是即使在SQL共享池中有一个完全相同的查询值,动态SQL也会重新进行语法分析。
    (4)避免不带任何条件的SQL语句的执行。没有任何条件的SQL语句在执行时,通常要进行FTS,数据库先定位一个数据块,然后按顺序依次查找其它数据,对于大型表这将是一个漫长的过程。
    (5)如果对有些表中的数据有约束,最好在建表的SQL语句用描述完整性来实现,而不是用SQL程序中实现。
    (6)可以通过取消自动提交模式,将SQL语句汇集一组执行后集中提交,程序还可以通过显式地用COMMIT和ROLLBACL进行提交和回滚该事务。
    (7)检索大量数据时费时很长,设置行预取数则能改善系统的工作表现,设置一个最大值,当SQL语句返回行超过该值,数值库暂时停止执行,除非用户发出新的指令,开始组织并显示数据,而不是让用户继续等待。

4、合理创建临时表或视图
所谓创建临时表或视图,就是根据需要在数据库基础上创建新表或视图,对于多表关联后再查询信息的可建新表,对于单表查询的可创建视图,这样可充分利用数据库的容量大、可扩充性强等特点,所有条件的判断、数值计算统计均可在数据库服务器后台统一处理后追加到临时表中,形成数据结果的过程可用数据库的过程或函数来实现。

5、过程或函数
利用数据库描述语言编写数据库的过程或函数,然后把过程或函数打成包在数据库后台统一运行包即可。

6、数据复制、快照、视图,远程过程调用技术的运用
数据复制,即将数据一次复制到本地,这样以后的查询就使用本地数据,但是只适合那些变化不大的数据。使用快照也可以在分布式数据库之间动态复制数据,定义快照的自动刷新时间或手工刷新,以保证数据的引用参照完整性。调用远程过程也会大大减少因频繁的SQL语句调用而带来的网络拥挤。

8、SQL的优化

1 WHERE 子句中的连接顺序

   ORACLE 采用自下而上的顺序解析 WHERE 子句 , 根据这个原理 , 表之间的连接必须写在其他 WHERE 条件之前 , 那些可以过滤掉最大数量记录的条件必须写在 WHERE 子句的末尾 .

 

2 SELECT 子句中避免使用‘ *

ORACLE 在解析的过程中 , 会将 '*' 依次转换成所有的列名 , 这个工作是通过查询数据字典完成的 , 这意味着将耗费更多的时间

 

3 )使用 DECODE 函数来减少处理时间:

使用 DECODE 函数可以避免重复扫描相同记录或重复连接相同的表 .

如下例:

  create table t as select username,default_tablespace,lock_date from dba_users;

  select * from t;

select username,decode(lock_date,null,'unlocked','locked') status from t;

select username,decode(lock_date,null,'unlocked') status from t;

decode 函数比较表达式和搜索字,如果匹配,返回结果;如果不匹配,返回 default 值;如果未定义 default 值,则返回空值。

语法如下:

    decode (expression, search_1, result_1)

    decode (expression, search_1, result_1, search_2, result_2)

    decode (expression, search_1, result_1, search_2, result_2, ...., search_n, result_n)

    decode (expression, search_1, result_1, default)

    decode (expression, search_1, result_1, search_2, result_2, default)

    decode (expression, search_1, result_1, search_2, result_2, ...., search_n, result_n, default)

 

 

4 )整合简单 , 无关联的数据库访问:

如果你有几个简单的数据库查询语句 , 你可以把它们整合到一个查询中 ( 即使它们之间没有关系 )

 

5 )删除重复记录:

最高效的删除重复记录方法 ( 因为使用了 ROWID) 例子:

DELETE  FROM  EMP E  WHERE  E.ROWID > (SELECT MIN(X.ROWID)
FROM  EMP X  WHERE  X.EMP_NO = E.EMP_NO);

 

6 )用 TRUNCATE 替代 DELETE

当删除表中的记录时 , 在通常情况下 , 回滚段 (rollback segments ) 用来存放可以被恢复的信息 . 如果你没有 COMMIT 事务 ,ORACLE 会将数据恢复到删除之前的状态 ( 准确地说是恢复到执行删除命令之前的状况 ) 而当运用 TRUNCATE , 回滚段不再存放任何可被恢复的信息 . 当命令运行后 , 数据不能被恢复 . 因此很少的资源被调用 , 执行时间也会很短 . ( 译者按 : TRUNCATE 只在删除全表适用 ,TRUNCATE DDL 不是 DML)

 

7 )尽量多使用 COMMIT

只要有可能 , 在程序中尽量多使用 COMMIT, 这样程序的性能得到提高 , 需求也会因为 COMMIT 所释放的资源而减少 :
COMMIT
所释放的资源
:
a.
回滚段上用于恢复数据的信息
.
b.
被程序语句获得的锁

c. redo log buffer
中的空间

d. ORACLE
为管理上述 3 种资源中的内部花费

 

8 )用 Where 子句替换 HAVING 子句:

  避免使用 HAVING 子句 , HAVING 只会在检索出所有记录之后才对结果集进行过滤 . 这个处理需要排序 , 总计等操作 . 如果能通过 WHERE 子句限制记录的数目 , 那就能减少这方面的开销 . ( oracle )on where having 这三个都可以加条件的子句中, on 是最先执行, where 次之, having 最后,因为 on 是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的, where 也应该比 having 快点的,因为它过滤数据后才进行 sum ,在两个表联接时才用 on 的,所以在一个表的时候,就剩下 where having 比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是 where 可以使用 rushmore 技术,而 having 就不能,在速度上后者要慢如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程, where 的作用时间是在计算之前就完成的,而 having 就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。在多表联接查询时, on where 更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由 where 进行过滤,然后再计算,计算完后再由 having 进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里

 

9 )减少对表的查询:

在含有子查询的 SQL 语句中 , 要特别注意减少对表的查询 . 例子:

     SELECT  TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT

TAB_NAME,DB_VER FROM  TAB_COLUMNS  WHERE  VERSION = 604)

 

10 )用 EXISTS 替代 IN 、用 NOT EXISTS 替代 NOT IN

在许多基于基础表的查询中 , 为了满足一个条件 , 往往需要对另一个表进行联接 . 在这种情况下 , 使用 EXISTS( NOT EXISTS) 通常将提高查询的效率 . 在子查询中 ,NOT IN 子句将执行一个内部的排序和合并 . 无论在哪种情况下 ,NOT IN 都是最低效的 ( 因为它对子查询中的表执行了一个全表遍历 ). 为了避免使用 NOT IN , 我们可以把它改写成外连接 (Outer Joins) NOT EXISTS.

例子:

(高效):

SELECT * FROM  EMP ( 基础表 )  WHERE  EMPNO > 0  AND  EXISTS (SELECT ‘X'  FROM DEPT  WHERE  DEPT.DEPTNO = EMP.DEPTNO  AND  LOC = ‘MELB')

( 低效 )

SELECT  * FROM  EMP ( 基础表 )  WHERE  EMPNO > 0  AND  DEPTNO IN(SELECT DEPTNO  FROM  DEPT  WHERE  LOC = ‘MELB')

 

11 )用 EXISTS 替换 DISTINCT

当提交一个包含一对多表信息 ( 比如部门表和雇员表 ) 的查询时 , 避免在 SELECT 子句中使用 DISTINCT. 一般可以考虑用 EXIST 替换 , EXISTS 使查询更为迅速 , 因为 RDBMS 核心模块将在子查询的条件一旦满足后 , 立刻返回结果 . 例子:

 ( 低效 ):    
SELECT  DISTINCT  DEPT_NO,DEPT_NAME  FROM  DEPT D , EMP E

WHERE  D.DEPT_NO = E.DEPT_NO
(
高效
):
SELECT  DEPT_NO,DEPT_NAME  FROM  DEPT D  WHERE  EXISTS ( SELECT ‘X'
FROM  EMP E  WHERE E.DEPT_NO = D.DEPT_NO);

 

12 sql 语句用大写的;

因为 oracle 总是先解析 sql 语句,把小写的字母转换成大写的再执行

 

13 )在 java 代码中尽量少用连接符“+”连接字符串!

 

14 )避免在索引列上使用 NOT 通常

我们要避免在索引列上使用 NOT, NOT 会产生在和在索引列上使用函数相同的影响 . ORACLE” 遇到” NOT, 他就会停止使用索引转而执行全表扫描 .

 

15 )避免在索引列上使用计算.

WHERE 子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
举例 :

低效: SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;
      
高效 : SELECT … FROM DEPT WHERE SAL > 25000/12;

 

16 )用 >= 替代 >

高效 :
SELECT * FROM  EMP  WHERE  DEPTNO >=4
低效
:
SELECT * FROM EMP WHERE DEPTNO >3
两者的区别在于 , 前者 DBMS 将直接跳到第一个 DEPT 等于 4 的记录而后者将首先定位到 DEPTNO=3 的记录并且向前扫描到第一个 DEPT 大于 3 的记录 .

 

17 )用 UNION 替换 OR ( 适用于索引列 )

通常情况下 , UNION 替换 WHERE 子句中的 OR 将会起到较好的效果 . 对索引列使用 OR 将造成全表扫描 . 注意 , 以上规则只针对多个索引列有效 . 如果有 column 没有被索引 , 查询效率可能会因为你没有选择 OR 而降低 . 在下面的例子中 , LOC_ID REGION 上都建有索引 .
高效
:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10
UNION
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE REGION = “MELBOURNE”
低效
:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10 OR REGION = “MELBOURNE”
如果你坚持要用 OR, 那就需要返回记录最少的索引列写在最前面 .

 

18 )避免在索引列上使用 IS NULL IS NOT NULL

避免在索引中使用任何可以为空的列, ORACLE 将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录 . 对于复合索引,如果每个列都为空,索引中同样不存在此记录 .  如果至少有一个列不为空,则记录存在于索引中.举例 : 如果唯一性索引建立在表的 A 列和 B 列上 , 并且表中存在一条记录的 A,B 值为 (123,null) , ORACLE 将不接受下一条具有相同 A,B 值( 123,null )的记录 ( 插入 ). 然而如果所有的索引列都为空, ORACLE 将认为整个键值为空而空不等于空 . 因此你可以插入 1000 条具有相同键值的记录 , 当然它们都是空 ! 因为空值不存在于索引列中 , 所以 WHERE 子句中对索引列进行空值比较将使 ORACLE 停用该索引 .

低效 : ( 索引失效 )
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE IS NOT NULL;
高效 : ( 索引有效
)
SELECT … FROM  DEPARTMENT  WHERE  DEPT_CODE >=0;

 

19 )总是使用索引的第一个列:

如果索引是建立在多个列上 , 只有在它的第一个列 (leading column) where 子句引用时 , 优化器才会选择使用该索引 . 这也是一条简单而重要的规则,当仅引用索引的第二个列时 , 优化器使用了全表扫描而忽略了索引

 

20 )用 UNION-ALL 替换 UNION

SQL 语句需要 UNION 两个查询结果集合时 , 这两个结果集合会以 UNION-ALL 的方式被合并 , 然后在输出最终结果前进行排序 . 如果用 UNION ALL 替代 UNION, 这样排序就不是必要了 . 效率就会因此得到提高 . 需要注意的是, UNION ALL 将重复输出两个结果集合中相同记录 . 因此各位还是要从业务需求分析使用 UNION ALL 的可行性 . UNION 将对结果集合排序 , 这个操作会使用到 SORT_AREA_SIZE 这块内存 . 对于这块内存的优化也是相当重要的 . 下面的 SQL 可以用来查询排序的消耗量

低效:
SELECT  ACCT_NUM, BALANCE_AMT
FROM  DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
UNION
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
高效
:
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'
UNION ALL
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = '31-DEC-95'

 

21 )需要当心的 WHERE 子句 :

某些 SELECT 语句中的 WHERE 子句不使用索引 . 这里有一些例子 .
在下面的例子里 ,

(1)‘!=' 将不使用索引 . 记住 , 索引只能告诉你什么存在于表中 , 而不能告诉你什么不存在于表中 .

(2) ‘||' 是字符连接函数 . 就象其他函数那样 , 停用了索引 .

(3) ‘+' 是数学函数 . 就象其他数学函数那样 , 停用了索引 .

(4) 相同的索引列不能互相比较 , 这将会启用全表扫描 .

 

22 )避免使用耗费资源的操作 :

带有 DISTINCT,UNION,MINUS,INTERSECT,ORDER BY SQL 语句会启动 SQL 引擎
执行耗费资源的排序 (SORT) 功能 . DISTINCT 需要一次排序操作 , 而其他的至少需要执行两次排序 . 通常 , 带有 UNION, MINUS , INTERSECT SQL 语句都可以用其他方式重写 . 如果你的数据库的 SORT_AREA_SIZE 调配得好 , 使用 UNION , MINUS, INTERSECT 也是可以考虑的 , 毕竟它们的可读性很强

 

23 )优化 GROUP BY:

提高 GROUP BY 语句的效率 , 可以通过将不需要的记录在 GROUP BY 之前过滤掉 . 下面两个查询返回相同结果但第二个明显就快了许多 .

低效 :
SELECT JOB , AVG(SAL)
FROM EMP
GROUP JOB
HAVING JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
高效
:
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
GROUP JOB

 24 )将索引数据和表数据分开在不同的表空间上(降低IO冲突) 

  25 ) 建立表分区,将数据分别存储在不同的分区上(以空间换取时间,减少IO)    26 ) 逻辑上优化: 
  1)可以对表进行逻辑分割,如中国移动用户表,可以根据手机尾数分成10个表,这样对性能会有一定的作用 
  2)Sql语句使用占位符语句,并且开发时候必须按照规定编写sql语句(如全部大写,全部小写等)oracle解析语句后会放置到共享池中, 如: 
  select * from Emp where name=?这个语句只会在共享池中有一条,而如果是字符串的话,那就根据不同名字存在不同的语句,所以占位符效率较好     

优化-- 使用Exists

declare 
  v_cnt number; 
begin 
  select count(*) 
    into v_cnt 
    from dual 
   where exists (select * from t_vip where col=1); 
  if v_cnt = 0 then 
    dbms_output.put_line('无记录'); 
  end if; 
end;

可以把判断封装成一个函数以方便使用,代码如下 
CREATE OR REPLACE FUNCTION EXISTS2 (IN_SQL IN VARCHAR2) 
  RETURN NUMBER 
IS 
  V_SQL VARCHAR2(4000); 
  V_CNT NUMBER(1); 
BEGIN 
  V_SQL := 'SELECT COUNT(*) FROM DUAL WHERE EXISTS (' || IN_SQL || ')'; 
  EXECUTE IMMEDIATE V_SQL INTO V_CNT; 
  RETURN(V_CNT); 
END; 
  /********************************************************** 
  * 使用示例 
  * begin 
  *   if EXISTS2('select * from dual where 1=1')=1 then 
  *     dbms_output.put_line('有记录'); 
  *   else 
  *     dbms_output.put_line('无记录'); 
  *   end if; 
  * end; 
  *****************************************************************/ 

 

优化-- insert判断优化

比如以下代码 
if not exists(select * from table1 where id=1) 
   insert into table1 values(1,'a'); 
可以改写成 
insert 
  when (not exists(select * from table1 where id=1)) then 
into table1 
select 1 as id, 'a' as data from dual; 


优化-- if、else判断优化

比如以下的代码 
if not exists(select * from table1 where id=2) 
   insert into table1 values(2,'b') 
else 
   update table1 set data='b' where id=2; 
可以改写成 
merge into table1 his 
using 

  select 2 as id, 'b' as data from dual 
) src 
on (his.id=src.id) 
when matched then 
  update set his.data=src.data where id=src.id

 

开发者博客www.developsearch.com

猜你喜欢

转载自keepwork.iteye.com/blog/1994107